diff options
author | Torsten Marek <shlomme@gmail.com> | 2014-03-30 15:54:13 -0700 |
---|---|---|
committer | Torsten Marek <shlomme@gmail.com> | 2014-03-30 15:54:13 -0700 |
commit | 96d7c29483500c90882f6d69e3328a5926183c47 (patch) | |
tree | 65f0633638483694ba2379296a4a5a19257ce729 | |
parent | 4d9e83a9c8e04383da565bfa188c583e193750c1 (diff) | |
download | pylint-96d7c29483500c90882f6d69e3328a5926183c47.tar.gz |
Make it possible to show a naming hint for invalid name by setting include-naming-hint. Also make the naming hints configurable.
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | checkers/base.py | 110 | ||||
-rw-r--r-- | doc/options.rst | 11 | ||||
-rw-r--r-- | test/test_base.py | 33 |
4 files changed, 84 insertions, 76 deletions
@@ -10,10 +10,14 @@ ChangeLog for Pylint * Add new warning 'eval-used', checking that the builtin function `eval` was used. + * Make it possible to show a naming hint for invalid name by setting + include-naming-hint. Also make the naming hints configurable. Fixes + BitBucket issue #138. + * Added support for enforcing multiple, but consistent name styles for different name types inside a single module; based on a patch written by morbo@google.com. - + * Also warn about empty docstrings on overridden methods; contributed by sebastianu@google.com. diff --git a/checkers/base.py b/checkers/base.py index 279d5b1..497aa40 100644 --- a/checkers/base.py +++ b/checkers/base.py @@ -822,79 +822,47 @@ functions, methods # everything else is not a proper sequence for reversed() self.add_message('bad-reversed-sequence', node=node) +_NAME_TYPES = { + 'module': (MOD_NAME_RGX, 'module'), + 'const': (CONST_NAME_RGX, 'constant'), + 'class': (CLASS_NAME_RGX, 'class'), + 'function': (DEFAULT_NAME_RGX, 'function'), + 'method': (DEFAULT_NAME_RGX, 'method'), + 'attr': (DEFAULT_NAME_RGX, 'attribute'), + 'argument': (DEFAULT_NAME_RGX, 'argument'), + 'variable': (DEFAULT_NAME_RGX, 'variable'), + 'class_attribute': (CLASS_ATTRIBUTE_RGX, 'class attribute'), + 'inlinevar': (COMP_VAR_RGX, 'inline iteration'), +} + +def _create_naming_options(): + name_options = [] + for name_type, (rgx, human_readable_name) in _NAME_TYPES.iteritems(): + name_type = name_type.replace('_', '-') + name_options.append(( + '%s-rgx' % (name_type,), + {'default': rgx, 'type': 'regexp', 'metavar': '<regexp>', + 'help': 'Regular expression matching correct %s names' % (human_readable_name,)})) + name_options.append(( + '%s-name-hint' % (name_type,), + {'default': rgx.pattern, 'type': 'string', 'metavar': '<string>', + 'help': 'Naming hint for %s names' % (human_readable_name,)})) + + return tuple(name_options) + class NameChecker(_BasicChecker): msgs = { 'C0102': ('Black listed name "%s"', 'blacklisted-name', 'Used when the name is listed in the black list (unauthorized \ names).'), - 'C0103': ('Invalid %s name "%s"', + 'C0103': ('Invalid %s name "%s"%s', 'invalid-name', 'Used when the name doesn\'t match the regular expression \ associated to its type (constant, variable, class...).'), } - options = (('module-rgx', - {'default' : MOD_NAME_RGX, - 'type' :'regexp', 'metavar' : '<regexp>', - 'help' : 'Regular expression which should only match correct ' - 'module names'} - ), - ('const-rgx', - {'default' : CONST_NAME_RGX, - 'type' :'regexp', 'metavar' : '<regexp>', - 'help' : 'Regular expression which should only match correct ' - 'module level names'} - ), - ('class-rgx', - {'default' : CLASS_NAME_RGX, - 'type' :'regexp', 'metavar' : '<regexp>', - 'help' : 'Regular expression which should only match correct ' - 'class names'} - ), - ('function-rgx', - {'default' : DEFAULT_NAME_RGX, - 'type' :'regexp', 'metavar' : '<regexp>', - 'help' : 'Regular expression which should only match correct ' - 'function names'} - ), - ('method-rgx', - {'default' : DEFAULT_NAME_RGX, - 'type' :'regexp', 'metavar' : '<regexp>', - 'help' : 'Regular expression which should only match correct ' - 'method names'} - ), - ('attr-rgx', - {'default' : DEFAULT_NAME_RGX, - 'type' :'regexp', 'metavar' : '<regexp>', - 'help' : 'Regular expression which should only match correct ' - 'instance attribute names'} - ), - ('argument-rgx', - {'default' : DEFAULT_NAME_RGX, - 'type' :'regexp', 'metavar' : '<regexp>', - 'help' : 'Regular expression which should only match correct ' - 'argument names'}), - ('variable-rgx', - {'default' : DEFAULT_NAME_RGX, - 'type' :'regexp', 'metavar' : '<regexp>', - 'help' : 'Regular expression which should only match correct ' - 'variable names'} - ), - ('class-attribute-rgx', - {'default' : CLASS_ATTRIBUTE_RGX, - 'type' :'regexp', 'metavar' : '<regexp>', - 'help' : 'Regular expression which should only match correct ' - 'attribute names in class bodies'} - ), - ('inlinevar-rgx', - {'default' : COMP_VAR_RGX, - 'type' :'regexp', 'metavar' : '<regexp>', - 'help' : 'Regular expression which should only match correct ' - 'list comprehension / generator expression variable \ - names'} - ), - # XXX use set + options = (# XXX use set ('good-names', {'default' : ('i', 'j', 'k', 'ex', 'Run', '_'), 'type' :'csv', 'metavar' : '<names>', @@ -914,7 +882,12 @@ class NameChecker(_BasicChecker): ' other\'s naming style when the name regexes' ' allow several styles.')} ), - ) + ('include-naming-hint', + {'default': False, 'type' : 'yn', 'metavar' : '<y_or_n>', + 'help': 'Include a hint for the correct naming format with invalid-name'} + ), + ) + _create_naming_options() + def __init__(self, linter): _BasicChecker.__init__(self, linter) @@ -1025,12 +998,11 @@ class NameChecker(_BasicChecker): match = None if match is None: - type_label = {'inlinedvar': 'inlined variable', - 'const': 'constant', - 'attr': 'attribute', - 'class_attribute': 'class attribute' - }.get(node_type, node_type) - self.add_message('invalid-name', node=node, args=(type_label, name)) + type_label = _NAME_TYPES[node_type][1] + hint = '' + if self.config.include_naming_hint: + hint = ' (hint: %s)' % (getattr(self.config, node_type + '_name_hint')) + self.add_message('invalid-name', node=node, args=(type_label, name, hint)) self.stats['badname_' + node_type] += 1 diff --git a/doc/options.rst b/doc/options.rst index ec17fb0..4a159f2 100644 --- a/doc/options.rst +++ b/doc/options.rst @@ -126,3 +126,14 @@ prevent built-in or interface-dictated names to trigger certain naming styles. Format: comma-separated groups of colon-separated names. This option can be used to combine name styles. For example, ``function:method`` enforces that functions and methods use the same style, and a style triggered by either name type carries over to the other. This requires that the regular expression for the combined name types use the same group names. + +Name Hints +^^^^^^^^^^ + +.. option:: --include-naming-hint=y|n + + Default: off + + Include a hint for the correct name format with every ``invalid-name`` warning. + + Name hints default to the regular expression, but can be separately configured with the ``--<name-type>-hint`` options. diff --git a/test/test_base.py b/test/test_base.py index 617366c..972c783 100644 --- a/test/test_base.py +++ b/test/test_base.py @@ -57,6 +57,27 @@ class NameCheckerTest(CheckerTestCase): 'bad_names': set(), } + @set_config(include_naming_hint=True) + def test_naming_hint(self): + const = test_utils.extract_node(""" + const = "CONSTANT" #@ + """) + with self.assertAddsMessages( + Message('invalid-name', node=const.targets[0], + args=('constant', 'const', ' (hint: (([A-Z_][A-Z0-9_]*)|(__.*__))$)'))): + self.checker.visit_assname(const.targets[0]) + + @set_config(include_naming_hint=True, + const_name_hint='CONSTANT') + def test_naming_hint_configured_hint(self): + const = test_utils.extract_node(""" + const = "CONSTANT" #@ + """) + with self.assertAddsMessages( + Message('invalid-name', node=const.targets[0], + args=('constant', 'const', ' (hint: CONSTANT)'))): + self.checker.visit_assname(const.targets[0]) + @set_config(attr_rgx=re.compile('[A-Z]+')) def test_property_names(self): # If a method is annotated with @property, it's name should @@ -82,7 +103,7 @@ class NameCheckerTest(CheckerTestCase): self.checker.visit_function(methods[0]) self.checker.visit_function(methods[2]) with self.assertAddsMessages(Message('invalid-name', node=methods[1], - args=('attribute', 'bar'))): + args=('attribute', 'bar', ''))): self.checker.visit_function(methods[1]) @set_config(attr_rgx=re.compile('[A-Z]+')) @@ -145,7 +166,7 @@ class MultiNamingStyleTest(CheckerTestCase): class CLASSC(object): #@ pass """) - with self.assertAddsMessages(Message('invalid-name', node=classes[1], args=('class', 'classb'))): + with self.assertAddsMessages(Message('invalid-name', node=classes[1], args=('class', 'classb', ''))): for cls in classes: self.checker.visit_class(cls) @@ -159,8 +180,8 @@ class MultiNamingStyleTest(CheckerTestCase): class CLASSC(object): #@ pass """) - with self.assertAddsMessages(Message('invalid-name', node=classes[0], args=('class', 'class_a')), - Message('invalid-name', node=classes[2], args=('class', 'CLASSC'))): + with self.assertAddsMessages(Message('invalid-name', node=classes[0], args=('class', 'class_a', '')), + Message('invalid-name', node=classes[2], args=('class', 'CLASSC', ''))): for cls in classes: self.checker.visit_class(cls) @@ -176,7 +197,7 @@ class MultiNamingStyleTest(CheckerTestCase): def FUNC(): #@ pass """, module_name='test') - with self.assertAddsMessages(Message('invalid-name', node=function_defs[1], args=('function', 'FUNC'))): + with self.assertAddsMessages(Message('invalid-name', node=function_defs[1], args=('function', 'FUNC', ''))): for func in function_defs: self.checker.visit_function(func) @@ -192,7 +213,7 @@ class MultiNamingStyleTest(CheckerTestCase): def UPPER(): #@ pass """) - with self.assertAddsMessages(Message('invalid-name', node=function_defs[3], args=('function', 'UPPER'))): + with self.assertAddsMessages(Message('invalid-name', node=function_defs[3], args=('function', 'UPPER', ''))): for func in function_defs: self.checker.visit_function(func) |