summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCeridwen <ceridwenv@gmail.com>2015-11-06 17:58:34 -0500
committerCeridwen <ceridwenv@gmail.com>2015-11-06 17:58:34 -0500
commitf659abbc89ae9eb295540d765e6e9f003578daf4 (patch)
tree57e436da9d1a799bce61a87d8f9cffe6ebb2dff5
parente28cf4e572d25d559c274acc81a7aff887d28efb (diff)
parentfcadcc2ea44651540f1118778a3826bbd4afec48 (diff)
downloadastroid-f659abbc89ae9eb295540d765e6e9f003578daf4.tar.gz
Merge upstream
-rw-r--r--astroid/arguments.py10
-rw-r--r--astroid/brain/brain_builtin_inference.py2
-rw-r--r--astroid/brain/brain_stdlib.py1
-rw-r--r--astroid/protocols.py10
-rw-r--r--astroid/scoped_nodes.py68
-rw-r--r--astroid/tests/unittest_inference.py48
6 files changed, 95 insertions, 44 deletions
diff --git a/astroid/arguments.py b/astroid/arguments.py
index a0ffe73..e4776ec 100644
--- a/astroid/arguments.py
+++ b/astroid/arguments.py
@@ -183,6 +183,16 @@ class CallSite(object):
else:
# XXX can do better ?
boundnode = funcnode.parent.frame()
+
+ if isinstance(boundnode, nodes.ClassDef):
+ # Verify that we're accessing a method
+ # of the metaclass through a class, as in
+ # `cls.metaclass_method`. In this case, the
+ # first argument is always the class.
+ method_scope = funcnode.parent.scope()
+ if method_scope is boundnode.metaclass():
+ return iter((boundnode, ))
+
if funcnode.type == 'method':
if not isinstance(boundnode, bases.Instance):
boundnode = bases.Instance(boundnode)
diff --git a/astroid/brain/brain_builtin_inference.py b/astroid/brain/brain_builtin_inference.py
index a0f55fd..23e878f 100644
--- a/astroid/brain/brain_builtin_inference.py
+++ b/astroid/brain/brain_builtin_inference.py
@@ -60,8 +60,6 @@ def _extend_str(class_node, rvalue):
return {rvalue}
def ljust(self, width, fillchar=None):
return {rvalue}
- def split(self, *args):
- return []
''')
code = code.format(rvalue=rvalue)
fake = AstroidBuilder(MANAGER).string_build(code)['whatever']
diff --git a/astroid/brain/brain_stdlib.py b/astroid/brain/brain_stdlib.py
index 6a988d6..2f5e46d 100644
--- a/astroid/brain/brain_stdlib.py
+++ b/astroid/brain/brain_stdlib.py
@@ -140,6 +140,7 @@ class deque(object):
def rotate(self, n): pass
def __iter__(self): return self
def __reversed__(self): return self.iterable[::-1]
+ def __getitem__(self, index): pass
''')
diff --git a/astroid/protocols.py b/astroid/protocols.py
index b294a0b..cb8426f 100644
--- a/astroid/protocols.py
+++ b/astroid/protocols.py
@@ -280,12 +280,16 @@ def _arguments_infer_argname(self, name, context):
# first argument of instance/class method
if self.args and getattr(self.args[0], 'name', None) == name:
functype = self.parent.type
+ cls = self.parent.parent.scope()
+ is_metaclass = isinstance(cls, nodes.ClassDef) and cls.type == 'metaclass'
+ # If this is a metaclass, then the first argument will always
+ # be the class, not an instance.
+ if is_metaclass or functype == 'classmethod':
+ yield cls
+ return
if functype == 'method':
yield bases.Instance(self.parent.parent.frame())
return
- if functype == 'classmethod':
- yield self.parent.parent.frame()
- return
if context and context.callcontext:
call_site = arguments.CallSite(context.callcontext)
diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py
index c75cd7a..3af9267 100644
--- a/astroid/scoped_nodes.py
+++ b/astroid/scoped_nodes.py
@@ -777,43 +777,45 @@ class FunctionDef(node_classes.Statement, Lambda):
else:
type_name = 'method'
- if self.decorators:
- for node in self.decorators.nodes:
- if isinstance(node, node_classes.Name):
- if node.name in builtin_descriptors:
- return node.name
-
- if isinstance(node, node_classes.Call):
- # Handle the following case:
- # @some_decorator(arg1, arg2)
- # def func(...)
- #
- try:
- current = next(node.func.infer())
- except exceptions.InferenceError:
- continue
- _type = _infer_decorator_callchain(current)
+ if not self.decorators:
+ return type_name
+
+ for node in self.decorators.nodes:
+ if isinstance(node, node_classes.Name):
+ if node.name in builtin_descriptors:
+ return node.name
+
+ if isinstance(node, node_classes.Call):
+ # Handle the following case:
+ # @some_decorator(arg1, arg2)
+ # def func(...)
+ #
+ try:
+ current = next(node.func.infer())
+ except exceptions.InferenceError:
+ continue
+ _type = _infer_decorator_callchain(current)
+ if _type is not None:
+ return _type
+
+ try:
+ for inferred in node.infer():
+ # Check to see if this returns a static or a class method.
+ _type = _infer_decorator_callchain(inferred)
if _type is not None:
return _type
- try:
- for inferred in node.infer():
- # Check to see if this returns a static or a class method.
- _type = _infer_decorator_callchain(inferred)
- if _type is not None:
- return _type
-
- if not isinstance(inferred, ClassDef):
+ if not isinstance(inferred, ClassDef):
+ continue
+ for ancestor in inferred.ancestors():
+ if not isinstance(ancestor, ClassDef):
continue
- for ancestor in inferred.ancestors():
- if not isinstance(ancestor, ClassDef):
- continue
- if ancestor.is_subtype_of('%s.classmethod' % BUILTINS):
- return 'classmethod'
- elif ancestor.is_subtype_of('%s.staticmethod' % BUILTINS):
- return 'staticmethod'
- except exceptions.InferenceError:
- pass
+ if ancestor.is_subtype_of('%s.classmethod' % BUILTINS):
+ return 'classmethod'
+ elif ancestor.is_subtype_of('%s.staticmethod' % BUILTINS):
+ return 'staticmethod'
+ except exceptions.InferenceError:
+ pass
return type_name
@decorators_mod.cachedproperty
diff --git a/astroid/tests/unittest_inference.py b/astroid/tests/unittest_inference.py
index f543dbb..98a4daf 100644
--- a/astroid/tests/unittest_inference.py
+++ b/astroid/tests/unittest_inference.py
@@ -1796,8 +1796,6 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
' '.index() #@
' '.find() #@
' '.count() #@
-
- ' '.split() #@
"""
ast = test_utils.extract_node(code, __name__)
self.assertInferConst(ast[0], u'')
@@ -1805,7 +1803,6 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
self.assertInferConst(ast[i], '')
for i in range(16, 19):
self.assertInferConst(ast[i], 0)
- self.assertInferList(ast[19], [])
def test_unicode_methods(self):
code = """
@@ -1830,8 +1827,6 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
u' '.index() #@
u' '.find() #@
u' '.count() #@
-
- u' '.split() #@
"""
ast = test_utils.extract_node(code, __name__)
self.assertInferConst(ast[0], '')
@@ -1839,7 +1834,6 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
self.assertInferConst(ast[i], u'')
for i in range(16, 19):
self.assertInferConst(ast[i], 0)
- self.assertInferList(ast[19], [])
def test_scope_lookup_same_attributes(self):
code = '''
@@ -3143,6 +3137,48 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
self.assertIsInstance(third_c, Instance)
self.assertEqual(third_c.name, 'A')
+ def test_metaclass_subclasses_arguments_are_classes_not_instances(self):
+ ast_node = test_utils.extract_node('''
+ class A(type):
+ def test(cls):
+ return cls
+ import six
+ @six.add_metaclass(A)
+ class B(object):
+ pass
+
+ B.test() #@
+ ''')
+ inferred = next(ast_node.infer())
+ self.assertIsInstance(inferred, nodes.ClassDef)
+ self.assertEqual(inferred.name, 'B')
+
+ def test_infer_cls_in_class_methods(self):
+ ast_nodes = test_utils.extract_node('''
+ class A(type):
+ def __call__(cls):
+ cls #@
+ class B(object):
+ def __call__(cls):
+ cls #@
+ ''')
+ first = next(ast_nodes[0].infer())
+ self.assertIsInstance(first, nodes.ClassDef)
+ second = next(ast_nodes[1].infer())
+ self.assertIsInstance(second, Instance)
+
+ @unittest.expectedFailure
+ def test_metaclass_arguments_are_classes_not_instances(self):
+ ast_node = test_utils.extract_node('''
+ class A(type):
+ def test(cls): return cls
+ A.test() #@
+ ''')
+ # This is not supported yet
+ inferred = next(ast_node.infer())
+ self.assertIsInstance(inferred, ClassDef)
+ self.assertEqual(inferred.name, 'A')
+
class GetattrTest(unittest.TestCase):