Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: lazify CLI loading #135

Merged
merged 5 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
rev: v5.0.0
hooks:
- id: check-yaml

Expand All @@ -10,7 +10,7 @@ repos:
- id: isort

- repo: https://github.com/psf/black
rev: 24.8.0
rev: 24.10.0
hooks:
- id: black
name: black
Expand All @@ -19,15 +19,16 @@ repos:
rev: 7.1.1
hooks:
- id: flake8
additional_dependencies: [flake8-breakpoint, flake8-print, flake8-pydantic, flake8-type-checking]

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
rev: v1.13.0
hooks:
- id: mypy
additional_dependencies: [types-setuptools, pydantic==1.10.4]
additional_dependencies: [types-setuptools, pydantic]

- repo: https://github.com/executablebooks/mdformat
rev: 0.7.17
rev: 0.7.18
hooks:
- id: mdformat
additional_dependencies: [mdformat-gfm, mdformat-frontmatter, mdformat-pyproject]
Expand Down
38 changes: 34 additions & 4 deletions ape_vyper/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,45 @@
from ape import plugins
from typing import Any

from ._utils import FileType
from .compiler import VyperCompiler
from .config import VyperConfig
from ape import plugins


@plugins.register(plugins.Config)
def config_class():
from .config import VyperConfig

return VyperConfig


@plugins.register(plugins.CompilerPlugin)
def register_compiler():
from ._utils import FileType
from .compiler import VyperCompiler

return tuple(e.value for e in FileType), VyperCompiler


def __getattr__(name: str) -> Any:
if name == "FileType":
from ._utils import FileType

return FileType

elif name == "VyperCompiler":
from .compiler import VyperCompiler

return VyperCompiler

elif name == "VyperConfig":
from .config import VyperConfig

return VyperConfig

else:
raise AttributeError(name)


__all__ = [
"FileType",
"VyperCompiler",
"VyperConfig",
]
2 changes: 1 addition & 1 deletion ape_vyper/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import ape
import click
from ape.cli import ape_cli_context, project_option
from ape.cli.options import ape_cli_context, project_option


@click.group
Expand Down
18 changes: 11 additions & 7 deletions ape_vyper/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,24 @@
from collections.abc import Iterable
from enum import Enum
from pathlib import Path
from typing import Any, Optional, Union
from typing import TYPE_CHECKING, Any, Optional, Union

import vvm # type: ignore
from ape.exceptions import ProjectError
from ape.logging import logger
from ape.managers import ProjectManager
from ape.types import SourceTraceback
from ape.utils import get_relative_path
from eth_utils import is_0x_prefixed
from ethpm_types import ASTNode, PCMap, SourceMapItem
from ethpm_types.source import Function
from packaging.specifiers import InvalidSpecifier, SpecifierSet
from packaging.version import Version

from ape_vyper.exceptions import RuntimeErrorType, VyperInstallError

if TYPE_CHECKING:
from ape.types.trace import SourceTraceback
from ethpm_types.source import Function
from packaging.version import Version

Optimization = Union[str, bool]
EVM_VERSION_DEFAULT = {
"0.2.15": "berlin",
Expand Down Expand Up @@ -54,7 +56,7 @@ def __str__(self) -> str:
return self.value


def install_vyper(version: Version):
def install_vyper(version: "Version"):
for attempt in range(MAX_INSTALL_RETRIES):
try:
vvm.install_vyper(version, show_progress=True)
Expand Down Expand Up @@ -255,7 +257,7 @@ def seek() -> Optional[Path]:
return None


def safe_append(data: dict, version: Union[Version, SpecifierSet], paths: Union[Path, set]):
def safe_append(data: dict, version: Union["Version", SpecifierSet], paths: Union[Path, set]):
if isinstance(paths, Path):
paths = {paths}
if version in data:
Expand Down Expand Up @@ -478,7 +480,9 @@ def is_immutable_member_load(opcodes: list[str]):
return not is_code_copy and opcodes and is_0x_prefixed(opcodes[0])


def extend_return(function: Function, traceback: SourceTraceback, last_pc: int, source_path: Path):
def extend_return(
function: "Function", traceback: "SourceTraceback", last_pc: int, source_path: Path
):
return_ast_result = [x for x in function.ast.children if x.ast_type == "Return"]
if not return_ast_result:
return
Expand Down
34 changes: 19 additions & 15 deletions ape_vyper/compiler/_versions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@
from typing import TYPE_CHECKING, Any, Optional

from ape.logging import logger
from ape.managers.project import ProjectManager
from ape.utils import ManagerAccessMixin, clean_path, get_relative_path
from ethpm_types import ASTNode, ContractType, SourceMap
from ethpm_types.ast import ASTClassification
from ethpm_types.source import Content
from packaging.version import Version
from vvm import compile_standard as vvm_compile_standard # type: ignore
from vvm.exceptions import VyperError # type: ignore

Expand All @@ -25,10 +23,13 @@
get_pcmap,
)
from ape_vyper.exceptions import VyperCompileError
from ape_vyper.imports import ImportMap

