Skip to content

Commit 3ead316

Browse files
committed
Resolve #8 -- Add RegEx based filePattern attribute in favor of filename
1 parent 804d3d9 commit 3ead316

File tree

4 files changed

+69
-35
lines changed

4 files changed

+69
-35
lines changed

.relint.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
- name: No ToDo
22
pattern: "[tT][oO][dD][oO]"
33
hint: Get it done right away!
4-
filename:
5-
- "*.py"
6-
- "*.js"
4+
filePattern: "((?!test_).).*\\.(py|js)"
75
error: false

README.rst

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,15 @@ You can write your own regular rules in a YAML file, like so:
2222
- name: No ToDo
2323
pattern: "[tT][oO][dD][oO]"
2424
hint: Get it done right away!
25-
filename:
26-
- "*.py"
27-
- "*.js"
25+
filePatter: ".*\.(py|js)"
2826
error: false
2927
3028
The ``name`` attribute is the name of your linter, the ``pattern`` can be
3129
any regular expression. The linter does lint entire files, therefore your
3230
expressions can match multiple lines and include newlines.
3331

3432
You can narrow down the file types your linter should be working with, by
35-
providing the optional ``filename`` attribute. The default is ``*``.
33+
providing the optional ``filePattern`` attribute. The default is ``.*``.
3634

3735
The optional `error` attribute allows you to only show a warning but not exit
3836
with a bad (non-zero) exit code. The default is `true`.
@@ -59,7 +57,7 @@ with `pre-commit`_ framework:
5957
.. code-block:: YAML
6058
6159
- repo: https://github.com/codingjoe/relint
62-
rev: 0.5.0
60+
rev: 1.2.0
6361
hooks:
6462
- id: relint
6563
@@ -73,28 +71,22 @@ Samples
7371
- name: db fixtures
7472
pattern: "def test_[^(]+\\([^)]*(customer|product)(, |\\))"
7573
hint: Use model_mommy recipies instead of db fixtures.
76-
filename:
77-
- "**/test_*.py"
74+
filePattern: "test_.*\.py"
7875
7976
- name: model_mommy recipies
8077
pattern: "mommy\\.make\\("
8178
hint: Please use mommy.make_recipe instead of mommy.make.
82-
filename:
83-
- "**/test_*.py"
84-
- "conftest.py"
85-
- "**/conftest.py"
79+
filePattern: "(test_.*|conftest)\.py"
8680
8781
- name: the database is lava
8882
pattern: "@pytest.fixture.*\\n[ ]*def [^(]+\\([^)]*(db|transactional_db)(, |\\))"
8983
hint: Please do not create db fixtures but model_mommy recipies instead.
90-
filename:
91-
- "*.py"
84+
filePattern: ".*\.py"
9285
9386
- name: No logger in management commands
9487
pattern: "(logger|import logging)"
9588
hint: "Please write to self.stdout or self.stderr in favor of using a logger."
96-
filename:
97-
- "*/management/commands/*.py"
89+
filePattern: "\/management\/commands\/.*\.py"
9890
9991
.. _`pre-commit`: https://pre-commit.com/
10092
.. _`relint-pre-commit.sh`: relint-pre-commit.sh

relint.py

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import glob
44
import re
55
import sys
6+
import warnings
67
from collections import namedtuple
78
from itertools import chain
89

@@ -16,10 +17,19 @@
1617
r"(?:\n|^)diff --git a\/.* b\/.*(?:\n|$)")
1718

1819

19-
Test = namedtuple('Test', ('name', 'pattern', 'hint', 'filename', 'error'))
20+
Test = namedtuple(
21+
'Test', (
22+
'name',
23+
'pattern',
24+
'hint',
25+
'file_pattern',
26+
'filename',
27+
'error',
28+
)
29+
)
2030

2131

22-
def parse_args():
32+
def parse_args(args):
2333
parser = argparse.ArgumentParser()
2434
parser.add_argument(
2535
'files',
@@ -42,19 +52,29 @@ def parse_args():
4252
action='store_true',
4353
help='Analyze content from git diff.'
4454
)
45-
return parser.parse_args()
55+
return parser.parse_args(args=args)
4656

4757

