Refactor the Rule class a bit.

This commit is contained in:
Dave Halter
2017-08-16 18:17:51 +02:00
parent 51f2de28c6
commit 412da07893
3 changed files with 61 additions and 21 deletions

View File

@@ -44,6 +44,8 @@ class Normalizer(object):
class NormalizerConfig(object): class NormalizerConfig(object):
normalizer_class = Normalizer normalizer_class = Normalizer
rule_value_map = {}
rule_type_map = {}
def create_normalizer(self, grammar): def create_normalizer(self, grammar):
if self.normalizer_class is None: if self.normalizer_class is None:
@@ -52,21 +54,30 @@ class NormalizerConfig(object):
return self.normalizer_class(grammar, self) return self.normalizer_class(grammar, self)
@classmethod @classmethod
def register_rule(cls, rule): def register_rule(cls, **kwargs):
""" """
Use it as a class decorator: Use it as a class decorator:
>>> normalizer = NormalizerConfig() >>> normalizer = NormalizerConfig()
>>> @normalizer.register_rule >>> @normalizer.register_rule(value='foo')
... class MyRule(Rule): ... class MyRule(Rule):
... error_code = 42 ... error_code = 42
""" """
try: return cls._register_rule(**kwargs)
rules = cls.rules
except AttributeError: @classmethod
rules = cls.rules = [] def _register_rule(cls, value=None, type=None):
rules.append(rule) if value is None and type is None:
return rule raise ValueError("You must register at least something.")
def decorator(func):
if value is not None:
cls.rule_value_map[value] = func
if type is not None:
cls.rule_type_map[type] = func
return func
return decorator
class Issue(object): class Issue(object):
@@ -93,4 +104,24 @@ class Issue(object):
class Rule(object): class Rule(object):
error_code = None error_code = None
message = None message = None
type = None
def check(self, node):
raise NotImplementedError()
def get_error_node(self, node):
return node
def add_error(self, error_code=None, message=None):
if error_code is None:
error_code = self.error_code
if error_code is None:
raise ValueError("The error code on the class is not set.")
if message is None:
message = self.message
if message is None:
raise ValueError("The message on the class is not set.")
def feed_node(self, node):
if self.check(node):
error_node = self.get_error_node(node)

View File

@@ -4,7 +4,7 @@ import warnings
import re import re
from contextlib import contextmanager from contextlib import contextmanager
from parso.normalizer import Normalizer, NormalizerConfig, Issue from parso.normalizer import Normalizer, NormalizerConfig, Issue, Rule
from parso.python.tree import search_ancestor from parso.python.tree import search_ancestor
_BLOCK_STMTS = ('if_stmt', 'while_stmt', 'for_stmt', 'try_stmt', 'with_stmt') _BLOCK_STMTS = ('if_stmt', 'while_stmt', 'for_stmt', 'try_stmt', 'with_stmt')
@@ -671,9 +671,6 @@ class ErrorFinder(Normalizer):
and leaf.get_next_leaf() != 'from' \ and leaf.get_next_leaf() != 'from' \
and self._version == (3, 5): and self._version == (3, 5):
self._add_syntax_error("'yield' inside async function", leaf.parent) self._add_syntax_error("'yield' inside async function", leaf.parent)
elif leaf.value == 'await':
if not self._context.is_async_funcdef():
self._add_syntax_error("'await' outside async function", leaf.parent)
elif leaf.value == 'from' and leaf.parent.type == 'yield_arg' \ elif leaf.value == 'from' and leaf.parent.type == 'yield_arg' \
and self._context.is_async_funcdef(): and self._context.is_async_funcdef():
yield_ = leaf.parent.parent yield_ = leaf.parent.parent
@@ -783,3 +780,22 @@ class ErrorFinder(Normalizer):
class ErrorFinderConfig(NormalizerConfig): class ErrorFinderConfig(NormalizerConfig):
normalizer_class = ErrorFinder normalizer_class = ErrorFinder
class SyntaxRule(Rule):
code = 901
class IndentationRule(Rule):
code = 903
@ErrorFinderConfig.register_rule(value='await')
class AwaitOutsideAsync(SyntaxRule):
message = "'await' outside async function"
def check_leaf_value(self, leaf):
return not self._context.is_async_funcdef()
def get_error_node(self, node):
return node.parent # Return the whole await statement.

View File

@@ -712,17 +712,10 @@ class PEP8NormalizerConfig(ErrorFinderConfig):
self.spaces_before_comment = spaces_before_comment self.spaces_before_comment = spaces_before_comment
@PEP8NormalizerConfig.register_rule @PEP8NormalizerConfig.register_rule(type='endmarker')
class FooRule(Rule):
pass
@PEP8NormalizerConfig.register_rule
class BlankLineAtEnd(Rule): class BlankLineAtEnd(Rule):
code = 392 code = 392
message = 'Blank line at end of file' message = 'Blank line at end of file'
leaf_event = ['endmarker']
def check(self, leaf): def check(self, leaf):
return self._newline_count >= 2 return self._newline_count >= 2