From 8a0343728bc9b578d533ef5aa21e1412a8951ce6 Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Mon, 26 Oct 2015 12:30:08 +0000 Subject: Class.getattr('__mro__') returns the actual MRO. Also, Class.getattr('__bases__') returns actual bases. It previously didn't work correctly, because it was putting the entire ancestors into the Tuple object and it put those classes into the wrong attribute. Closes issue #128. --- ChangeLog | 3 +++ astroid/scoped_nodes.py | 14 ++++++++------ astroid/tests/unittest_scoped_nodes.py | 23 +++++++++++++++++++++++ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 88e4aa2..6b93e83 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,9 @@ Change log for the astroid package (used to be astng) ===================================================== -- + + * Class.getattr('__mro__') returns the actual MRO. Closes issue #128. + * The logilab-common dependency is not needed anymore as the needed code was integrated into astroid. diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py index 31fc2f8..e6e3324 100644 --- a/astroid/scoped_nodes.py +++ b/astroid/scoped_nodes.py @@ -1359,14 +1359,16 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG, if name in self.special_attributes: if name == '__module__': return [node_classes.const_factory(self.root().qname())] + values - # FIXME: do we really need the actual list of ancestors? - # returning [Tuple()] + values don't break any test - # this is ticket http://www.logilab.org/ticket/52785 - # XXX need proper meta class handling + MRO implementation - if name == '__bases__' or (name == '__mro__' and self.newstyle): + if name == '__bases__': node = node_classes.Tuple() - node.items = self.ancestors(recurs=True, context=context) + elts = list(self._inferred_bases(context)) + node.postinit(elts=elts) return [node] + values + if name == '__mro__' and self.newstyle: + mro = self.mro() + node = node_classes.Tuple() + node.postinit(elts=mro) + return [node] return std_special_attributes(self, name) # don't modify the list in self.locals! values = list(values) diff --git a/astroid/tests/unittest_scoped_nodes.py b/astroid/tests/unittest_scoped_nodes.py index bf090da..8e82290 100644 --- a/astroid/tests/unittest_scoped_nodes.py +++ b/astroid/tests/unittest_scoped_nodes.py @@ -584,6 +584,29 @@ class ClassNodeTest(ModuleLoader, unittest.TestCase): self.assertEqual(len(cls.getattr('__dict__')), 1) self.assertEqual(len(cls.getattr('__mro__')), 1) + def test__mro__attribute(self): + node = test_utils.extract_node(''' + class A(object): pass + class B(object): pass + class C(A, B): pass + ''') + mro = node.getattr('__mro__')[0] + self.assertIsInstance(mro, nodes.Tuple) + self.assertEqual(mro.elts, node.mro()) + + def test__bases__attribute(self): + node = test_utils.extract_node(''' + class A(object): pass + class B(object): pass + class C(A, B): pass + class D(C): pass + ''') + bases = node.getattr('__bases__')[0] + self.assertIsInstance(bases, nodes.Tuple) + self.assertEqual(len(bases.elts), 1) + self.assertIsInstance(bases.elts[0], nodes.ClassDef) + self.assertEqual(bases.elts[0].name, 'C') + def test_cls_special_attributes_2(self): astroid = builder.parse(''' class A: pass -- cgit v1.2.1