Skip to content

Commit a91eeeb

Browse files
committed
bears/general: Add RegexLintBear
Closes #1532
1 parent c3ae6bc commit a91eeeb

File tree

5 files changed

+142
-0
lines changed

5 files changed

+142
-0
lines changed

bear-languages.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ RadonBear:
329329
- Python
330330
- Python 2
331331
- Python 3
332+
RegexLintBear:
332333
RuboCopBear:
333334
- Ruby
334335
RubyFastererBear:

bear-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pylint~=1.7.2
3131
pyroma~=2.2.0
3232
pyyaml~=3.12
3333
radon==1.4.0
34+
regexlint~=1.6
3435
restructuredtext-lint~=1.0
3536
rstcheck~=3.1
3637
safety~=1.8.2

bear-requirements.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ pip_requirements:
6868
version: ~=3.12
6969
radon:
7070
version: ==1.4.0
71+
regexlint:
72+
version: ~=1.6
7173
restructuredtext-lint:
7274
version: ~=1.0
7375
rstcheck:

bears/general/RegexLintBear.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import re
2+
3+
from queue import Queue
4+
from sarge import run, Capture
5+
from contextlib import suppress
6+
7+
from bears.general.AnnotationBear import AnnotationBear
8+
9+
from coalib.bears.LocalBear import LocalBear
10+
from coalib.results.Result import Result
11+
from coalib.settings.Section import Section
12+
from coalib.settings.Setting import Setting
13+
from coalib.testing.LocalBearTestHelper import execute_bear
14+
15+
from dependency_management.requirements.PipRequirement import PipRequirement
16+
17+
18+
class RegexLintBear(LocalBear):
19+
LANGUAGES = {'All'}
20+
REQUIREMENTS = {PipRequirement('regexlint', '1.6')}
21+
AUTHORS = {'The coala developers'}
22+
AUTHORS_EMAILS = {'[email protected]'}
23+
LICENSE = 'AGPL-3.0'
24+
CAN_DETECT = {'Formatting'}
25+
26+
def run(self, filename, file, language: str):
27+
"""
28+
Bear for linting regex through regexlint.
29+
30+
:param language:
31+
The programming language of the file(s).
32+
"""
33+
section = Section('')
34+
section.append(Setting('language', language))
35+
bear = AnnotationBear(section, Queue())
36+
37+
with execute_bear(bear, filename, file) as result:
38+
for src_range in result[0].contents['strings']:
39+
src_line = src_range.affected_source({filename: file})[0]
40+
regex = src_line[src_range.start.column:src_range.end.column-1]
41+
with suppress(re.error):
42+
re.compile(regex)
43+
out = run('regexlint --regex "{}"'.format(regex),
44+
stdout=Capture()).stdout.text
45+
if out[-3:-1] != 'OK':
46+
yield Result.from_values(
47+
origin=self,
48+
message=out,
49+
file=filename,
50+
line=src_range.start.line,
51+
column=src_range.start.column,
52+
end_line=src_range.end.line,
53+
end_column=src_range.end.column,
54+
)

tests/general/RegexLintBearTest.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
from os.path import abspath
2+
from queue import Queue
3+
4+
from bears.general.RegexLintBear import RegexLintBear
5+
from coalib.testing.LocalBearTestHelper import (
6+
LocalBearTestHelper, execute_bear)
7+
from coalib.results.Result import Result
8+
from coalib.settings.Section import Section
9+
from coalib.settings.Setting import Setting
10+
11+
12+
test_good_python_file = """
13+
some_regex = r'[a-zA-Z]]'
14+
"""
15+
16+
test_bad_python_file = """
17+
some_regex = r'(else|elseif)'
18+
"""
19+
20+
test_good_cpp_file = """
21+
char some_regex[] = "[a-zA-Z]]";
22+
"""
23+
24+
test_bad_cpp_file = """
25+
char some_regex[13] = "(else|elseif)";
26+
"""
27+
28+
test_re_error_file = """
29+
some_regex = r'*ab' # This should be skipped
30+
some_other_regex = r'[a-z]'
31+
"""
32+
33+
BAD_MESSAGE = ('E105:argv:root:0: Potential out of order alternation between '
34+
'\'else\' and \'elseif\'\n'
35+
' \'(else|elseif)\'\n'
36+
' ^ here\n')
37+
38+
39+
class RegexLintBearTest(LocalBearTestHelper):
40+
41+
def setUp(self):
42+
self.section = Section('')
43+
self.queue = Queue()
44+
self.uut = RegexLintBear(self.section, self.queue)
45+
46+
def test_good_python_file(self):
47+
self.section.append(Setting('language', 'python 3'))
48+
with execute_bear(self.uut, abspath('good_python_file'),
49+
test_good_python_file.splitlines(True)) as results:
50+
self.assertEqual(results, [])
51+
52+
def test_bad_python_file(self):
53+
self.section.append(Setting('language', 'python 3'))
54+
with execute_bear(self.uut, abspath('bad_python_file'),
55+
test_bad_python_file.splitlines()) as results:
56+
self.assertEqual(results[0],
57+
Result.from_values(origin=self.uut,
58+
message=BAD_MESSAGE,
59+
file='bad_python_file',
60+
line=2, column=15,
61+
end_line=2, end_column=29))
62+
63+
def test_good_cpp_file(self):
64+
self.section.append(Setting('language', 'cpp'))
65+
with execute_bear(self.uut, abspath('good_cpp_file'),
66+
test_good_cpp_file.splitlines()) as results:
67+
self.assertEqual(results, [])
68+
69+
def test_bad_cpp_file(self):
70+
self.section.append(Setting('language', 'cpp'))
71+
with execute_bear(self.uut, abspath('bad_cpp_file'),
72+
test_bad_cpp_file.splitlines()) as results:
73+
self.assertEqual(results[0],
74+
Result.from_values(origin=self.uut,
75+
message=BAD_MESSAGE,
76+
file='bad_cpp_file',
77+
line=2, column=23,
78+
end_line=2, end_column=37))
79+
80+
def test_re_error_file(self):
81+
self.section.append(Setting('language', 'cpp'))
82+
with execute_bear(self.uut, abspath('re_error_file'),
83+
test_re_error_file.splitlines()) as results:
84+
self.assertEqual(results, [])

0 commit comments

Comments
 (0)