diff options
author | Bryce Guinta <bryce.paul.guinta@gmail.com> | 2018-07-08 21:39:59 -0600 |
---|---|---|
committer | Bryce Guinta <bryce.guinta@protonmail.com> | 2018-07-13 18:15:50 -0600 |
commit | 2aa27e9aed6ffcba4a61655e291e852ecd001549 (patch) | |
tree | a72893897adc3a15015eca6837c37b51127f74d3 | |
parent | be3ba810d57253942cdc9e0639a7ad0043f7a688 (diff) | |
download | astroid-git-2aa27e9aed6ffcba4a61655e291e852ecd001549.tar.gz |
Fix attributes of ancestor classes from appearing in attribute inference
Close #581
-rw-r--r-- | astroid/bases.py | 9 | ||||
-rw-r--r-- | astroid/scoped_nodes.py | 4 | ||||
-rw-r--r-- | astroid/tests/unittest_inference.py | 30 |
3 files changed, 33 insertions, 10 deletions
diff --git a/astroid/bases.py b/astroid/bases.py index c7b0cbc3..caf230f1 100644 --- a/astroid/bases.py +++ b/astroid/bases.py @@ -343,9 +343,7 @@ class UnboundMethod(Proxy): # instance of the class given as first argument. if (self._proxied.name == '__new__' and self._proxied.parent.frame().qname() == '%s.object' % BUILTINS): - # XXX Avoid issue with type.__new__ inference. - # https://github.com/PyCQA/astroid/issues/581 - if caller.args and len(caller.args) == 1: + if caller.args: node_context = context.extra_context.get(caller.args[0]) infer = caller.args[0].infer(context=node_context) else: @@ -440,10 +438,7 @@ class BoundMethod(UnboundMethod): if (self.bound.__class__.__name__ == 'ClassDef' and self.bound.name == 'type' and self.name == '__new__' - and len(caller.args) == 4 - # TODO(cpopa): this check shouldn't be needed. - and self._proxied.parent.frame().qname() == '%s.object' % BUILTINS): - + and len(caller.args) == 4): # Check if we have an ``type.__new__(mcs, name, bases, attrs)`` call. new_cls = self._infer_type_new_call(caller, context) if new_cls: diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py index 1eef1416..3b1cc50a 100644 --- a/astroid/scoped_nodes.py +++ b/astroid/scoped_nodes.py @@ -2366,8 +2366,8 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG, context = contextmod.copy_context(context) context.lookupname = name try: - attrs = self.getattr(name, context, class_context=class_context) - for inferred in bases._infer_stmts(attrs, context, frame=self): + attr = self.getattr(name, context, class_context=class_context)[0] + for inferred in bases._infer_stmts([attr], context, frame=self): # yield Uninferable object instead of descriptors when necessary if (not isinstance(inferred, node_classes.Const) and isinstance(inferred, bases.Instance)): diff --git a/astroid/tests/unittest_inference.py b/astroid/tests/unittest_inference.py index 353ac23b..39f43435 100644 --- a/astroid/tests/unittest_inference.py +++ b/astroid/tests/unittest_inference.py @@ -3404,7 +3404,8 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase): ''') inferred = next(ast_node.infer()) self.assertIsInstance(inferred, nodes.ClassDef) - titles = [title.value for title in inferred.igetattr('title')] + titles = [title.value for attr in inferred.getattr('title') + for title in attr.inferred()] self.assertEqual(titles, ['Catch 22', 'Ubik', 'Grimus']) @unittest.expectedFailure @@ -4684,5 +4685,32 @@ def test_limit_inference_result_amount(): assert util.Uninferable in result_limited +def test_attribute_inference_should_not_access_base_classes(): + """attributes of classes should mask ancestor attribues""" + code = """ + type.__new__ #@ + """ + res = extract_node(code).inferred() + assert len(res) == 1 + assert res[0].parent.name == "type" + + +def test_attribute_mro_object_inference(): + """ + Inference should only infer results from the first available method + """ + inferred = extract_node(""" + class A: + def foo(self): + return 1 + class B(A): + def foo(self): + return 2 + B().foo() #@ + """).inferred() + assert len(inferred) == 1 + assert inferred[0].value == 2 + + if __name__ == '__main__': unittest.main() |