diff --git a/parso/normalizer.py b/parso/normalizer.py index 509cf6d..9607749 100644 --- a/parso/normalizer.py +++ b/parso/normalizer.py @@ -1,4 +1,5 @@ from contextlib import contextmanager +from functools import total_ordering class Normalizer(object): @@ -15,7 +16,8 @@ class Normalizer(object): def add_issue(self, code, message, node): issue = Issue(node, code, message) - self.issues.append(issue) + if issue not in self.issues: + self.issues.append(issue) return True @@ -53,6 +55,16 @@ class Issue(object): self.message = message self.start_pos = node.start_pos + def __eq__(self, other): + return self.start_pos == other.start_pos and self.code == other.code + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return hash((self.code, self.start_pos)) + + class Rule(object): error_code = None diff --git a/parso/python/normalizer.py b/parso/python/normalizer.py index bf8971b..c09c4c3 100644 --- a/parso/python/normalizer.py +++ b/parso/python/normalizer.py @@ -141,9 +141,11 @@ class PEP8Normalizer(Normalizer): def __init__(self, config): super(PEP8Normalizer, self).__init__(config) self._previous_leaf = None + self._actual_previous_leaf = None self._on_newline = True self._newline_count = 0 self._wanted_newline_count = None + self._max_new_lines_in_prefix = 0 self._new_statement = True self._implicit_indentation_possible = False # The top of stack of the indentation nodes. @@ -264,6 +266,9 @@ class PEP8Normalizer(Normalizer): return int(suite_node.parent is None) + 1 def _reset_newlines(self, spacing, actual_leaf, is_comment=False): + self._max_new_lines_in_prefix = \ + max(self._max_new_lines_in_prefix, self._newline_count) + wanted = self._wanted_newline_count if wanted is not None: # Need to substract one @@ -280,6 +285,27 @@ class PEP8Normalizer(Normalizer): else: self._wanted_newline_count = None + if not is_comment: + wanted = self._get_wanted_blank_lines_count() + actual = self._max_new_lines_in_prefix - 1 + + val = actual_leaf.value + needs_lines = ( + val == '@' and actual_leaf.parent.type == 'decorator' + or val == 'class' + or val == 'async' and actual_leaf.get_next_leaf() == 'def' + or val == 'def' and self._actual_previous_leaf != 'async' + ) + if needs_lines and actual < wanted: + # The first leaf should not be added. + if self._actual_previous_leaf is not None: + code = 302 if wanted == 2 else 301 + message = "expected %s blank line, found %s" \ + % (wanted, actual) + self.add_issue(code, message, spacing) + + self._max_new_lines_in_prefix = 0 + self._newline_count = 0 def normalize(self, leaf): @@ -306,6 +332,9 @@ class PEP8Normalizer(Normalizer): if not self._new_statement: self._reset_newlines(part, leaf) + self._max_blank_lines = 0 + + self._actual_previous_leaf = leaf return x diff --git a/test/normalizer_issue_files/E30.py b/test/normalizer_issue_files/E30.py index 0bb8cd6..2383a32 100644 --- a/test/normalizer_issue_files/E30.py +++ b/test/normalizer_issue_files/E30.py @@ -146,14 +146,14 @@ if a(): a() -#: E301+3:4 +#: E301+2 def a(): x = 1 def b(): pass -#: E301+3:4 E301+5:8 +#: E301+2 E301+4 def a(): x = 2 def b(): @@ -162,7 +162,7 @@ def a(): pass -#: E301+4 E301+6 +#: E301+2 E301+4 E301+5 def a(): x = 1 class C: