summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS.txt2
-rw-r--r--ChangeLog2
-rw-r--r--pylint/checkers/base.py38
-rw-r--r--pylint/test/unittest_checker_base.py11
-rw-r--r--pylintrc3
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.
diff --git a/ChangeLog b/ChangeLog
index 240bc7542..6510bfed6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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])
diff --git a/pylintrc b/pylintrc
index 79c6d3905..f5ccaff91 100644
--- a/pylintrc
+++ b/pylintrc
@@ -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]