diff --git a/parso/normalizer.py b/parso/normalizer.py index 4efbba9..4a068c0 100644 --- a/parso/normalizer.py +++ b/parso/normalizer.py @@ -44,6 +44,8 @@ class Normalizer(object): class NormalizerConfig(object): normalizer_class = Normalizer + rule_value_map = {} + rule_type_map = {} def create_normalizer(self, grammar): if self.normalizer_class is None: @@ -52,21 +54,30 @@ class NormalizerConfig(object): return self.normalizer_class(grammar, self) @classmethod - def register_rule(cls, rule): + def register_rule(cls, **kwargs): """ Use it as a class decorator: >>> normalizer = NormalizerConfig() - >>> @normalizer.register_rule + >>> @normalizer.register_rule(value='foo') ... class MyRule(Rule): ... error_code = 42 """ - try: - rules = cls.rules - except AttributeError: - rules = cls.rules = [] - rules.append(rule) - return rule + return cls._register_rule(**kwargs) + + @classmethod + def _register_rule(cls, value=None, type=None): + if value is None and type is None: + 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): @@ -93,4 +104,24 @@ class Issue(object): class Rule(object): error_code = 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) diff --git a/parso/python/errors.py b/parso/python/errors.py index fe82494..a359e51 100644 --- a/parso/python/errors.py +++ b/parso/python/errors.py @@ -4,7 +4,7 @@ import warnings import re 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 _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 self._version == (3, 5): 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' \ and self._context.is_async_funcdef(): yield_ = leaf.parent.parent @@ -783,3 +780,22 @@ class ErrorFinder(Normalizer): class ErrorFinderConfig(NormalizerConfig): 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. diff --git a/parso/python/pep8.py b/parso/python/pep8.py index 2776d6d..456ba6a 100644 --- a/parso/python/pep8.py +++ b/parso/python/pep8.py @@ -712,17 +712,10 @@ class PEP8NormalizerConfig(ErrorFinderConfig): self.spaces_before_comment = spaces_before_comment -@PEP8NormalizerConfig.register_rule -class FooRule(Rule): - pass - - -@PEP8NormalizerConfig.register_rule +@PEP8NormalizerConfig.register_rule(type='endmarker') class BlankLineAtEnd(Rule): code = 392 message = 'Blank line at end of file' - leaf_event = ['endmarker'] - def check(self, leaf): return self._newline_count >= 2