Skip to content
Open
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
30 changes: 30 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ disabled_rules = ["NTX1", "NTX2"]
# Additional paths to search for imports
path = ["lib", "vendor/contracts"]

# Files or directories to exclude from linting
exclude = ["build/", "tests/mocks/"]

# Rule-specific configurations
[tool.natrix.rule_configs.MemoryExpansion]
max_frame_size = 25000
Expand Down Expand Up @@ -69,6 +72,32 @@ This is equivalent to using the `-p` flag in the command line:
natrix contract.vy -p lib vendor/contracts ../shared
```

### Excluding Files and Directories

Exclude specific files or directories from linting:

```toml
[tool.natrix]
# Exclude single directory
exclude = ["build/"]

# Exclude multiple files and directories
exclude = ["build/", "tests/mocks/", "temp.vy"]

# Relative paths are resolved relative to pyproject.toml location
exclude = ["contracts/legacy/", "../generated/contracts/"]
```

Files and directories in the exclude list will be completely ignored during linting. This is useful for:
- Third-party modules
- Mock contracts from other projects
- Deployed legacy code that should remain unchanged

You can also exclude files via command line:
```bash
natrix --exclude build/ tests/fixtures/ temp.vy
```

### Disabling Rules

Disable specific rules globally across your project:
Expand Down Expand Up @@ -124,6 +153,7 @@ This outputs issues as a JSON array instead of the default colored terminal outp
[tool.natrix]
files = ["contracts/core/", "contracts/interfaces/", "tests/unit/"]
path = ["lib/snekmate", "lib/vyper-utils"]
exclude = ["contracts/legacy/", "test/mocks/"]
disabled_rules = ["NTX7"]

[tool.natrix.rule_configs.MemoryExpansion]
Expand Down
46 changes: 41 additions & 5 deletions natrix/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,32 @@ def lint_file(
return issues


def find_vy_files(directory: Path) -> list[Path]:
def find_vy_files(directory: Path, exclude_paths: list[Path] | None = None) -> list[Path]:
# Recursively find all Vyper files (.vy and .vyi) in the given directory,
# excluding specified directories
# excluding specified files and directories
if exclude_paths is None:
exclude_paths = []

vy_files = []
for root, _, files in os.walk(directory):
# Collect all Vyper files
for file in files:
file_path = Path(root) / file
if file_path.suffix in VYPER_EXTENSIONS:
vy_files.append(file_path)
# Check if this file should be excluded
file_path_resolved = file_path.resolve()
should_exclude = False

for exclude_path in exclude_paths:
exclude_path_resolved = exclude_path.resolve()
# Check if file matches exactly or is within an excluded directory
if (file_path_resolved == exclude_path_resolved or
exclude_path_resolved in file_path_resolved.parents):
should_exclude = True
break

if not should_exclude:
vy_files.append(file_path)

return vy_files

Expand All @@ -134,6 +150,7 @@ def read_pyproject_config() -> dict[str, Any]:
"disabled_rules": set(),
"rule_configs": {},
"path": [],
"exclude": [],
}

try:
Expand Down Expand Up @@ -172,6 +189,13 @@ def read_pyproject_config() -> dict[str, Any]:
(project_root / path).resolve()
for path in natrix_config["path"]
]
if "exclude" in natrix_config and isinstance(
natrix_config["exclude"], list
):
config["exclude"] = [
(project_root / path).resolve()
for path in natrix_config["exclude"]
]
except Exception as e:
print(f"Warning: Error reading pyproject.toml: {e}")

Expand Down Expand Up @@ -237,6 +261,13 @@ def parse_args() -> argparse.Namespace:
action="store_true",
help="Output issues in JSON format.",
)
lint_parser.add_argument(
"-e",
"--exclude",
type=str,
nargs="+",
help="List of files or directories to exclude from linting (e.g., --exclude tests/ build/).",
)

# Create the codegen subcommand parser
codegen_parser = subparsers.add_parser("codegen", help="Code generation utilities")
Expand Down Expand Up @@ -396,6 +427,11 @@ def main() -> None:
if args.path:
extra_paths.extend(args.path)

# Combine exclude paths from CLI and pyproject.toml
exclude_paths = pyproject_config.get("exclude", [])
if args.exclude:
exclude_paths.extend([Path(p).resolve() for p in args.exclude])

# Handle files
if args.files:
all_vy_files = []
Expand All @@ -404,7 +440,7 @@ def main() -> None:
if path.is_file() and path.suffix in VYPER_EXTENSIONS:
all_vy_files.append(path)
elif path.is_dir():
dir_vy_files = find_vy_files(path)
dir_vy_files = find_vy_files(path, exclude_paths)
if not dir_vy_files:
formatter.print(f"No Vyper files found in the directory: {path}")
all_vy_files.extend(dir_vy_files)
Expand All @@ -419,7 +455,7 @@ def main() -> None:
else:
# If no paths are provided, search for Vyper files in the current
# directory recursively
all_vy_files = find_vy_files(Path())
all_vy_files = find_vy_files(Path(), exclude_paths)

if not all_vy_files:
formatter.print("No Vyper files found in the current directory.")
Expand Down