summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorsten Marek <shlomme@gmail.com>2014-07-24 16:29:12 +0200
committerTorsten Marek <shlomme@gmail.com>2014-07-24 16:29:12 +0200
commit4305109a216a5b48df7619380b39e9d6465e1d53 (patch)
tree110081b6ae91a583fa3c5517aa55aad7d6291b10
parentc4bfde2863540a9260d81c094bab20d29b4ff612 (diff)
downloadastroid-4305109a216a5b48df7619380b39e9d6465e1d53.tar.gz
Return new classes when inferring calls to type(name, bases, ns) and its subtypes.
-rw-r--r--ChangeLog4
-rw-r--r--scoped_nodes.py25
-rw-r--r--test/unittest_scoped_nodes.py12
3 files changed, 40 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index ae4d7bc..ef24ed5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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):