summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryce Guinta <bryce.paul.guinta@gmail.com>2018-07-08 21:39:59 -0600
committerBryce Guinta <bryce.guinta@protonmail.com>2018-07-13 18:15:50 -0600
commit2aa27e9aed6ffcba4a61655e291e852ecd001549 (patch)
treea72893897adc3a15015eca6837c37b51127f74d3
parentbe3ba810d57253942cdc9e0639a7ad0043f7a688 (diff)
downloadastroid-git-2aa27e9aed6ffcba4a61655e291e852ecd001549.tar.gz
Fix attributes of ancestor classes from appearing in attribute inference
Close #581
-rw-r--r--astroid/bases.py9
-rw-r--r--astroid/scoped_nodes.py4
-rw-r--r--astroid/tests/unittest_inference.py30
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()