mirror of
https://github.com/davidhalter/parso.git
synced 2025-12-07 05:14: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):
|
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
|
@classmethod
|
||||||
def register_rule(cls, rule):
|
def register_rule(cls, rule):
|
||||||
"""
|
"""
|
||||||
Use it as a class decorator:
|
Use it as a class decorator:
|
||||||
|
|
||||||
>>> normalizer = Normalizer()
|
>>> normalizer = NormalizerConfig()
|
||||||
>>> @normalizer.register_rule
|
>>> @normalizer.register_rule
|
||||||
... class MyRule(Rule):
|
... class MyRule(Rule):
|
||||||
... error_code = 42
|
... error_code = 42
|
||||||
@@ -16,12 +40,6 @@ class Normalizer(object):
|
|||||||
rules.append(rule)
|
rules.append(rule)
|
||||||
return rule
|
return rule
|
||||||
|
|
||||||
def normalize(self, leaf):
|
|
||||||
return leaf.prefix + leaf.value
|
|
||||||
|
|
||||||
def iter_errors(self, leaf):
|
|
||||||
return iter([])
|
|
||||||
|
|
||||||
|
|
||||||
class Error(object):
|
class Error(object):
|
||||||
def __init__(self, leaf, code, message):
|
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):
|
class CompressNormalizer(Normalizer):
|
||||||
"""
|
"""
|
||||||
Removes comments and whitespace.
|
Removes comments and whitespace.
|
||||||
@@ -9,16 +12,114 @@ class CompressNormalizer(Normalizer):
|
|||||||
return leaf.prefix + leaf.value
|
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):
|
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.
|
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
|
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.
|
Some Python specific utitilies.
|
||||||
"""
|
"""
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
default_normalizer = normalizer.PEP8Normalizer()
|
default_normalizer_config = normalizer.PEP8NormalizerConfig()
|
||||||
|
|
||||||
def get_definition(self):
|
def get_definition(self):
|
||||||
if self.type in ('newline', 'endmarker'):
|
if self.type in ('newline', 'endmarker'):
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class NodeOrLeaf(object):
|
|||||||
The base class for nodes and leaves.
|
The base class for nodes and leaves.
|
||||||
"""
|
"""
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
default_normalizer = None
|
default_normalizer_config = None
|
||||||
|
|
||||||
def get_root_node(self):
|
def get_root_node(self):
|
||||||
"""
|
"""
|
||||||
@@ -154,22 +154,34 @@ class NodeOrLeaf(object):
|
|||||||
e.g. a statement.
|
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.
|
The returned code will be normalized, e.g. PEP8 for Python.
|
||||||
"""
|
"""
|
||||||
if normalizer is None:
|
normalizer = self._get_normalizer(normalizer_config)
|
||||||
normalizer = self.default_normalizer
|
return self._normalize(normalizer)
|
||||||
if normalizer is None:
|
|
||||||
raise ValueError("You need to specify a normalizer, because "
|
|
||||||
"there's no default normalizer for this tree.")
|
|
||||||
|
|
||||||
|
def _normalize(self, normalizer):
|
||||||
try:
|
try:
|
||||||
children = self.children
|
children = self.children
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return normalizer.normalize(self)
|
return normalizer.normalize(self)
|
||||||
else:
|
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