if TYPE_CHECKING:
from ape.managers.project import ProjectManager
from packaging.version import Version

from ape_vyper.compiler.api import VyperCompiler
from ape_vyper.imports import ImportMap


class BaseVyperCompiler(ManagerAccessMixin):
Expand All @@ -39,7 +40,7 @@ class BaseVyperCompiler(ManagerAccessMixin):
def __init__(self, api: "VyperCompiler"):
self.api = api

def get_import_remapping(self, project: Optional[ProjectManager] = None) -> dict[str, dict]:
def get_import_remapping(self, project: Optional["ProjectManager"] = None) -> dict[str, dict]:
# Overridden on 0.4 to not use.
# Import remappings are for Vyper versions 0.2 - 0.3 to
# create the interfaces dict.
Expand All @@ -48,11 +49,11 @@ def get_import_remapping(self, project: Optional[ProjectManager] = None) -> dict

def compile(
self,
vyper_version: Version,
vyper_version: "Version",
settings: dict,
import_map: ImportMap,
import_map: "ImportMap",
compiler_data: dict,
project: Optional[ProjectManager] = None,
project: Optional["ProjectManager"] = None,
):
pm = project or self.local_project
for settings_key, settings_set in settings.items():
Expand Down Expand Up @@ -155,10 +156,10 @@ def compile(

def get_settings(
self,
version: Version,
version: "Version",
source_paths: Iterable[Path],
compiler_data: dict,
project: Optional[ProjectManager] = None,
project: Optional["ProjectManager"] = None,
) -> dict:
pm = project or self.local_project
default_optimization = self._get_default_optimization(version)
Expand Down Expand Up @@ -210,7 +211,7 @@ def _classify_ast(self, _node: ASTNode):
self._classify_ast(child)

def _get_sources_dictionary(
self, source_ids: Iterable[str], project: Optional[ProjectManager] = None, **kwargs
self, source_ids: Iterable[str], project: Optional["ProjectManager"] = None, **kwargs
) -> dict[str, dict]:
"""
Generate input for the "sources" key in the input JSON.
Expand All @@ -225,7 +226,7 @@ def _get_sources_dictionary(
def _get_selection_dictionary(
self,
selection: Iterable[str],
project: Optional[ProjectManager] = None,
project: Optional["ProjectManager"] = None,
**kwargs,
) -> dict:
"""
Expand All @@ -238,7 +239,10 @@ def _get_selection_dictionary(
return {s: ["*"] for s in selection if (pm.path / s).is_file() if "interfaces" not in s}

def _get_compile_kwargs(
self, vyper_version: Version, compiler_data: dict, project: Optional[ProjectManager] = None
self,
vyper_version: "Version",
compiler_data: dict,
project: Optional["ProjectManager"] = None,
) -> dict:
"""
Generate extra kwargs to pass to Vyper.
Expand All @@ -249,14 +253,14 @@ def _get_compile_kwargs(
comp_kwargs["base_path"] = pm.path
return comp_kwargs

def _get_base_compile_kwargs(self, vyper_version: Version, compiler_data: dict):
def _get_base_compile_kwargs(self, vyper_version: "Version", compiler_data: dict):
vyper_binary = compiler_data[vyper_version]["vyper_binary"]
comp_kwargs = {"vyper_version": vyper_version, "vyper_binary": vyper_binary}
return comp_kwargs

def _get_pcmap(
self,
vyper_version: Version,
vyper_version: "Version",
ast: Any,
src_map: list,
opcodes: list[str],
Expand All @@ -274,7 +278,7 @@ def _parse_source_map(self, raw_source_map: Any) -> SourceMap:
# All versions < 0.4 use this one
return SourceMap(root=raw_source_map)

def _get_default_optimization(self, vyper_version: Version) -> Optimization:
def _get_default_optimization(self, vyper_version: "Version") -> Optimization:
"""
The default value for "optimize" in the settings for input JSON.
"""
Expand Down
9 changes: 5 additions & 4 deletions ape_vyper/compiler/_versions/vyper_02.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from typing import Any

from packaging.version import Version
from typing import TYPE_CHECKING, Any

from ape_vyper._utils import get_legacy_pcmap
from ape_vyper.compiler._versions.base import BaseVyperCompiler

if TYPE_CHECKING:
from packaging.version import Version


class Vyper02Compiler(BaseVyperCompiler):
"""
Expand All @@ -15,7 +16,7 @@ class Vyper02Compiler(BaseVyperCompiler):

def _get_pcmap(
self,
vyper_version: Version,
vyper_version: "Version",
ast: Any,
src_map: list,
opcodes: list[str],
Expand Down
25 changes: 15 additions & 10 deletions ape_vyper/compiler/_versions/vyper_04.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
import os
from collections.abc import Iterable
from pathlib import Path
from typing import Optional
from typing import TYPE_CHECKING, Optional

from ape.managers import ProjectManager
from ape.utils import get_full_extension, get_relative_path
from ethpm_types import SourceMap
from packaging.version import Version

from ape_vyper._utils import FileType, Optimization
from ape_vyper.compiler._versions.base import BaseVyperCompiler
from ape_vyper.imports import ImportMap

if TYPE_CHECKING:
from ape.managers.project import ProjectManager
from packaging.version import Version


class Vyper04Compiler(BaseVyperCompiler):
"""
Compiler for Vyper>=0.4.0.
"""

def get_import_remapping(self, project: Optional[ProjectManager] = None) -> dict[str, dict]:
def get_import_remapping(self, project: Optional["ProjectManager"] = None) -> dict[str, dict]:
# Import remappings are not used in 0.4.
# You always import via module or package name.
return {}

def get_settings(
self,
version: Version,
version: "Version",
source_paths: Iterable[Path],
compiler_data: dict,
project: Optional[ProjectManager] = None,
project: Optional["ProjectManager"] = None,
) -> dict:
pm = project or self.local_project

Expand All @@ -43,7 +45,7 @@ def get_settings(
return settings

def _get_sources_dictionary(
self, source_ids: Iterable[str], project: Optional[ProjectManager] = None, **kwargs
self, source_ids: Iterable[str], project: Optional["ProjectManager"] = None, **kwargs
) -> dict[str, dict]:
pm = project or self.local_project
if not source_ids:
Expand Down Expand Up @@ -83,18 +85,21 @@ def _get_sources_dictionary(
return src_dict

def _get_compile_kwargs(
self, vyper_version: Version, compiler_data: dict, project: Optional[ProjectManager] = None
self,
vyper_version: "Version",
compiler_data: dict,
project: Optional["ProjectManager"] = None,
) -> dict:
return self._get_base_compile_kwargs(vyper_version, compiler_data)

def _get_default_optimization(self, vyper_version: Version) -> Optimization:
def _get_default_optimization(self, vyper_version: "Version") -> Optimization:
return "gas"

def _parse_source_map(self, raw_source_map: dict) -> SourceMap:
return SourceMap(root=raw_source_map["pc_pos_map_compressed"])

def _get_selection_dictionary(
self, selection: Iterable[str], project: Optional[ProjectManager] = None, **kwargs
self, selection: Iterable[str], project: Optional["ProjectManager"] = None, **kwargs
) -> dict:
pm = project or self.local_project
return {
Expand Down
Loading
Loading