summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorsten Marek <shlomme@gmail.com>2014-03-30 15:54:13 -0700
committerTorsten Marek <shlomme@gmail.com>2014-03-30 15:54:13 -0700
commit96d7c29483500c90882f6d69e3328a5926183c47 (patch)
tree65f0633638483694ba2379296a4a5a19257ce729
parent4d9e83a9c8e04383da565bfa188c583e193750c1 (diff)
downloadpylint-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--ChangeLog6
-rw-r--r--checkers/base.py110
-rw-r--r--doc/options.rst11
-rw-r--r--test/test_base.py33
4 files changed, 84 insertions, 76 deletions
diff --git a/ChangeLog b/ChangeLog
index e7b416d..e51aa7f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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)