Skip to content

Commit

Permalink
✨ Make all paths in Wake config pure paths
Browse files Browse the repository at this point in the history
  • Loading branch information
michprev committed Sep 25, 2024
1 parent bf00281 commit f85751f
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 31 deletions.
7 changes: 4 additions & 3 deletions wake/cli/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,15 +652,16 @@ def process_detection(detection: Detection) -> Dict[str, Any]:

if loaded["system"] == "Windows":
wake_contracts_path = PureWindowsPath(loaded["wake_contracts_path"])
original_project_root = PureWindowsPath(loaded["project_root"])
else:
wake_contracts_path = PurePosixPath(loaded["wake_contracts_path"])
original_project_root = PurePosixPath(loaded["project_root"])

config = WakeConfig.fromdict(loaded["config"], wake_contracts_path=wake_contracts_path)
original_project_root = Path(loaded["project_root"])
config = WakeConfig.fromdict(loaded["config"], wake_contracts_path=wake_contracts_path, paths_mode=loaded["system"])

# add project root as an include path (for solc to resolve imports correctly)
if config.project_root_path != original_project_root:
config.update({"compiler": {"solc": {"include_paths": set(config.compiler.solc.include_paths) | {original_project_root}, }}}, [])
config.update({"compiler": {"solc": {"include_paths": set(config.compiler.solc.include_paths) | {original_project_root}, }}}, [], paths_mode=loaded["system"])

modified_files = {
Path(path): source['content'].encode("utf-8") for path, source in loaded["sources"].items()
Expand Down
8 changes: 4 additions & 4 deletions wake/compiler/build_data_model.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import weakref
from pathlib import Path
from pathlib import Path, PurePath
from types import MappingProxyType
from typing import Any, Dict, FrozenSet, List, Optional

Expand Down Expand Up @@ -104,9 +104,9 @@ class ProjectBuildInfo(BuildInfoModel):

compilation_units: Dict[str, CompilationUnitBuildInfo]
source_units_info: Dict[str, SourceUnitInfo]
allow_paths: FrozenSet[Path]
exclude_paths: FrozenSet[Path]
include_paths: FrozenSet[Path]
allow_paths: FrozenSet[PurePath]
exclude_paths: FrozenSet[PurePath]
include_paths: FrozenSet[PurePath]
settings: SolcInputSettings
target_solidity_version: Optional[SolidityVersion]
wake_version: str
Expand Down
40 changes: 22 additions & 18 deletions wake/config/data_model.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import re
from dataclasses import astuple
from pathlib import Path
from typing import Dict, FrozenSet, List, Optional
from pathlib import Path, PurePath, PurePosixPath, PureWindowsPath
from typing import Dict, FrozenSet, List, Optional, Iterable

from pydantic import BaseModel, ConfigDict, Field, PlainSerializer, field_serializer
from pydantic import BaseModel, ConfigDict, Field, PlainSerializer, field_serializer, field_validator, ValidationInfo
from pydantic.dataclasses import dataclass
from pydantic.functional_validators import BeforeValidator
from typing_extensions import Annotated
Expand All @@ -20,6 +20,16 @@ class WakeConfigModel(BaseModel):
)


def normalize_paths(paths: Iterable[str], info: ValidationInfo) -> FrozenSet[PurePath]:
if info.context and info.context.get("paths_mode") is not None:
if info.context["paths_mode"] == "Windows":
return frozenset(PureWindowsPath(path) for path in paths)
else:
return frozenset(PurePosixPath(path) for path in paths)
else:
return frozenset(Path(path).resolve() for path in paths)


@dataclass
class SolcRemapping:
context: Optional[str]
Expand Down Expand Up @@ -96,15 +106,11 @@ def convert_remapping(v):


