Skip to content

Commit

Permalink
-x/--exclude option, closes #27
Browse files Browse the repository at this point in the history
  • Loading branch information
simonw committed Jun 21, 2023
1 parent 2f09285 commit d022c11
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 8 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,14 @@ To search within a specific file, pass that file using the `-f` option. You can
```bash
symbex MyClass -f my_file.py
```
To search within a specific directory and all of its subdirectories, use the `-d` option:
To search within a specific directory and all of its subdirectories, use the `-d/--directory` option:
```bash
symbex Database -d ~/projects/datasette
```
You can exclude files in specified directories using the `-x/--exclude` option:
```bash
symbex Database -d ~/projects/datasette -x ~/projects/datasette/tests
```
If `symbex` encounters any Python code that it cannot parse, it will print a warning message and continue searching:
```
# Syntax error in path/badcode.py: expected ':' (<unknown>, line 1)
Expand Down Expand Up @@ -301,6 +305,7 @@ Options:
--version Show the version and exit.
-f, --file FILE Files to search
-d, --directory DIRECTORY Directories to search
-x, --exclude DIRECTORY Directories to exclude
-s, --signatures Show just function and class signatures
--docstrings Show function and class signatures plus docstrings
--count Show count of matching symbols
Expand Down
22 changes: 22 additions & 0 deletions symbex/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
multiple=True,
help="Directories to search",
)
@click.option(
"excludes",
"-x",
"--exclude",
type=click.Path(file_okay=False, dir_okay=True, resolve_path=True),
multiple=True,
help="Directories to exclude",
)
@click.option(
"-s",
"--signatures",
Expand Down Expand Up @@ -96,6 +104,7 @@ def cli(
symbols,
files,
directories,
excludes,
signatures,
docstrings,
count,
Expand Down Expand Up @@ -198,10 +207,15 @@ def cli(
if not files and not directories:
directories = ["."]

excludes = [pathlib.Path(exclude) for exclude in excludes]

def iterate_files():
yield from (pathlib.Path(f) for f in files)
for directory in directories:
for path in pathlib.Path(directory).rglob("*.py"):
# Skip if path is inside any of 'excludes'
if any(is_subpath(path, exclude) for exclude in excludes):
continue
if path.is_file():
yield path

Expand Down Expand Up @@ -288,3 +302,11 @@ def filter(node: ast.AST) -> bool:
print()
if count:
click.echo(num_matches)


def is_subpath(path: pathlib.Path, parent: pathlib.Path) -> bool:
try:
path.relative_to(parent)
return True
except ValueError:
return False
22 changes: 15 additions & 7 deletions tests/test_symbex.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def directory_full_of_code(tmpdir):
for path, content in (
("foo.py", "def foo1():\n pass\n\n@decorated\ndef foo2():\n pass\n\n"),
("bar.py", "class BarClass:\n pass\n\n"),
("nested.py/baz.py", 'def baz(delimiter=", ", type=str):\n pass\n\n'),
("nested.py/x/baz.py", 'def baz(delimiter=", ", type=str):\n pass\n\n'),
("nested.py/error.py", "def baz_error()" + "bug:\n pass\n\n"),
(
"methods.py",
Expand Down Expand Up @@ -68,21 +68,26 @@ async def async_method(a, b, c):
),
(
["baz", "--silent"],
'# File: nested.py/baz.py Line: 1\ndef baz(delimiter=", ", type=str):\n pass\n\n',
'# File: nested.py/x/baz.py Line: 1\ndef baz(delimiter=", ", type=str):\n pass\n\n',
),
(
["async_func", "--silent"],
"# File: async.py Line: 1\nasync def async_func(a, b, c):\n pass\n\n",
),
# The -f option
(
["baz", "-f", "nested.py/baz.py", "--silent"],
'# File: nested.py/baz.py Line: 1\ndef baz(delimiter=", ", type=str):\n pass\n\n',
["baz", "-f", "nested.py/x/baz.py", "--silent"],
'# File: nested.py/x/baz.py Line: 1\ndef baz(delimiter=", ", type=str):\n pass\n\n',
),
# The -d option
(
["baz", "-d", "nested.py", "--silent"],
'# File: nested.py/baz.py Line: 1\ndef baz(delimiter=", ", type=str):\n pass\n\n',
'# File: nested.py/x/baz.py Line: 1\ndef baz(delimiter=", ", type=str):\n pass\n\n',
),
# The -d option with -x to exclude
(
["baz", "-d", "nested.py", "-x", "nested.py/x/", "--silent"],
"",
),
# Classes
(
Expand Down Expand Up @@ -154,7 +159,10 @@ def test_fixture(directory_full_of_code, monkeypatch, args, expected):
(["BarClass", "--silent"], "# File: bar.py Line: 1\n" "class BarClass"),
(
["baz", "--silent"],
("# File: nested.py/baz.py Line: 1\n" 'def baz(delimiter=", ", type=str)'),
(
"# File: nested.py/x/baz.py Line: 1\n"
'def baz(delimiter=", ", type=str)'
),
),
),
)
Expand All @@ -174,7 +182,7 @@ def test_errors(directory_full_of_code, monkeypatch):
result = runner.invoke(cli, ["baz"], catch_exceptions=False)
assert result.exit_code == 0
expected = (
"# File: nested.py/baz.py Line: 1\n"
"# File: nested.py/x/baz.py Line: 1\n"
'def baz(delimiter=", ", type=str):\n'
" pass\n\n"
)
Expand Down

0 comments on commit d022c11

Please sign in to comment.