Try to add syntax errors to pep8 normalizing.

This commit is contained in:
Dave Halter
2017-07-10 23:04:54 +02:00
parent 53cf408d99
commit b3923e65e8
13 changed files with 93 additions and 49 deletions

View File

@@ -7,6 +7,7 @@ class Normalizer(object):
self.issues = [] self.issues = []
def walk(self, node): def walk(self, node):
self.initialize(node)
value = self.visit(node) value = self.visit(node)
self.finalize() self.finalize()
return value return value
@@ -27,6 +28,9 @@ class Normalizer(object):
def visit_leaf(self, leaf): def visit_leaf(self, leaf):
return leaf.prefix + leaf.value return leaf.prefix + leaf.value
def initialize(self, node):
pass
def finalize(self): def finalize(self):
pass pass

View File

@@ -1,7 +1,10 @@
from contextlib import contextmanager from contextlib import contextmanager
from parso.normalizer import Normalizer, NormalizerConfig from parso.normalizer import Normalizer, NormalizerConfig, Issue
_BLOCK_STMTS = ('if_stmt', 'while_stmt', 'for_stmt', 'try_stmt', 'with_stmt')
# This is the maximal block size given by python.
_MAX_BLOCK_SIZE = 20
class CompressNormalizer(Normalizer): class CompressNormalizer(Normalizer):
""" """
@@ -11,6 +14,23 @@ class CompressNormalizer(Normalizer):
return leaf.prefix + leaf.value return leaf.prefix + leaf.value
class Context(object):
def __init__(self, scope, parent_context=None):
self.blocks = []
@contextmanager
def add_block(self, node):
self.blocks.append(node)
yield
self.blocks.pop()
@contextmanager
def add_context(self, node):
self.blocks.append(node)
yield Context(node, parent_context=self)
self.blocks.pop()
class ErrorFinder(Normalizer): class ErrorFinder(Normalizer):
""" """
Searches for errors in the syntax tree. Searches for errors in the syntax tree.
@@ -19,37 +39,49 @@ class ErrorFinder(Normalizer):
super(ErrorFinder, self).__init__(*args, **kwargs) super(ErrorFinder, self).__init__(*args, **kwargs)
self._error_dict = {} self._error_dict = {}
def initialize(self, node):
from parso.python.tree import search_ancestor
parent_scope = search_ancestor(node, 'classdef', 'funcdef', 'file_input')
self._context = Context(parent_scope)
@contextmanager @contextmanager
def visit_node(self, node): def visit_node(self, node):
if node.type == 'error_node': if node.type == 'error_node':
leaf = node.get_next_leaf() leaf = node.get_next_leaf()
self._add_error(901, "Syntax Error", leaf) self._add_syntax_error("Syntax Error", leaf)
elif node.type in _BLOCK_STMTS:
with self._context.add_block(node):
yield
return
elif node.type in ('classdef', 'funcdef'):
context = self._context
with self._context.add_context(node) as new_context:
if len(context.blocks) == _MAX_BLOCK_SIZE:
self._add_syntax_error("Too many statically nested blocks", node)
self._context = new_context
yield
self._context = context
return
yield yield
def visit_leaf(self, leaf): def visit_leaf(self, leaf):
if leaf.type == 'error_leaf': if leaf.type == 'error_leaf':
self._add_error(901, "Syntax Error", leaf) self._add_syntax_error("Syntax Error", leaf)
return '' return ''
def _add_syntax_error(self, message, node):
self._add_error(901, message, node)
def _add_error(self, code, message, node): def _add_error(self, code, message, node):
# Check if the issues are on the same line.
line = node.start_pos[0] line = node.start_pos[0]
self._error_dict.setdefault(line, (code, message, node)) self._error_dict.setdefault(line, (code, message, node))
def finalize(self): def finalize(self):
for code, message, node in self._error_dict.values(): for code, message, node in self._error_dict.values():
self.add_issue(code, message, node) self.issues.append(Issue(node, code, message))
def add_issue(self, code, message, node):
# Check if the issues are on the same line.
prev = node.get_previous_leaf()
if prev is not None and prev.type == 'error_leaf':
# There's already an error nearby. There's a huge chance they are
# related, so don't report this one.
return
super(ErrorFinder, self).add_issue(code, message, node)
class ErrorFinderConfig(NormalizerConfig): class ErrorFinderConfig(NormalizerConfig):

View File

