Allow checks to provide a extension matcher

When a check is for a rst file vs a plaintext
file and is not interoperable this change allows
that check to declare that it only knows how to
scan certain files.

It also fixes the max line checker to use a similar
routine to differentiate between rst files and non
rst files and uses a different routine based on this
decision.

Change-Id: I1e3e5f9f1631ed5babe2df17c987e8e872938a38
This commit is contained in:
Joshua Harlow 2014-08-30 18:00:21 -07:00 committed by Joshua Harlow
parent 9b439777f4
commit 15ee57b572
4 changed files with 68 additions and 31 deletions

View File

@ -75,6 +75,7 @@ class CheckCarriageReturn(LineCheck):
class CheckValidity(ContentCheck):
REPORTS = frozenset(["D000"])
EXT_MATCHER = re.compile(r"(.*)[.]rst", re.I)
# Only used when running in sphinx mode.
SPHINX_IGNORES_REGEX = [
@ -107,6 +108,11 @@ class CheckValidity(ContentCheck):
class CheckMaxLineLength(ContentCheck):
REPORTS = frozenset(["D001"])
def __init__(self, cfg):
super(CheckMaxLineLength, self).__init__(cfg)
self._max_line_length = self._cfg['max_line_length']
self._allow_long_titles = self._cfg['allow_long_titles']
def _extract_node_lines(self, doc):
def extract_lines(node, start_line):
@ -185,10 +191,14 @@ class CheckMaxLineLength(ContentCheck):
directives.append((i, find_directive_end(i, lines)))
return directives
def report_iter(self, parsed_file):
doc = parsed_file.document
lines = list(parsed_file.lines_iter())
def _txt_checker(self, parsed_file):
for i, line in enumerate(parsed_file.lines_iter()):
if len(line) > self._max_line_length:
yield (i + 1, 'D001', 'Line too long')
def _rst_checker(self, parsed_file):
lines = list(parsed_file.lines_iter())
doc = parsed_file.document
nodes_lines, first_line = self._extract_node_lines(doc)
directives = self._extract_directives(lines)
@ -225,10 +235,8 @@ class CheckMaxLineLength(ContentCheck):
docutils_nodes.subtitle,
docutils_nodes.section,
)
max_line_length = self._cfg['max_line_length']
allow_long_titles = self._cfg['allow_long_titles']
for i, line in enumerate(lines):
if len(line) > max_line_length:
if len(line) > self._max_line_length:
in_directive = False
for (start, end) in directives:
if i >= start and i <= end:
@ -245,6 +253,14 @@ class CheckMaxLineLength(ContentCheck):
nodes = find_containing_nodes(i + 1)
if any_types(nodes, skip_types):
continue
if allow_long_titles and any_types(nodes, title_types):
if self._allow_long_titles and any_types(nodes, title_types):
continue
yield (i + 1, 'D001', 'Line too long')
def report_iter(self, parsed_file):
if parsed_file.extension.lower() != '.rst':
checker_func = self._txt_checker
else:
checker_func = self._rst_checker
for issue in checker_func(parsed_file):
yield issue

View File

@ -216,6 +216,16 @@ def main():
check_name = ".".join([c.__class__.__module__,
c.__class__.__name__])
error_counts.setdefault(check_name, 0)
try:
extension_matcher = c.EXT_MATCHER
except AttributeError:
pass
else:
if not extension_matcher.match(f.extension):
print(" Skipping check '%s' since it does not understand"
" parsing a file with extension '%s'"
% (check_name, f.extension))
continue
try:
reports = set(c.REPORTS)
except AttributeError:

View File

@ -33,6 +33,7 @@ class ParsedFile(object):
self._encoding = encoding
self._doc = None
self._errors = None
self._extension = os.path.splitext(filename)[1]
@property
def errors(self):
@ -76,6 +77,10 @@ class ParsedFile(object):
line = line[0:-1]
yield line
@property
def extension(self):
return self._extension
@property
def filename(self):
return self._filename

View File

@ -73,20 +73,21 @@ test
content += "\n\n"
content += ("a" * 60) + " " + ("b" * 60)
content += "\n"
conf = {
'max_line_length': 79,
'allow_long_titles': True,
}
for ext in ['.rst', '.txt']:
with tempfile.NamedTemporaryFile(suffix=ext) as fh:
fh.write(content)
fh.flush()
with tempfile.NamedTemporaryFile() as fh:
fh.write(content)
fh.flush()
parsed_file = parser.ParsedFile(fh.name)
conf = {
'max_line_length': 79,
'allow_long_titles': True,
}
check = checks.CheckMaxLineLength(conf)
errors = list(check.report_iter(parsed_file))
self.assertEqual(1, len(errors))
(line, code, msg) = errors[0]
self.assertIn(code, check.REPORTS)
parsed_file = parser.ParsedFile(fh.name)
check = checks.CheckMaxLineLength(conf)
errors = list(check.report_iter(parsed_file))
self.assertEqual(1, len(errors))
(line, code, msg) = errors[0]
self.assertIn(code, check.REPORTS)
def test_unsplittable_length(self):
content = """
@ -102,15 +103,20 @@ test
content += "\n\n"
content += "a" * 100
content += "\n"
conf = {
'max_line_length': 79,
'allow_long_titles': True,
}
# This number is different since rst parsing is aware that titles
# are allowed to be over-length, while txt parsing is not aware of
# this fact (since it has no concept of title sections).
extensions = [(0, '.rst'), (1, '.txt')]
for expected_errors, ext in extensions:
with tempfile.NamedTemporaryFile(suffix=ext) as fh:
fh.write(content)
fh.flush()
with tempfile.NamedTemporaryFile() as fh:
fh.write(content)
fh.flush()
parsed_file = parser.ParsedFile(fh.name)
conf = {
'max_line_length': 79,
'allow_long_titles': True,
}
check = checks.CheckMaxLineLength(conf)
errors = list(check.report_iter(parsed_file))
self.assertEqual(0, len(errors))
parsed_file = parser.ParsedFile(fh.name)
check = checks.CheckMaxLineLength(conf)
errors = list(check.report_iter(parsed_file))
self.assertEqual(expected_errors, len(errors))