Skip to content

Commit

Permalink
fix: allow regex globs for compile exclusions (#2108)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Jun 3, 2024
1 parent f6ea787 commit 38e3ae8
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 18 deletions.
1 change: 1 addition & 0 deletions docs/userguides/compile.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ compile:
exclude:
- "examples" # Exclude all files in the examples/ directory
- "*Mock.sol" # Exclude all files ending in Mock.sol
- r"(?!.*_mock\.vy$)" # You can also use regex instead of globs (prefix with `r`).
```
You can also exclude files using the `--config-override` CLI option:
Expand Down
32 changes: 20 additions & 12 deletions src/ape/managers/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from contextlib import contextmanager
from functools import cached_property, singledispatchmethod
from pathlib import Path
from re import Pattern
from typing import Any, Optional, Union, cast

from eth_typing import HexStr
Expand Down Expand Up @@ -59,7 +60,7 @@ def __init__(
self,
root_path: Path,
get_contracts_path: Callable,
exclude_globs: Optional[set[str]] = None,
exclude_globs: Optional[set[Union[str, Pattern]]] = None,
):
self.root_path = root_path
self.get_contracts_path = get_contracts_path
Expand Down Expand Up @@ -213,20 +214,27 @@ def is_excluded(self, path: Path) -> bool:
parent_dir_name = path.parent.name

for excl in self.exclude_globs:
# perf: Check parent directory first to exclude faster by marking them all.
if path_match(parent_dir_name, excl):
self._exclude_cache[source_id] = True
for sub in get_all_files_in_directory(path.parent):
sub_source_id = self._get_source_id(sub)
self._exclude_cache[sub_source_id] = True
if isinstance(excl, Pattern):
for opt in options:
if excl.match(opt):
self._exclude_cache[source_id] = True
return True

return True

for opt in options:
if path_match(opt, excl):
else:
# perf: Check parent directory first to exclude faster by marking them all.
if path_match(parent_dir_name, excl):
self._exclude_cache[source_id] = True
for sub in get_all_files_in_directory(path.parent):
sub_source_id = self._get_source_id(sub)
self._exclude_cache[sub_source_id] = True

return True

for opt in options:
if path_match(opt, excl):
self._exclude_cache[source_id] = True
return True

self._exclude_cache[source_id] = False
return False

Expand Down Expand Up @@ -2042,7 +2050,7 @@ def deployments(self) -> DeploymentManager:
return DeploymentManager(self)

@property
def exclusions(self) -> set[str]:
def exclusions(self) -> set[Union[str, Pattern]]:
"""
Source-file exclusion glob patterns.
"""
Expand Down
27 changes: 24 additions & 3 deletions src/ape_compile/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import re
from re import Pattern
from typing import Union

from pydantic import field_validator

from ape import plugins
Expand All @@ -24,9 +28,11 @@ class Config(PluginConfig):
Configure general compiler settings.
"""

exclude: set[str] = set()
exclude: set[Union[str, Pattern]] = set()
"""
Source exclusion globs across all file types.
Source exclusion globs or regex patterns across all file types.
To use regex, start your values with ``r"`` and they'll be turned
into regex pattern objects.
**NOTE**: ``ape.utils.misc.SOURCE_EXCLUDE_PATTERNS`` are automatically
included in this set.
Expand All @@ -51,7 +57,22 @@ class Config(PluginConfig):
@field_validator("exclude", mode="before")
@classmethod
def validate_exclude(cls, value):
return {*(value or []), *SOURCE_EXCLUDE_PATTERNS}
given_values = []

# Convert regex to Patterns.
for given in value or []:
if (given.startswith('r"') and given.endswith('"')) or (
given.startswith("r'") and given.endswith("'")
):
value_clean = given[2:-1]
pattern = re.compile(value_clean)
given_values.append(pattern)

else:
given_values.append(given)

# Include defaults.
return {*given_values, *SOURCE_EXCLUDE_PATTERNS}


@plugins.register(plugins.Config)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ compile:
exclude:
- exclude_dir
- Excl*.json
- r"Ignore\w*\.json"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"foo": "bar"}
8 changes: 5 additions & 3 deletions tests/integration/cli/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def test_compile(ape_cli, runner, project, clean_cache):
# in the test project!
excluded = (
"Exclude.json",
"IgnoreUsingRegex.json",
"UnwantedContract.json",
"tsconfig.json",
"package.json",
Expand Down Expand Up @@ -323,8 +324,9 @@ def test_raw_compiler_output_bytecode(project):
@skip_projects_except("with-contracts")
def test_compile_exclude(ape_cli, runner):
result = runner.invoke(ape_cli, ("compile", "--force"), catch_exceptions=False)
assert "Compiling 'Exclude.json'" not in result.output
assert "Compiling 'exclude_dir/UnwantedContract.json'" not in result.output
assert "Compiling 'contracts/Exclude.json'" not in result.output
assert "Compiling 'contracts/IgnoreUsingRegex.json'" not in result.output
assert "Compiling 'contracts/exclude_dir/UnwantedContract.json'" not in result.output


@skip_projects_except("with-contracts")
Expand All @@ -336,4 +338,4 @@ def test_compile_config_override(ape_cli, runner):
'{"compile": {"exclude": ["*ContractA*"]}}',
)
result = runner.invoke(ape_cli, arguments, catch_exceptions=False)
assert "Compiling 'ContractA.json'" not in result.output
assert "Compiling 'contracts/ContractA.json'" not in result.output

0 comments on commit 38e3ae8

Please sign in to comment.