@@ -1,7 +1,8 @@
import re import re
from contextlib import contextmanager from contextlib import contextmanager
from parso.normalizer import Normalizer, Rule, NormalizerConfig from parso.python.normalizer import ErrorFinder, ErrorFinderConfig
from parso.normalizer import Rule
_IMPORT_TYPES = ('import_name', 'import_from') _IMPORT_TYPES = ('import_name', 'import_from')
@@ -147,7 +148,7 @@ def _is_magic_name(name):
return name.value.startswith('__') and name.value.startswith('__') return name.value.startswith('__') and name.value.startswith('__')
class PEP8Normalizer(Normalizer): class PEP8Normalizer(ErrorFinder):
def __init__(self, config): def __init__(self, config):
super(PEP8Normalizer, self).__init__(config) super(PEP8Normalizer, self).__init__(config)
self._previous_part = None self._previous_part = None
@@ -172,6 +173,10 @@ class PEP8Normalizer(Normalizer):
@contextmanager @contextmanager
def visit_node(self, node): def visit_node(self, node):
with super(PEP8Normalizer, self).visit_node(node):
return self._visit_node(node)
def _visit_node(self, node):
typ = node.type typ = node.type
if typ in 'import_name': if typ in 'import_name':
@@ -329,6 +334,7 @@ class PEP8Normalizer(Normalizer):
self._newline_count = 0 self._newline_count = 0
def visit_leaf(self, leaf): def visit_leaf(self, leaf):
super(PEP8Normalizer, self).visit_leaf(leaf)
for part in leaf._split_prefix(): for part in leaf._split_prefix():
if part.type == 'spacing': if part.type == 'spacing':
# This part is used for the part call after for. # This part is used for the part call after for.
@@ -684,7 +690,7 @@ class PEP8Normalizer(Normalizer):
super(PEP8Normalizer, self).add_issue(code, message, node) super(PEP8Normalizer, self).add_issue(code, message, node)
class PEP8NormalizerConfig(NormalizerConfig): class PEP8NormalizerConfig(ErrorFinderConfig):
normalizer_class = PEP8Normalizer normalizer_class = PEP8Normalizer
""" """
Normalizing to PEP8. Not really implemented, yet. Normalizing to PEP8. Not really implemented, yet.

View File

@@ -1,7 +1,7 @@
for a in 'abc': for a in 'abc':
for b in 'xyz': for b in 'xyz':
print a # indented with 8 spaces print(a) # indented with 8 spaces
# TODO currently not an error, because the indentation matches. #: E901:1
print(b) # indented with 1 tab print(b) # indented with 1 tab
if True: if True:
#: E101:0 #: E101:0

View File

@@ -15,6 +15,7 @@ if False:
pass pass
print print
print print
#: E901:4
print print
mimetype = 'application/x-directory' mimetype = 'application/x-directory'
#: E111:5 #: E111:5

View File

