diff options
-rw-r--r-- | CONTRIBUTORS.txt | 2 | ||||
-rw-r--r-- | ChangeLog | 2 | ||||
-rw-r--r-- | pylint/checkers/base.py | 38 | ||||
-rw-r--r-- | pylint/test/unittest_checker_base.py | 11 | ||||
-rw-r--r-- | pylintrc | 3 |
5 files changed, 49 insertions, 7 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index d2fc460b9..5811be9bd 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -96,3 +96,5 @@ Order doesn't matter (not that much, at least ;) Refactory wrong-import-position to skip try-import and nested cases. * Luis Escobar (Vauxoo), Moisés López (Vauxoo): Add bad-docstring-quotes and docstring-first-line-empty + +* Yannick Brehon: contributor. @@ -3,6 +3,8 @@ ChangeLog for Pylint -- + * Made the list of property-defining decorators configurable. + * Fix a false positive for keyword variadics with regard to keyword only arguments. If a keyword only argument was necessary for a function, but that function was called diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index 473ef6e1d..e77a3648a 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -142,17 +142,34 @@ def _is_multi_naming_match(match, node_type, confidence): if sys.version_info < (3, 0): - PROPERTY_CLASSES = set(('__builtin__.property', 'abc.abstractproperty')) + BUILTIN_PROPERTY = '__builtin__.property' else: - PROPERTY_CLASSES = set(('builtins.property', 'abc.abstractproperty')) + BUILTIN_PROPERTY = 'builtins.property' -def _determine_function_name_type(node): +def _get_properties(config): + """Returns a tuple of property classes and names. + + Property classes are fully qualified, such as 'abc.abstractproperty' and + property names are the actual names, such as 'abstract_property'. + """ + property_classes = set((BUILTIN_PROPERTY,)) + property_names = set() # Not returning 'property', it has its own check. + if config is not None: + property_classes.update(config.property_classes) + property_names.update((prop.rsplit('.', 1)[-1] + for prop in config.property_classes)) + return property_classes, property_names + + +def _determine_function_name_type(node, config=None): """Determine the name type whose regex the a function's name should match. :param node: A function node. + :param config: Configuration from which to pull additional property classes. :returns: One of ('function', 'method', 'attr') """ + property_classes, property_names = _get_properties(config) if not node.is_method(): return 'function' if node.decorators: @@ -164,9 +181,9 @@ def _determine_function_name_type(node): # or @abc.abstractproperty), the name type is 'attr'. if (isinstance(decorator, astroid.Name) or (isinstance(decorator, astroid.Attribute) and - decorator.attrname == 'abstractproperty')): + decorator.attrname in property_names)): infered = safe_infer(decorator) - if infered and infered.qname() in PROPERTY_CLASSES: + if infered and infered.qname() in property_classes: return 'attr' # If the function is decorated using the prop_method.{setter,getter} # form, treat it like an attribute as well. @@ -1154,6 +1171,14 @@ class NameChecker(_BasicChecker): {'default': False, 'type' : 'yn', 'metavar' : '<y_or_n>', 'help': 'Include a hint for the correct naming format with invalid-name'} ), + ('property-classes', + {'default': ('abc.abstractproperty',), + 'type': 'csv', + 'metavar': '<decorator names>', + 'help': 'List of decorators that produce properties, such as ' + 'abc.abstractproperty. Add to this list to register ' + 'other decorators that produce valid properties.'} + ), ) + _create_naming_options() @@ -1217,7 +1242,8 @@ class NameChecker(_BasicChecker): confidence = (INFERENCE if has_known_bases(node.parent.frame()) else INFERENCE_FAILURE) - self._check_name(_determine_function_name_type(node), + self._check_name(_determine_function_name_type(node, + config=self.config), node.name, node, confidence) # Check argument names args = node.args.args diff --git a/pylint/test/unittest_checker_base.py b/pylint/test/unittest_checker_base.py index 2619a7b90..e5b72722f 100644 --- a/pylint/test/unittest_checker_base.py +++ b/pylint/test/unittest_checker_base.py @@ -93,7 +93,8 @@ class NameCheckerTest(CheckerTestCase): args=('constant', 'const', ' (hint: CONSTANT)'))): self.checker.visit_assignname(const.targets[0]) - @set_config(attr_rgx=re.compile('[A-Z]+')) + @set_config(attr_rgx=re.compile('[A-Z]+'), + property_classes=('abc.abstractproperty', '.custom_prop')) def test_property_names(self): # If a method is annotated with @property, it's name should # match the attr regex. Since by default the attribute regex is the same @@ -101,6 +102,9 @@ class NameCheckerTest(CheckerTestCase): methods = test_utils.extract_node(""" import abc + def custom_prop(f): + return property(f) + class FooClass(object): @property def FOO(self): #@ @@ -113,10 +117,15 @@ class NameCheckerTest(CheckerTestCase): @abc.abstractproperty def BAZ(self): #@ pass + + @custom_prop + def QUX(self): #@ + pass """) with self.assertNoMessages(): self.checker.visit_functiondef(methods[0]) self.checker.visit_functiondef(methods[2]) + self.checker.visit_functiondef(methods[3]) with self.assertAddsMessages(Message('invalid-name', node=methods[1], args=('attribute', 'bar', ''))): self.checker.visit_functiondef(methods[1]) @@ -252,6 +252,9 @@ no-docstring-rgx=__.*__ # ones are exempt. docstring-min-length=-1 +# List of decorators that define properties, such as abc.abstractproperty. +property-classes=abc.abstractproperty + [TYPECHECK] |