diff options
author | Torsten Marek <shlomme@gmail.com> | 2014-07-24 16:29:12 +0200 |
---|---|---|
committer | Torsten Marek <shlomme@gmail.com> | 2014-07-24 16:29:12 +0200 |
commit | 4305109a216a5b48df7619380b39e9d6465e1d53 (patch) | |
tree | 110081b6ae91a583fa3c5517aa55aad7d6291b10 | |
parent | c4bfde2863540a9260d81c094bab20d29b4ff612 (diff) | |
download | astroid-4305109a216a5b48df7619380b39e9d6465e1d53.tar.gz |
Return new classes when inferring calls to type(name, bases, ns) and its subtypes.
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | scoped_nodes.py | 25 | ||||
-rw-r--r-- | test/unittest_scoped_nodes.py | 12 |
3 files changed, 40 insertions, 1 deletions
@@ -2,6 +2,10 @@ Change log for the astroid package (used to be astng) ===================================================== -- + + * infer_call_result called on a subtype of the builtin type will now + return a new `Class` rather than an `Instance`. + * `Class.metaclass()` now handles module-level __metaclass__ declaration on python 2, and no longer looks at the __metaclass__ class attribute on python 3. diff --git a/scoped_nodes.py b/scoped_nodes.py index f070837..6b09b9d 100644 --- a/scoped_nodes.py +++ b/scoped_nodes.py @@ -851,9 +851,32 @@ class Class(Statement, LocalsDictNodeNG, FilterStmtsMixin): def callable(self): return True + def _is_subtype_of(self, type_name): + if self.qname() == type_name: + return True + for anc in self.ancestors(): + if anc.qname() == type_name: + return True + def infer_call_result(self, caller, context=None): """infer what a class is returning when called""" - yield Instance(self) + if self._is_subtype_of('%s.type' % (BUILTINS,)) and len(caller.args) == 3: + name_node = caller.args[0].infer().next() + if isinstance(name_node, Const) and isinstance(name_node.value, basestring): + name = name_node.value + else: + yield YES + return + result = Class(name, None) + bases = caller.args[1].infer().next() + if isinstance(bases, (Tuple, List)): + result.bases = bases.itered() + else: + result.bases = [YES] + result.parent = caller.parent + yield result + else: + yield Instance(self) def scope_lookup(self, node, name, offset=0): if node in self.bases: diff --git a/test/unittest_scoped_nodes.py b/test/unittest_scoped_nodes.py index 37ff3be..116b275 100644 --- a/test/unittest_scoped_nodes.py +++ b/test/unittest_scoped_nodes.py @@ -852,6 +852,18 @@ def g2(): self.assertIsInstance(meta, nodes.Class) self.assertEqual(meta.name, metaclass) + def test_metaclass_type(self): + klass = extract_node(""" + def with_metaclass(meta, base=object): + return meta("NewBase", (base, ), {}) + + class ClassWithMeta(with_metaclass(type)): #@ + pass + """) + self.assertEqual( + ['NewBase', 'object'], + [base.name for base in klass.ancestors()]) + def test_nonregr_infer_callresult(self): astroid = abuilder.string_build(dedent(""" class Delegate(object): |