summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcpopa <devnull@localhost>2014-07-01 13:35:52 +0300
committercpopa <devnull@localhost>2014-07-01 13:35:52 +0300
commit7f71cbb5cc39d981a544c6a1af50521a132ba44d (patch)
tree6dec69182bcedb55d0b5d952ebc005eaf070557e
parentf607ae828edb94f36732ca2797c5b010e1a49fa4 (diff)
downloadpylint-7f71cbb5cc39d981a544c6a1af50521a132ba44d.tar.gz
Emit attribute-defined-outside-init for all cases, not just for the last assignment. Closes issue #262.
-rw-r--r--ChangeLog3
-rw-r--r--checkers/classes.py44
-rw-r--r--test/input/func_defining-attr-methods_order.py8
-rw-r--r--test/messages/func_defining-attr-methods_order.txt2
4 files changed, 36 insertions, 21 deletions
diff --git a/ChangeLog b/ChangeLog
index 0c6a85c..6e3bce0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -15,6 +15,9 @@ ChangeLog for Pylint
* Issue broad-except and bare-except even if the number
of except handlers is different than 1. Fixes issue #113.
+ * Issue attribute-defined-outside-init for all cases, not just
+ for the last assignment. Closes issue #262.
+
2014-04-30 -- 1.2.1
* Restore the ability to specify the init-hook option via the
configuration file, which was accidentally broken in 1.2.0.
diff --git a/checkers/classes.py b/checkers/classes.py
index f5e2783..02d304b 100644
--- a/checkers/classes.py
+++ b/checkers/classes.py
@@ -272,28 +272,30 @@ a metaclass class method.'}
isinstance(n.statement(), (astroid.Delete, astroid.AugAssign))]
if not nodes:
continue # error detected by typechecking
- attr_defined = False
# check if any method attr is defined in is a defining method
- for node in nodes:
- if node.frame().name in defining_methods:
- attr_defined = True
- if not attr_defined:
- # check attribute is defined in a parent's __init__
- for parent in cnode.instance_attr_ancestors(attr):
- attr_defined = False
- # check if any parent method attr is defined in is a defining method
- for node in parent.instance_attrs[attr]:
- if node.frame().name in defining_methods:
- attr_defined = True
- if attr_defined:
- # we're done :)
- break
- else:
- # check attribute is defined as a class attribute
- try:
- cnode.local_attr(attr)
- except astroid.NotFoundError:
- self.add_message('attribute-defined-outside-init', args=attr, node=node)
+ if any(node.frame().name in defining_methods
+ for node in nodes):
+ continue
+
+ # check attribute is defined in a parent's __init__
+ for parent in cnode.instance_attr_ancestors(attr):
+ attr_defined = False
+ # check if any parent method attr is defined in is a defining method
+ for node in parent.instance_attrs[attr]:
+ if node.frame().name in defining_methods:
+ attr_defined = True
+ if attr_defined:
+ # we're done :)
+ break
+ else:
+ # check attribute is defined as a class attribute
+ try:
+ cnode.local_attr(attr)
+ except astroid.NotFoundError:
+ for node in nodes:
+ if node.frame().name not in defining_methods:
+ self.add_message('attribute-defined-outside-init',
+ args=attr, node=node)
def visit_function(self, node):
"""check method arguments, overriding"""
diff --git a/test/input/func_defining-attr-methods_order.py b/test/input/func_defining-attr-methods_order.py
index d64217d..888b192 100644
--- a/test/input/func_defining-attr-methods_order.py
+++ b/test/input/func_defining-attr-methods_order.py
@@ -26,8 +26,16 @@ class A(object):
def set_z(self, z):
''' set_z docstring filler '''
self.z = z
+ self.z = z
def setUp(self):
''' setUp docstring filler '''
self.x = 0
self.y = 0
+
+class B(A):
+ ''' class B '''
+
+ def test(self):
+ """ test """
+ self.z = 44
diff --git a/test/messages/func_defining-attr-methods_order.txt b/test/messages/func_defining-attr-methods_order.txt
index 3594754..5588319 100644
--- a/test/messages/func_defining-attr-methods_order.txt
+++ b/test/messages/func_defining-attr-methods_order.txt
@@ -1 +1,3 @@
W: 28:A.set_z: Attribute 'z' defined outside __init__
+W: 29:A.set_z: Attribute 'z' defined outside __init__
+W: 41:B.test: Attribute 'z' defined outside __init__