Skip to content
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: 4 additions & 7 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,11 @@ jobs:
python -m pip install --upgrade pip
pip install .[lint]

- name: Run Black
run: black --check .
- name: Run ruff format
run: ruff format --check .

- name: Run isort
run: isort --check-only .

- name: Run flake8
run: flake8 .
- name: Run ruff check
run: ruff check .

- name: Run mdformat
run: mdformat . --check
Expand Down
20 changes: 5 additions & 15 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,12 @@ repos:
hooks:
- id: check-yaml

- repo: https://github.com/PyCQA/isort
rev: 6.0.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.10
hooks:
- id: isort

- repo: https://github.com/psf/black
rev: 25.1.0
hooks:
- id: black
name: black

- repo: https://github.com/pycqa/flake8
rev: 7.1.2
hooks:
- id: flake8
additional_dependencies: [flake8-breakpoint, flake8-print, flake8-pydantic, flake8-type-checking]
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.15.0
Expand Down
40 changes: 22 additions & 18 deletions ape_solidity/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,10 @@ def get_import_remapping(self, project: Optional[ProjectManager] = None) -> dict
return remapping

def get_compiler_settings(
self, contract_filepaths: Iterable[Path], project: Optional[ProjectManager] = None, **kwargs
self,
contract_filepaths: Iterable[Path],
project: Optional[ProjectManager] = None,
**kwargs,
) -> dict[Version, dict]:
pm = project or self.local_project
paths = _validate_can_compile(contract_filepaths)
Expand Down Expand Up @@ -341,9 +344,15 @@ def _get_settings_from_version_map(
settings: dict = {}
for solc_version, sources in version_map.items():
version_settings: dict[str, Union[Any, list[Any]]] = {
"optimizer": {"enabled": config.optimize, "runs": config.optimization_runs},
"optimizer": {
"enabled": config.optimize,
"runs": config.optimization_runs,
},
"outputSelection": {
str(get_relative_path(p, pm.path)): {"*": OUTPUT_SELECTION, "": ["ast"]}
str(get_relative_path(p, pm.path)): {
"*": OUTPUT_SELECTION,
"": ["ast"],
}
for p in sorted(sources)
},
**kwargs,
Expand Down Expand Up @@ -415,30 +424,22 @@ def get_standard_input_json_from_settings(
if solc_version >= Version("0.6.9"):
arguments["base_path"] = pm.path

vers_settings["outputSelection"] = {
k: v for k, v in vers_settings["outputSelection"].items() if (pm.path / k).is_file()
}

if missing_sources := [
x for x in vers_settings["outputSelection"] if not (pm.path / x).is_file()
]:
# See if the missing sources are from dependencies (they likely are)
# and cater the error message accordingly.
if dependencies_needed := [x for x in missing_sources if str(x).startswith("@")]:
# Missing dependencies. Should only get here if dependencies are found
# in import-strs but are not installed (not in project or globally).
missing_str = ", ".join(dependencies_needed)
raise CompilerError(
f"Missing required dependencies '{missing_str}'. "
"Install them using `dependencies:` "
"in an ape-config.yaml or using the `ape pm install` command."
)

# Otherwise, we are missing project-level source files for some reason.
# This would only happen if the user passes in unexpected files outside
# of core.
missing_src_str = ", ".join(missing_sources)
raise CompilerError(f"Sources '{missing_src_str}' not found in '{pm.name}'.")
raise CompilerError(
f"Sources '{missing_src_str}' not found in '{pm.name}' "
f"(project path: {pm.path})."
)

sources = {
x: {"content": (pm.path / x).read_text(encoding="utf8")}
Expand All @@ -461,8 +462,8 @@ def compile(
) -> Iterator[ContractType]:
pm = project or self.local_project
settings = settings or {}
paths = [p for p in contract_filepaths] # Handles generator.
source_ids = [f"{get_relative_path(p.absolute(), pm.path)}" for p in paths]
paths = [(p if Path(p).is_absolute() else pm.path / p) for p in contract_filepaths]
source_ids = [f"{get_relative_path(Path(p).absolute(), pm.path)}" for p in paths]
_validate_can_compile(paths)

# Compile in an isolated env so the .cache folder does not interfere with anything.
Expand Down Expand Up @@ -500,7 +501,10 @@ def _compile(
logger.info(log_str)
cleaned_version = Version(solc_version.base_version)
solc_binary = get_executable(version=cleaned_version)
arguments: dict = {"solc_binary": solc_binary, "solc_version": cleaned_version}
arguments: dict = {
"solc_binary": solc_binary,
"solc_version": cleaned_version,
}

if solc_version >= Version("0.6.9"):
arguments["base_path"] = pm.path
Expand Down
31 changes: 15 additions & 16 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,6 @@ plugins = ["pydantic.mypy"]
[tool.setuptools_scm]
write_to = "ape_solidity/version.py"

# NOTE: you have to use single-quoted strings in TOML for regular expressions.
# It's the equivalent of r-strings in Python. Multiline strings are treated as
# verbose regular expressions by Black. Use [ ] to denote a significant space
# character.

[tool.black]
line-length = 100
target-version = ['py39', 'py310', 'py311', 'py312', 'py313']
include = '\.pyi?$'

[tool.pytest.ini_options]
addopts = """
-p no:ape_test
Expand All @@ -35,12 +25,21 @@ fuzzing: Run Hypothesis fuzz test suite
install: Tests that will install a solc version (slow)
"""

[tool.isort]
line_length = 100
force_grid_wrap = 0
include_trailing_comma = true
multi_line_output = 3
use_parentheses = true
[tool.ruff]
target-version = "py310"
line-length = 100

[tool.ruff.lint.pydocstyle]
convention = "google"

[tool.ruff.lint.isort]
known-first-party = ["ape_solidity"]

[tool.ruff.format]
quote-style = "double"
line-ending = "auto"
indent-style = "space"
docstring-code-format = true

[tool.mdformat]
number = true
10 changes: 0 additions & 10 deletions setup.cfg

This file was deleted.

6 changes: 1 addition & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,10 @@
"pytest-benchmark", # For performance tests
],
"lint": [
"black>=25.1.0,<26", # Auto-formatter and linter
"ruff>=0.14.10", # Linter and auto-formatter
"mypy>=1.15.0,<2", # Static type analyzer
"types-requests", # Needed for mypy type shed
"types-setuptools", # Needed for mypy type shed
"flake8>=7.1.2,<8", # Style linter
"flake8-pydantic", # For detecting issues with Pydantic models
"flake8-type-checking", # Detect imports to move in/out of type-checking blocks
"isort>=5.13.2,<6", # Import sorting linter
"mdformat>=0.7.22", # Auto-formatter for markdown
"mdformat-gfm>=0.3.5", # Needed for formatting GitHub-flavored markdown
"mdformat-frontmatter>=0.4.1", # Needed for frontmatters-style headers in issue templates
Expand Down
21 changes: 20 additions & 1 deletion tests/test_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,10 @@ def test_get_version_map_importing_more_constrained_version(project, compiler):

actual = compiler.get_version_map((path,), project=project)
expected_version = Version("0.8.12+commit.f00d7308")
expected_sources = ("ImportSourceWithEqualSignVersion", "SpecificVersionWithEqualSign")
expected_sources = (
"ImportSourceWithEqualSignVersion",
"SpecificVersionWithEqualSign",
)
assert expected_version in actual

actual_ids = [x.stem for x in actual[expected_version]]
Expand Down Expand Up @@ -1070,3 +1073,19 @@ def test_compile_code(project, compiler):
assert actual.ast is not None
assert len(actual.runtime_bytecode.bytecode) > 0
assert len(actual.deployment_bytecode.bytecode) > 0


def test_compile_relative_path_from_other_cwd(project, compiler, tmp_path, monkeypatch):
# Regression: a relative source path must be resolved against the
# project root, not the current working directory.
monkeypatch.chdir(tmp_path)
actual = [c for c in compiler.compile(("contracts/CompilesOnce.sol",), project=project)]
assert len(actual) == 1
assert actual[0].name == "CompilesOnce"
assert actual[0].source_id == "contracts/CompilesOnce.sol"


def test_compile_missing_source_raises_clear_error(project, compiler, monkeypatch):
monkeypatch.chdir(project.path)
with pytest.raises(CompilerError, match=r"not found in .*project path:"):
list(compiler.compile(("contracts/DoesNotExist.sol",), project=project))
Loading