diff options
author | cpopa <devnull@localhost> | 2014-08-14 15:29:44 +0300 |
---|---|---|
committer | cpopa <devnull@localhost> | 2014-08-14 15:29:44 +0300 |
commit | 86a9ed29402d9396bce7eb1a6dff8dba6e941e12 (patch) | |
tree | 5145370e7b8690e5ebda6bfaee7edd221f36bce2 | |
parent | c407bc2e3218a6cab9d8c6eaf371eaa6fa7d8f61 (diff) | |
download | pylint-86a9ed29402d9396bce7eb1a6dff8dba6e941e12.tar.gz |
Look in the metaclass, if defined, for members not found in the current class. Closes issue #306.
-rw-r--r-- | ChangeLog | 3 | ||||
-rw-r--r-- | checkers/typecheck.py | 14 | ||||
-rw-r--r-- | test/functional/class_members.py | 51 | ||||
-rw-r--r-- | test/functional/class_members.txt | 6 | ||||
-rw-r--r-- | test/input/func_class_members.py | 32 | ||||
-rw-r--r-- | test/messages/func_class_members.txt | 4 |
6 files changed, 74 insertions, 36 deletions
@@ -59,6 +59,9 @@ ChangeLog for Pylint * Don't emit 'unused-import' when a special object is imported (__all__, __doc__ etc.). Closes issue #309. + + * Look in the metaclass, if defined, for members not found in the current + class. Closes issue #306. 2014-07-26 -- 1.3.0 diff --git a/checkers/typecheck.py b/checkers/typecheck.py index 537a220..a8851a2 100644 --- a/checkers/typecheck.py +++ b/checkers/typecheck.py @@ -249,6 +249,20 @@ accessed. Python regular expressions are accepted.'} # explicit skipping of module member access if owner.root().name in self.config.ignored_modules: continue + if isinstance(owner, astroid.Class): + # Look up in the metaclass only if the owner is itself + # a class. + # TODO: getattr doesn't return by default members + # from the metaclass, because handling various cases + # of methods accessible from the metaclass itself + # and/or subclasses only is too complicated for little to + # no benefit. + metaclass = owner.metaclass() + try: + if metaclass and metaclass.getattr(node.attrname): + continue + except NotFoundError: + pass missingattr.add((owner, name)) continue # stop on the first found diff --git a/test/functional/class_members.py b/test/functional/class_members.py new file mode 100644 index 0000000..d60458b --- /dev/null +++ b/test/functional/class_members.py @@ -0,0 +1,51 @@ +""" Various tests for class members access. """
+# pylint: disable=R0903
+
+class MyClass(object):
+ """class docstring"""
+
+ def __init__(self):
+ """init"""
+ self.correct = 1
+
+ def test(self):
+ """test"""
+ self.correct += 2
+ self.incorrect += 2 # [no-member]
+ del self.havenot # [no-member]
+ self.nonexistent1.truc() # [no-member]
+ self.nonexistent2[1] = 'hehe' # [no-member]
+
+class XYZMixin(object):
+ """access to undefined members should be ignored in mixin classes by
+ default
+ """
+ def __init__(self):
+ print self.nonexistent
+
+
+class NewClass(object):
+ """use object.__setattr__"""
+ def __init__(self):
+ self.__setattr__('toto', 'tutu')
+
+from abc import ABCMeta
+
+class TestMetaclass(object):
+ """ Test attribute access for metaclasses. """
+ __metaclass__ = ABCMeta
+
+class Metaclass(type):
+ """ metaclass """
+ @classmethod
+ def test(mcs):
+ """ classmethod """
+
+class UsingMetaclass(object):
+ """ empty """
+ __metaclass__ = Metaclass
+
+TestMetaclass.register(int)
+UsingMetaclass.test()
+TestMetaclass().register(int) # [no-member]
+UsingMetaclass().test() # [no-member]
diff --git a/test/functional/class_members.txt b/test/functional/class_members.txt new file mode 100644 index 0000000..0cb808f --- /dev/null +++ b/test/functional/class_members.txt @@ -0,0 +1,6 @@ +no-member:14:MyClass.test:Instance of 'MyClass' has no 'incorrect' member
+no-member:15:MyClass.test:Instance of 'MyClass' has no 'havenot' member
+no-member:16:MyClass.test:Instance of 'MyClass' has no 'nonexistent1' member
+no-member:17:MyClass.test:Instance of 'MyClass' has no 'nonexistent2' member
+no-member:48::Instance of 'TestMetaclass' has no 'register' member
+no-member:49::Instance of 'UsingMetaclass' has no 'test' member
diff --git a/test/input/func_class_members.py b/test/input/func_class_members.py deleted file mode 100644 index ad147e5..0000000 --- a/test/input/func_class_members.py +++ /dev/null @@ -1,32 +0,0 @@ -# pylint: disable=R0903 -"""test class members""" - -__revision__ = '' - -class MyClass(object): - """class docstring""" - - def __init__(self): - """init""" - self.correct = 1 - - def test(self): - """test""" - self.correct += 2 - self.incorrect += 2 - del self.havenot - self.nonexistent1.truc() - self.nonexistent2[1] = 'hehe' - -class XYZMixin(object): - """access to undefined members should be ignored in mixin classes by - default - """ - def __init__(self): - print self.nonexistent - - -class NewClass(object): - """use object.__setattr__""" - def __init__(self): - self.__setattr__('toto', 'tutu') diff --git a/test/messages/func_class_members.txt b/test/messages/func_class_members.txt deleted file mode 100644 index 054e21a..0000000 --- a/test/messages/func_class_members.txt +++ /dev/null @@ -1,4 +0,0 @@ -E: 16:MyClass.test: Instance of 'MyClass' has no 'incorrect' member -E: 17:MyClass.test: Instance of 'MyClass' has no 'havenot' member -E: 18:MyClass.test: Instance of 'MyClass' has no 'nonexistent1' member -E: 19:MyClass.test: Instance of 'MyClass' has no 'nonexistent2' member |