mirror of
https://github.com/davidhalter/parso.git
synced 2025-12-06 12:54:29 +08:00
Try to implement some of the pydocstyle functionality in the normalizer function.
This commit is contained in:
@@ -1,10 +1,34 @@
|
||||
from contextlib import contextmanager
|
||||
|
||||
|
||||
class Normalizer(object):
|
||||
def __init__(self, config):
|
||||
self._config = config
|
||||
self.issues = []
|
||||
|
||||
@contextmanager
|
||||
def visit_node(self):
|
||||
yield
|
||||
|
||||
def normalize(self, leaf):
|
||||
return leaf.prefix + leaf.value
|
||||
|
||||
|
||||
class NormalizerConfig(object):
|
||||
normalizer_class = Normalizer
|
||||
|
||||
def create_normalizer(self):
|
||||
if self.normalizer_class is None:
|
||||
return None
|
||||
|
||||
return self.normalizer_class(self)
|
||||
|
||||
@classmethod
|
||||
def register_rule(cls, rule):
|
||||
"""
|
||||
Use it as a class decorator:
|
||||
|
||||
>>> normalizer = Normalizer()
|
||||
>>> normalizer = NormalizerConfig()
|
||||
>>> @normalizer.register_rule
|
||||
... class MyRule(Rule):
|
||||
... error_code = 42
|
||||
@@ -16,12 +40,6 @@ class Normalizer(object):
|
||||
rules.append(rule)
|
||||
return rule
|
||||
|
||||
def normalize(self, leaf):
|
||||
return leaf.prefix + leaf.value
|
||||
|
||||
def iter_errors(self, leaf):
|
||||
return iter([])
|
||||
|
||||
|
||||
class Error(object):
|
||||
def __init__(self, leaf, code, message):
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
from parso.normalizer import Normalizer, Error
|
||||
from contextlib import contextmanager
|
||||
from parso.normalizer import Normalizer, Rule, NormalizerConfig
|
||||
|
||||
|
||||
IMPORT_TYPES = ('import_name', 'import_from')
|
||||
|
||||
class CompressNormalizer(Normalizer):
|
||||
"""
|
||||
Removes comments and whitespace.
|
||||
@@ -9,16 +12,114 @@ class CompressNormalizer(Normalizer):
|
||||
return leaf.prefix + leaf.value
|
||||
|
||||
|
||||
class WhitespaceInfo(object):
|
||||
def __init__(self, leaf):
|
||||
parts = list(leaf._split_prefix())
|
||||
'''
|
||||
' ': 'spaces',
|
||||
'#': 'comment',
|
||||
'\\': 'backslash',
|
||||
'\f': 'formfeed',
|
||||
'\n': 'newline',
|
||||
'\r': 'newline',
|
||||
'\t': 'tabs',
|
||||
'''
|
||||
for part in parts:
|
||||
if part.type:
|
||||
part
|
||||
self.newline_count = 2
|
||||
self.indentation = ' '
|
||||
self.trailing_whitespace = []
|
||||
self.comment_whitespace = []
|
||||
|
||||
class PEP8Normalizer(Normalizer):
|
||||
def __init__(self, config):
|
||||
super(PEP8Normalizer, self).__init__(config)
|
||||
self.indentation = 0
|
||||
|
||||
@contextmanager
|
||||
def visit_node(self, state, node):
|
||||
typ = node.type
|
||||
|
||||
if typ in 'import_name':
|
||||
names = node.get_defined_names()
|
||||
if len(names) > 1:
|
||||
for name in names[:1]:
|
||||
self.log_error(401, 'Multiple imports on one line', name)
|
||||
elif typ == 'lambdef':
|
||||
if node.parent.type == 'expr_stmt':
|
||||
self.log_error(731, 'Do not assign a lambda expression, use a def', node)
|
||||
elif typ == 'try_stmt':
|
||||
for child in node.children:
|
||||
# Here we can simply check if it's an except, because otherwise
|
||||
# it would be an except_clause.
|
||||
if child == 'except':
|
||||
self.log_error(722, 'Do not use bare except, specify exception instead', node)
|
||||
elif typ == 'comparison':
|
||||
odd = False
|
||||
for child in node.children:
|
||||
if odd:
|
||||
if child not in ('is', '=='):
|
||||
break
|
||||
else:
|
||||
if child.type == 'atom_expr':
|
||||
break
|
||||
trailer = child.children[-1]
|
||||
atom = child.children[-1]
|
||||
if not (trailer.type == 'trailer' and atom.type == 'name'
|
||||
and atom.value == 'type'):
|
||||
break
|
||||
odd = not odd
|
||||
else:
|
||||
self.log_error(721, "Do not compare types, use 'isinstance()", node)
|
||||
|
||||
if typ in IMPORT_TYPES:
|
||||
module = node.parent
|
||||
if module.type == 'file_input':
|
||||
index = module.children.index(node)
|
||||
for child in module.children[:index]:
|
||||
if child.type not in IMPORT_TYPES:
|
||||
self.log_error(402, 'Module level import not at top of file', node)
|
||||
break
|
||||
|
||||
if typ == 'suite':
|
||||
self.indentation += 1
|
||||
yield
|
||||
if typ == 'suite':
|
||||
self.indentation -= 1
|
||||
|
||||
def normalize(self, leaf):
|
||||
typ = leaf.type
|
||||
if typ == 'name' and leaf.value in ('l', 'O', 'I'):
|
||||
if leaf.is_definition():
|
||||
message = "Do not define %s named 'l', 'O', or 'I' one line"
|
||||
if leaf.parent.type == 'class' and leaf.parent.name == leaf:
|
||||
self.log_error(742, message % 'classes', leaf)
|
||||
elif leaf.parent.type == 'function' and leaf.parent.name == leaf:
|
||||
self.log_error(743, message % 'function', leaf)
|
||||
else:
|
||||
self.log_error(741, message % 'variables', leaf)
|
||||
|
||||
for part in leaf._split_prefix():
|
||||
part
|
||||
return leaf.value
|
||||
|
||||
|
||||
class PEP8NormalizerConfig(NormalizerConfig):
|
||||
normalizer_class = PEP8Normalizer
|
||||
"""
|
||||
Normalizing to PEP8. Not really implemented, yet.
|
||||
"""
|
||||
def normalize(self, leaf):
|
||||
return leaf.value
|
||||
|
||||
def iter_errors(self, leaf):
|
||||
return iter([])
|
||||
|
||||
|
||||
class Rule():
|
||||
@PEP8NormalizerConfig.register_rule
|
||||
class FooRule(Rule):
|
||||
pass
|
||||
|
||||
|
||||
@PEP8NormalizerConfig.register_rule
|
||||
class BlankLineAtEnd(Rule):
|
||||
code = 'W391'
|
||||
message = 'blank line at end of file'
|
||||
|
||||
leaf_event = ['endmarker']
|
||||
|
||||
@@ -65,7 +65,7 @@ class PythonMixin(object):
|
||||
Some Python specific utitilies.
|
||||
"""
|
||||
__slots__ = ()
|
||||
default_normalizer = normalizer.PEP8Normalizer()
|
||||
default_normalizer_config = normalizer.PEP8NormalizerConfig()
|
||||
|
||||
def get_definition(self):
|
||||
if self.type in ('newline', 'endmarker'):
|
||||
|
||||
@@ -22,7 +22,7 @@ class NodeOrLeaf(object):
|
||||
The base class for nodes and leaves.
|
||||
"""
|
||||
__slots__ = ()
|
||||
default_normalizer = None
|
||||
default_normalizer_config = None
|
||||
|
||||
def get_root_node(self):
|
||||
"""
|
||||
@@ -154,22 +154,34 @@ class NodeOrLeaf(object):
|
||||
e.g. a statement.
|
||||
"""
|
||||
|
||||
def normalize(self, normalizer=None):
|
||||
def _get_normalizer(self, normalizer_config):
|
||||
if normalizer_config is None:
|
||||
normalizer_config = self.default_normalizer_config
|
||||
if normalizer_config is None:
|
||||
raise ValueError("You need to specify a normalizer, because "
|
||||
"there's no default normalizer for this tree.")
|
||||
return normalizer_config.create_normalizer()
|
||||
|
||||
def normalize(self, normalizer_config=None):
|
||||
"""
|
||||
The returned code will be normalized, e.g. PEP8 for Python.
|
||||
"""
|
||||
if normalizer is None:
|
||||
normalizer = self.default_normalizer
|
||||
if normalizer is None:
|
||||
raise ValueError("You need to specify a normalizer, because "
|
||||
"there's no default normalizer for this tree.")
|
||||
normalizer = self._get_normalizer(normalizer_config)
|
||||
return self._normalize(normalizer)
|
||||
|
||||
def _normalize(self, normalizer):
|
||||
try:
|
||||
children = self.children
|
||||
except AttributeError:
|
||||
return normalizer.normalize(self)
|
||||
else:
|
||||
return ''.join(child.normalize(normalizer) for child in children)
|
||||
with normalizer.visit_node(self):
|
||||
return ''.join(child._normalize(normalizer) for child in children)
|
||||
|
||||
def _get_normalize_errors(self, normalizer_config=None):
|
||||
normalizer = self._get_normalizer(normalizer_config)
|
||||
self._normalize(normalizer)
|
||||
return normalizer.issues
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user