diff --git a/codelimit/__main__.py b/codelimit/__main__.py index f25c757..a49f633 100755 --- a/codelimit/__main__.py +++ b/codelimit/__main__.py @@ -1,10 +1,11 @@ import os from pathlib import Path -from typing import List, Annotated +from typing import List, Annotated, Optional import typer from codelimit.common.CheckResult import CheckResult +from codelimit.common.Configuration import Configuration from codelimit.common.Scanner import scan_codebase, is_excluded from codelimit.common.report.Report import Report from codelimit.common.report.ReportReader import ReportReader @@ -91,8 +92,14 @@ def upload( @cli.callback() -def main(): +def main( + exclude: Annotated[ + Optional[list[str]], typer.Option(help="Glob patterns for exclusion") + ] = None +): """CodeLimit: Your refactoring alarm.""" + if exclude: + Configuration.excludes.extend(exclude) if __name__ == "__main__": diff --git a/codelimit/common/Configuration.py b/codelimit/common/Configuration.py index ddf21c1..e23a1ac 100644 --- a/codelimit/common/Configuration.py +++ b/codelimit/common/Configuration.py @@ -1,28 +1,29 @@ -default_excludes = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".git-rewrite", - ".hg", - ".ipynb_checkpoints", - ".mypy_cache", - ".nox", - ".pants.d", - ".pytest_cache", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - ".vscode", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "venv", - "test", - "tests", -] +class Configuration: + excludes = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", + "test", + "tests", + ] diff --git a/codelimit/common/Scanner.py b/codelimit/common/Scanner.py index c66f315..e16c108 100644 --- a/codelimit/common/Scanner.py +++ b/codelimit/common/Scanner.py @@ -1,3 +1,4 @@ +import fnmatch import os from os.path import relpath from pathlib import Path @@ -6,7 +7,7 @@ from halo import Halo from codelimit.common.Codebase import Codebase -from codelimit.common.Configuration import default_excludes +from codelimit.common.Configuration import Configuration from codelimit.common.Language import Language from codelimit.common.Location import Location from codelimit.common.Measurement import Measurement @@ -96,7 +97,13 @@ def scan_file(language: Language, path: str) -> list[Measurement]: def is_excluded(path: Path): - for part in path.parts[:-1]: - if part in default_excludes: - return True + for exclude in Configuration.excludes: + exclude_parts = exclude.split(os.sep) + if len(exclude_parts) == 1: + for part in path.parts: + if fnmatch.fnmatch(part, exclude): + return True + else: + if fnmatch.fnmatch(str(path), exclude): + return True return False diff --git a/docs/configuration.md b/docs/configuration.md index 5e036bd..cae964e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -18,13 +18,57 @@ Or you can ignore a function by putting a `# nocl` comment on any line of the header: ```python -def some_function(): # nocl +def some_function(): # nocl ... ``` ```python def some_functions( some_numbers: list[int] -) -> int: # nocl +) -> int: # nocl ... ``` + +## Excluding files + +Files can be excluded from analysis by using the `--exclude` option. +This option can be used multiple times and takes a [glob pattern](https://en.wikipedia.org/wiki/Glob_(programming)) as a +value, for example: + +```shell +codelimit --exclude "*.generated.py" --exclude "docs/*" ... +``` + +The `--exclude` option extends the default exclusion list. +The default exclusion list is: + +```python +[ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", + "test", + "tests", +] +``` \ No newline at end of file diff --git a/tests/common/test_Scanner.py b/tests/common/test_Scanner.py index 217ad44..eb63541 100644 --- a/tests/common/test_Scanner.py +++ b/tests/common/test_Scanner.py @@ -2,7 +2,8 @@ import tempfile from pathlib import Path -from codelimit.common.Scanner import scan_codebase +from codelimit.common.Configuration import Configuration +from codelimit.common.Scanner import scan_codebase, is_excluded from codelimit.common.source_utils import get_location_range @@ -68,3 +69,18 @@ def test_skip_hidden_files(): assert result.total_loc() == 2 assert len(result.all_measurements()) == 1 assert result.all_files()[0] == "foo.py" + + +def test_is_excluded(): + assert is_excluded(Path("venv/foo/bar.py")) + assert not is_excluded(Path("foo/bar.py")) + + Configuration.excludes = ["output"] + + assert is_excluded(Path("output/foo/bar.py")) + assert not is_excluded(Path("venv/foo/bar.py")) + assert not is_excluded(Path("foo/bar.py")) + + Configuration.excludes = ["foo/bar/*"] + + assert is_excluded(Path("foo/bar/foobar.py"))