Skip to content

Commit 306d5e7

Browse files
committed
Add --exclude option to implement file exclusion
- Add --exclude (repeatable) option to beagle search for glob-based file/path exclusion. - Apply exclusion logic in search results, skipping files matching any exclude pattern. - Create and standardize tests using pytest style. - Add edge case and pattern coverage to tests. - Update .gitignore for standard Python/dev artifacts. - Add [testenv:unit] to tox.ini for pytest-based tests and include in envlist. - Update CI to run tox -e unit for unit tests.
1 parent 5bc8e07 commit 306d5e7

6 files changed

Lines changed: 88 additions & 2 deletions

File tree

.github/workflows/main.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ jobs:
2828
run: tox -e pep8
2929
- name: Check covering
3030
run: tox -e cover
31+
- name: Run unit tests
32+
run: tox -e unit
3133
- name: Test with tox
3234
run: tox
3335

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ doc/build
77
.coverage
88
build/
99
.venv/
10-
*/__pycache__/
10+
*__pycache__/

beagle/search.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ def get_parser(self, prog_name):
5555
dest='file_pattern',
5656
help='file name pattern',
5757
)
58+
parser.add_argument(
59+
'--exclude',
60+
dest='exclude',
61+
action='append',
62+
default=[],
63+
help='exclude files or paths matching glob pattern (repeatable)',
64+
)
5865
parser.add_argument(
5966
'--ignore-case',
6067
default=False,
@@ -96,6 +103,12 @@ def check_repo(repo):
96103

97104
for repo, repo_matches in sorted(interesting_repos):
98105
for repo_match in repo_matches['Matches']:
106+
filename = repo_match['Filename']
107+
# Exclude files matching any exclude pattern
108+
if parsed_args.exclude:
109+
if any(fnmatch.fnmatch(filename, pat)
110+
for pat in parsed_args.exclude):
111+
continue
99112
for file_match in repo_match['Matches']:
100113
if (parsed_args.ignore_comments and
101114
file_match['Line'].lstrip().startswith(

tests/__init__.py

Whitespace-only changes.

tests/test_search.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import unittest
2+
3+
4+
class DummyArgs:
5+
def __init__(self, exclude=None, repo_pattern=None,
6+
ignore_comments=False, comment_marker='#', context_lines=0):
7+
self.exclude = exclude or []
8+
self.repo_pattern = repo_pattern or ''
9+
self.ignore_comments = ignore_comments
10+
self.comment_marker = comment_marker
11+
self.context_lines = context_lines
12+
13+
14+
class TestBeagleSearchExclude(unittest.TestCase):
15+
def setUp(self):
16+
# Simulate results as returned by hound.query
17+
self.results = {
18+
'repo1': {
19+
'Matches': [
20+
{'Filename': 'src/main.py',
21+
'Matches': [{'LineNumber': 1,
22+
'Line': 'foo', 'Before': [], 'After': []}]},
23+
{'Filename': 'src/test_utils.py',
24+
'Matches': [{'LineNumber': 2,
25+
'Line': 'bar', 'Before': [], 'After': []}]},
26+
{'Filename': 'docs/readme.py',
27+
'Matches': [{'LineNumber': 3, 'Line': 'baz',
28+
'Before': [], 'After': []}]},
29+
]
30+
}
31+
}
32+
33+
def test_exclude_pattern(self):
34+
from beagle.search import Search
35+
search = Search(app=None, app_args=None)
36+
args = DummyArgs(exclude=['*test*', '*doc*'])
37+
found = list(search._flatten_results(self.results, args))
38+
# Only src/main.py should remain
39+
self.assertEqual(len(found), 1)
40+
self.assertIn('src/main.py', found[0])
41+
42+
def test_no_exclude(self):
43+
from beagle.search import Search
44+
search = Search(app=None, app_args=None)
45+
args = DummyArgs(exclude=[])
46+
found = list(search._flatten_results(self.results, args))
47+
self.assertEqual(len(found), 3)
48+
self.assertIn('src/main.py', found[0])
49+
self.assertIn('src/test_utils.py', found[1])
50+
self.assertIn('docs/readme.py', found[2])
51+
52+
def test_partial_exclude(self):
53+
from beagle.search import Search
54+
search = Search(app=None, app_args=None)
55+
args = DummyArgs(exclude=['*test*'])
56+
found = list(search._flatten_results(self.results, args))
57+
self.assertEqual(len(found), 2)
58+
self.assertIn('src/main.py', found[0])
59+
self.assertIn('docs/readme.py', found[1])
60+
61+
62+
if __name__ == '__main__':
63+
unittest.main()

tox.ini

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tox]
22
minversion = 3.2.0
3-
envlist = py38,pep8
3+
envlist = py38,pep8,unit
44

55
[testenv]
66
usedevelop = True
@@ -30,6 +30,14 @@ commands =
3030
python setup.py sdist
3131
sphinx-build -a -E -W -b html doc/source doc/build/html;;
3232

33+
[testenv:unit]
34+
description = Run pytest-based unit tests
35+
deps =
36+
pytest
37+
-rrequirements.txt
38+
commands =
39+
pytest tests
40+
3341
[flake8]
3442
# E123, E125 skipped as they are invalid PEP-8.
3543
# W504 skipped to give priority to W503

0 commit comments

Comments
 (0)