diff options
author | Ceridwen <ceridwenv@gmail.com> | 2015-11-06 17:58:34 -0500 |
---|---|---|
committer | Ceridwen <ceridwenv@gmail.com> | 2015-11-06 17:58:34 -0500 |
commit | f659abbc89ae9eb295540d765e6e9f003578daf4 (patch) | |
tree | 57e436da9d1a799bce61a87d8f9cffe6ebb2dff5 /astroid | |
parent | e28cf4e572d25d559c274acc81a7aff887d28efb (diff) | |
parent | fcadcc2ea44651540f1118778a3826bbd4afec48 (diff) | |
download | astroid-f659abbc89ae9eb295540d765e6e9f003578daf4.tar.gz |
Merge upstream
Diffstat (limited to 'astroid')
-rw-r--r-- | astroid/arguments.py | 10 | ||||
-rw-r--r-- | astroid/brain/brain_builtin_inference.py | 2 | ||||
-rw-r--r-- | astroid/brain/brain_stdlib.py | 1 | ||||
-rw-r--r-- | astroid/protocols.py | 10 | ||||
-rw-r--r-- | astroid/scoped_nodes.py | 68 | ||||
-rw-r--r-- | astroid/tests/unittest_inference.py | 48 |
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): |