class SolcConfig(WakeConfigModel):
allow_paths: FrozenSet[
Annotated[Path, BeforeValidator(lambda p: Path(p).resolve())]
] = frozenset()
allow_paths: FrozenSet[PurePath] = frozenset()
"""Wake should set solc `--allow-paths` automatically. This option allows to specify additional allowed paths."""
evm_version: Optional[EvmVersionEnum] = None
"""Version of the EVM to compile for. Leave unset to let the solc decide."""
exclude_paths: FrozenSet[
Annotated[Path, BeforeValidator(lambda p: Path(p).resolve())]
] = Field(
exclude_paths: FrozenSet[PurePath] = Field(
default_factory=lambda: frozenset(
[
Path.cwd() / "node_modules",
Expand All @@ -119,9 +125,7 @@ class SolcConfig(WakeConfigModel):
"""
Solidity files in these paths are excluded from compilation unless imported from a non-excluded file.
"""
include_paths: FrozenSet[
Annotated[Path, BeforeValidator(lambda p: Path(p).resolve())]
] = Field(default_factory=lambda: frozenset([Path.cwd() / "node_modules"]))
include_paths: FrozenSet[PurePath] = Field(default_factory=lambda: frozenset([Path.cwd() / "node_modules"]))
"""
Paths where to search for Solidity files imported using direct (non-relative) import paths.
"""
Expand All @@ -148,6 +152,8 @@ class SolcConfig(WakeConfigModel):
Use new IR-based compiler pipeline.
"""

_normalize_paths = field_validator("allow_paths", "include_paths", "exclude_paths", mode="before")(normalize_paths)

@field_serializer("target_version", when_used="json")
def serialize_target_version(self, version: Optional[SolidityVersion], info):
return str(version) if version is not None else None
Expand Down Expand Up @@ -207,9 +213,7 @@ class DetectorsConfig(WakeConfigModel):
"""
Names of detectors that should only be loaded.
"""
ignore_paths: FrozenSet[
Annotated[Path, BeforeValidator(lambda p: Path(p).resolve())]
] = Field(
ignore_paths: FrozenSet[PurePath] = Field(
default_factory=lambda: frozenset(
[
Path.cwd() / "venv",
Expand All @@ -222,9 +226,7 @@ class DetectorsConfig(WakeConfigModel):
Detections in these paths must be ignored under all circumstances.
Useful for ignoring detections in Solidity test files.
"""
exclude_paths: FrozenSet[
Annotated[Path, BeforeValidator(lambda p: Path(p).resolve())]
] = Field(
exclude_paths: FrozenSet[PurePath] = Field(
default_factory=lambda: frozenset(
[
Path.cwd() / "node_modules",
Expand All @@ -238,6 +240,8 @@ class DetectorsConfig(WakeConfigModel):
Useful for ignoring detections in dependencies.
"""

_normalize_paths = field_validator("ignore_paths", "exclude_paths", mode="before")(normalize_paths)


# namespace for detector configs
class DetectorConfig(WakeConfigModel, extra="allow"):
Expand Down
8 changes: 5 additions & 3 deletions wake/config/wake_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ def fromdict(
*,
project_root_path: Optional[Union[str, Path]] = None,
wake_contracts_path: Optional[PurePath] = None,
paths_mode: Optional[str] = None,
) -> "WakeConfig":
"""
Args:
Expand All @@ -239,7 +240,7 @@ def fromdict(
"""
instance = cls(project_root_path=project_root_path, wake_contracts_path=wake_contracts_path)
with change_cwd(instance.project_root_path):
parsed_config = TopLevelConfig.model_validate(config_dict)
parsed_config = TopLevelConfig.model_validate(config_dict, context={"paths_mode": paths_mode})
instance.__config_raw = parsed_config.model_dump(
by_alias=True, exclude_unset=True
)
Expand Down Expand Up @@ -308,6 +309,7 @@ def update(
self,
config_dict: Dict[str, Any],
deleted_options: Iterable[Tuple[Union[int, str], ...]],
paths_mode: Optional[str] = None,
) -> Dict:
"""
Update the config with a new dictionary.
Expand All @@ -320,7 +322,7 @@ def update(
Dictionary containing the modified config options.
"""
with change_cwd(self.project_root_path):
parsed_config = TopLevelConfig.model_validate(config_dict)
parsed_config = TopLevelConfig.model_validate(config_dict, context={"paths_mode": paths_mode})
parsed_config_raw = parsed_config.model_dump(by_alias=True, exclude_unset=True)

original_config = deepcopy(self.__config_raw)
Expand All @@ -346,7 +348,7 @@ def update(
except ValueError:
pass

self.__config = TopLevelConfig.model_validate(self.__config_raw)
self.__config = TopLevelConfig.model_validate(self.__config_raw, context={"paths_mode": paths_mode})
modified_keys = {}
self.__modified_keys(
original_config,
Expand Down
6 changes: 4 additions & 2 deletions wake/lsp/features/completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,8 +574,9 @@ async def completion(

for include_path in chain(
context.config.compiler.solc.include_paths,
[context.config.project_root_path, Path(context.config.wake_contracts_path)],
[context.config.project_root_path, context.config.wake_contracts_path],
):
include_path = Path(include_path)
if include_path.is_dir():
for p in include_path.iterdir():
if p.is_dir():
Expand All @@ -600,8 +601,9 @@ async def completion(
else:
for include_path in chain(
context.config.compiler.solc.include_paths,
[context.config.project_root_path, Path(context.config.wake_contracts_path)],
[context.config.project_root_path, context.config.wake_contracts_path],
):
include_path = Path(include_path)
if include_path.is_dir():
dir = include_path / parent
if dir.is_dir():
Expand Down
3 changes: 2 additions & 1 deletion wake/utils/openzeppelin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import platform
import subprocess
from pathlib import Path
from typing import Optional

from wake.config import WakeConfig
Expand All @@ -11,7 +12,7 @@ def get_contracts_package_version(config: WakeConfig) -> Optional[SemanticVersio
try:
node_modules_path = next(
path
for path in config.compiler.solc.include_paths
for path in (Path(p) for p in config.compiler.solc.include_paths)
if "node_modules" in path.stem and path.is_dir()
)
except StopIteration:
Expand Down

0 comments on commit f85751f

Please sign in to comment.