4858
def load_config(path):
4959
with open(path) as fs:
5060
for test in yaml.safe_load(fs):
51-
filename = test.get('filename', ['*'])
52-
if not isinstance(filename, list):
53-
filename = list(filename)
61+
filename = test.get('filename')
62+
if filename:
63+
warnings.warn(
64+
"The glob style 'filename' configuration attribute has been"
65+
" deprecated in favor of a new RegEx based 'filePattern' attribute."
66+
" 'filename' support will be removed in relint version 2.0.",
67+
DeprecationWarning
68+
)
69+
if not isinstance(filename, list):
70+
filename = list(filename)
71+
file_pattern = test.get('filePattern', '.*')
72+
file_pattern = re.compile(file_pattern)
5473
yield Test(
5574
name=test['name'],
5675
pattern=re.compile(test['pattern'], re.MULTILINE),
5776
hint=test.get('hint'),
77+
file_pattern=file_pattern,
5878
filename=filename,
5979
error=test.get('error', True)
6080
)
@@ -68,10 +88,16 @@ def lint_file(filename, tests):
6888
pass
6989
else:
7090
for test in tests:
71-
if any(fnmatch.fnmatch(filename, fp) for fp in test.filename):
72-
for match in test.pattern.finditer(content):
73-
line_number = match.string[:match.start()].count('\n') + 1
74-
yield filename, test, match, line_number
91+
if test.filename:
92+
if any(fnmatch.fnmatch(filename, fp) for fp in test.filename):
93+
for match in test.pattern.finditer(content):
94+
line_number = match.string[:match.start()].count('\n') + 1
95+
yield filename, test, match, line_number
96+
else:
97+
if test.file_pattern.match(filename):
98+
for match in test.pattern.finditer(content):
99+
line_number = match.string[:match.start()].count('\n') + 1
100+
yield filename, test, match, line_number
75101

76102

77103
def parse_line_numbers(output):
@@ -183,8 +209,8 @@ def parse_diff(output):
183209
return changed_content
184210

185211

186-
def main():
187-
args = parse_args()
212+
def main(args=sys.argv):
213+
args = parse_args(args)
188214
paths = {
189215
path
190216
for file in args.files
@@ -207,5 +233,9 @@ def main():
207233
exit(exit_code)
208234

209235

236+
if not sys.warnoptions:
237+
warnings.simplefilter("default")
238+
239+
210240
if __name__ == '__main__':
211241
main()

test_relint.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import io
22
import sys
3+
import warnings
34

45
import pytest
56

@@ -10,10 +11,8 @@
1011
class TestMain:
1112
@pytest.mark.parametrize('filename', ['test_relint.py', '[a-b].py', '[b-a].py'])
1213
def test_main_execution(self, mocker, filename):
13-
mocker.patch.object(sys, 'argv', ['relint.py', filename])
14-
1514
with pytest.raises(SystemExit) as exc_info:
16-
main()
15+
main(['relint.py', filename])
1716

1817
assert exc_info.value.code == 0
1918

@@ -26,12 +25,11 @@ def test_main_execution_with_diff(self, capsys, mocker, tmpdir):
2625
"+# TODO do something"
2726
)
2827

29-
mocker.patch.object(sys, 'argv', ['relint.py', 'dummy.py', '--diff'])
3028
mocker.patch.object(sys, 'stdin', diff)
3129

3230
with tmpdir.as_cwd():
3331
with pytest.raises(SystemExit) as exc_info:
34-
main()
32+
main(['relint.py', 'dummy.py', '--diff'])
3533

3634
expected_message = 'Hint: Get it done right away!'
3735

@@ -144,3 +142,19 @@ def test_parse_complete_diff(self):
144142
expected = {'test_relint.py': [2]}
145143

146144
assert parsed_content == expected
145+
146+
def test_filename_warning(self, tmpdir):
147+
tmpdir.join('.relint.yml').write(
148+
'- name: old\n'
149+
' pattern: ".*"\n'
150+
' filename: "*.py"\n'
151+
)
152+
153+
with tmpdir.as_cwd():
154+
with warnings.catch_warnings(record=True) as w:
155+
with pytest.raises(SystemExit) as exc_info:
156+
main(['**'])
157+
158+
assert exc_info.value.code == 0
159+
assert issubclass(w[-1].category, DeprecationWarning)
160+
assert "'filename'" in str(w[-1].message)

0 commit comments

Comments
 (0)