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): class CheckValidity(ContentCheck):
REPORTS = frozenset(["D000"]) REPORTS = frozenset(["D000"])
EXT_MATCHER = re.compile(r"(.*)[.]rst", re.I)
# Only used when running in sphinx mode. # Only used when running in sphinx mode.
SPHINX_IGNORES_REGEX = [ SPHINX_IGNORES_REGEX = [
@ -107,6 +108,11 @@ class CheckValidity(ContentCheck):
class CheckMaxLineLength(ContentCheck): class CheckMaxLineLength(ContentCheck):
REPORTS = frozenset(["D001"]) 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_node_lines(self, doc):
def extract_lines(node, start_line): def extract_lines(node, start_line):
@ -185,10 +191,14 @@ class CheckMaxLineLength(ContentCheck):
directives.append((i, find_directive_end(i, lines))) directives.append((i, find_directive_end(i, lines)))
return directives return directives
def report_iter(self, parsed_file): def _txt_checker(self, parsed_file):
doc = parsed_file.document for i, line in enumerate(parsed_file.lines_iter()):
lines = list(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) nodes_lines, first_line = self._extract_node_lines(doc)
directives = self._extract_directives(lines) directives = self._extract_directives(lines)
@ -225,10 +235,8 @@ class CheckMaxLineLength(ContentCheck):
docutils_nodes.subtitle, docutils_nodes.subtitle,
docutils_nodes.section, 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): for i, line in enumerate(lines):
if len(line) > max_line_length: if len(line) > self._max_line_length:
in_directive = False in_directive = False
for (start, end) in directives: for (start, end) in directives:
if i >= start and i <= end: if i >= start and i <= end:
@ -245,6 +253,14 @@ class CheckMaxLineLength(ContentCheck):
nodes = find_containing_nodes(i + 1) nodes = find_containing_nodes(i + 1)
if any_types(nodes, skip_types): if any_types(nodes, skip_types):
continue continue
if allow_long_titles and any_types(nodes, title_types): if self._allow_long_titles and any_types(nodes, title_types):
continue continue
yield (i + 1, 'D001', 'Line too long') 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__, check_name = ".".join([c.__class__.__module__,
c.__class__.__name__]) c.__class__.__name__])
error_counts.setdefault(check_name, 0) 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: try:
reports = set(c.REPORTS) reports = set(c.REPORTS)
except AttributeError: except AttributeError:

View File

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

View File

@ -73,20 +73,21 @@ test
content += "\n\n" content += "\n\n"
content += ("a" * 60) + " " + ("b" * 60) content += ("a" * 60) + " " + ("b" * 60)
content += "\n" 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: parsed_file = parser.ParsedFile(fh.name)
fh.write(content) check = checks.CheckMaxLineLength(conf)
fh.flush() errors = list(check.report_iter(parsed_file))
parsed_file = parser.ParsedFile(fh.name) self.assertEqual(1, len(errors))
conf = { (line, code, msg) = errors[0]
'max_line_length': 79, self.assertIn(code, check.REPORTS)
'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)
def test_unsplittable_length(self): def test_unsplittable_length(self):
content = """ content = """
@ -102,15 +103,20 @@ test
content += "\n\n" content += "\n\n"
content += "a" * 100 content += "a" * 100
content += "\n" 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: parsed_file = parser.ParsedFile(fh.name)
fh.write(content) check = checks.CheckMaxLineLength(conf)
fh.flush() errors = list(check.report_iter(parsed_file))
parsed_file = parser.ParsedFile(fh.name) self.assertEqual(expected_errors, len(errors))
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))