@@ -1,7 +1,7 @@
print "E121", ( abc = "E121", (
#: E121:2 #: E121:2
"dent") "dent")
print "E122", ( abc = "E122", (
#: E121:0 #: E121:0
"dent") "dent")
my_list = [ my_list = [
@@ -9,11 +9,11 @@ my_list = [
4, 5, 6, 4, 5, 6,
#: E123 #: E123
] ]
print "E124", ("visual", abc = "E124", ("visual",
"indent_two" "indent_two"
#: E124:14 #: E124:14
) )
print "E124", ("visual", abc = "E124", ("visual",
"indent_five" "indent_five"
#: E124:0 #: E124:0
) )
@@ -25,19 +25,19 @@ if (row < 0 or self.moduleCount <= row or
col < 0 or self.moduleCount <= col): col < 0 or self.moduleCount <= col):
raise Exception("%s,%s - %s" % (row, col, self.moduleCount)) raise Exception("%s,%s - %s" % (row, col, self.moduleCount))
print "E126", ( abc = "E126", (
#: E126:12 #: E126:12
"dent") "dent")
print "E126", ( abc = "E126", (
#: E126:8 #: E126:8
"dent") "dent")
print "E127", ("over-", abc = "E127", ("over-",
#: E127:18 #: E127:18
"over-indent") "over-indent")
print "E128", ("visual", abc = "E128", ("visual",
#: E128:4 #: E128:4
"hanging") "hanging")
print "E128", ("under-", abc = "E128", ("under-",
#: E128:14 #: E128:14
"under-indent") "under-indent")
@@ -63,7 +63,7 @@ rv.update(dict.fromkeys((
abricot = 3 + \ abricot = 3 + \
4 + \ 4 + \
5 + 6 5 + 6
print "hello", ( abc = "hello", (
"there", "there",
#: E126:5 #: E126:5

View File

@@ -55,32 +55,32 @@ if start[1] > end_col and not (
"indented for visual indent") "indented for visual indent")
print "OK", ("visual", abc = "OK", ("visual",
"indent") "indent")
print "Okay", ("visual", abc = "Okay", ("visual",
"indent_three" "indent_three"
) )
print "a-ok", ( abc = "a-ok", (
"there", "there",
"dude", "dude",
) )
print "hello", ( abc = "hello", (
"there", "there",
"dude") "dude")
print "hello", ( abc = "hello", (
"there", "there",
# "john", # "john",
"dude") "dude")
print "hello", ( abc = "hello", (
"there", "dude") "there", "dude")
print "hello", ( abc = "hello", (
"there", "dude", "there", "dude",
) )
@@ -196,12 +196,12 @@ if bar:
if ((foo.bar("baz") and if ((foo.bar("baz") and
foo.bar("frop") foo.bar("frop")
)): )):
print "yes" print("yes")
# also ok, but starting to look like LISP # also ok, but starting to look like LISP
if ((foo.bar("baz") and if ((foo.bar("baz") and
foo.bar("frop"))): foo.bar("frop"))):
print "yes" print("yes")
#: E129+1:4 E127+2:9 #: E129+1:4 E127+2:9
if (a == 2 or if (a == 2 or
@@ -223,7 +223,7 @@ if length > options.max_line_length:
# blub # blub
print 'l.{line}\t{pos}\t{name}\t{text}'.format( asd = 'l.{line}\t{pos}\t{name}\t{text}'.format(
line=token[2][0], line=token[2][0],
pos=pos, pos=pos,
name=tokenize.tok_name[token[0]], name=tokenize.tok_name[token[0]],

View File

@@ -83,9 +83,6 @@ help = "print total number of errors " \
help = u"print total number of errors " \ help = u"print total number of errors " \
u"to standard error" u"to standard error"
help = ur"print total number of errors " \
ur"to standard error"
help = b"print total number of errors " \ help = b"print total number of errors " \
b"to standard error" b"to standard error"
@@ -187,6 +184,8 @@ try: %s -d5
''' % sys.argv[0]) ''' % sys.argv[0])
# The try statement above was not finished.
#: E901
d = { # comment d = { # comment
1: 2 1: 2
} }
@@ -284,7 +283,7 @@ some_hash = {
} }
print dedent( abc = dedent(
''' '''
mkdir -p ./{build}/ mkdir -p ./{build}/
mv ./build/ ./{build}/%(revision)s/ mv ./build/ ./{build}/%(revision)s/

View File

@@ -23,6 +23,7 @@ if True:
or another_very_long_variable_name: or another_very_long_variable_name:
raise Exception() raise Exception()
#: E901+1:8 E901+5
dictionary = [ dictionary = [
"is": { "is": {
# Might be a E122:4, but is not because the code is invalid Python. # Might be a E122:4, but is not because the code is invalid Python.
@@ -40,7 +41,7 @@ setup('',
#: E123+2:4 E291:15 #: E123+2:4 E291:15
print "E123", ( abc = "E123", (
"bad", "hanging", "close" "bad", "hanging", "close"
) )

View File

@@ -97,7 +97,7 @@ for foo in """
123 123
""".strip().split(): """.strip().split():
print(foo) print(foo)
print dedent( abc = dedent(
''' '''
mkdir -p ./{build}/ mkdir -p ./{build}/
mv ./build/ ./{build}/%(revision)s/ mv ./build/ ./{build}/%(revision)s/

View File

@@ -35,18 +35,18 @@ result = [
#: E203:9 #: E203:9
if x == 4 : if x == 4 :
print x, y print(x, y)
x, y = y, x x, y = y, x
if x == 4: if x == 4:
#: E203:12 E702:13 #: E203:12 E702:13
a = x, y ; x, y = y, x a = x, y ; x, y = y, x
if x == 4: if x == 4:
print x, y print(x, y)
#: E203:12 #: E203:12
x, y = y , x x, y = y , x
# Okay # Okay
if x == 4: if x == 4:
print x, y print(x, y)
x, y = y, x x, y = y, x
a[b1, :] == a[b1, ...] a[b1, :] == a[b1, ...]
a[b1, :1] == 3 a[b1, :1] == 3

View File

@@ -22,6 +22,7 @@ True and False
if 1: if 1:
pass pass
# Syntax Error, no indentation # Syntax Error, no indentation
#: E901+1
if 1: if 1:
pass pass
#: E223:8 #: E223:8

View File

@@ -27,7 +27,7 @@ def _get_error_list(code, version=None):
('?', [(1, 0)]), ('?', [(1, 0)]),
('??', [(1, 0)]), ('??', [(1, 0)]),
('? ?', [(1, 0)]), ('? ?', [(1, 0)]),
('?\n?', [(1, 0)]), ('?\n?', [(1, 0), (2, 0)]),
('? * ?', [(1, 0)]), ('? * ?', [(1, 0)]),
('1 + * * 2', [(1, 4)]), ('1 + * * 2', [(1, 4)]),
('?\n1\n?', [(1, 0), (3, 0)]), ('?\n1\n?', [(1, 0), (3, 0)]),