diff options
author | Ceridwen <ceridwenv@gmail.com> | 2015-11-06 18:16:41 -0500 |
---|---|---|
committer | Ceridwen <ceridwenv@gmail.com> | 2015-11-06 18:16:41 -0500 |
commit | d052e7e223d32c7afbe8d8a19ff3747025f59982 (patch) | |
tree | d336c42ff5f55840a4703ce6c2f3c133a7e8a34c | |
parent | d3d9bcbc91dfb60ee88b1a6667db92fd00172d67 (diff) | |
parent | 2f88b3eb44b6e895f45dd45c709e29a8f365e04f (diff) | |
download | astroid-git-d052e7e223d32c7afbe8d8a19ff3747025f59982.tar.gz |
Merge upstream changes
-rw-r--r-- | astroid/__init__.py | 2 | ||||
-rw-r--r-- | astroid/arguments.py | 12 | ||||
-rw-r--r-- | astroid/bases.py | 2 | ||||
-rw-r--r-- | astroid/brain/brain_stdlib.py | 1 | ||||
-rw-r--r-- | astroid/exceptions.py | 6 | ||||
-rw-r--r-- | astroid/inference.py | 2 | ||||
-rw-r--r-- | astroid/manager.py | 2 | ||||
-rw-r--r-- | astroid/protocols.py | 10 | ||||
-rw-r--r-- | astroid/scoped_nodes.py | 76 | ||||
-rw-r--r-- | astroid/tests/unittest_brain.py | 12 | ||||
-rw-r--r-- | astroid/tests/unittest_inference.py | 42 | ||||
-rw-r--r-- | astroid/tests/unittest_manager.py | 2 | ||||
-rw-r--r-- | astroid/tests/unittest_scoped_nodes.py | 1 | ||||
-rw-r--r-- | astroid/util.py | 4 | ||||
-rw-r--r-- | tox.ini | 35 |
15 files changed, 136 insertions, 73 deletions
diff --git a/astroid/__init__.py b/astroid/__init__.py index 973bb84b..fee55f5c 100644 --- a/astroid/__init__.py +++ b/astroid/__init__.py @@ -63,7 +63,7 @@ from astroid.bases import Instance, BoundMethod, UnboundMethod from astroid.node_classes import are_exclusive, unpack_infer from astroid.scoped_nodes import builtin_lookup from astroid.builder import parse -from astroid.util import Uninferable +from astroid.util import Uninferable, YES # make a manager instance (borg) accessible from astroid package from astroid.manager import AstroidManager diff --git a/astroid/arguments.py b/astroid/arguments.py index 7136834c..949783af 100644 --- a/astroid/arguments.py +++ b/astroid/arguments.py @@ -196,6 +196,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) @@ -222,7 +232,7 @@ class CallSite(object): "to {func!r}: {unpacked_kwargs!r} doesn't correspond to " "{keyword_arguments!r}.", keyword_arguments=self.keyword_arguments, - unpacked_kwargs = self._unpacked_kwargs, + unpacked_kwargs=self._unpacked_kwargs, call_site=self, func=funcnode, arg=name, context=context) kwarg = nodes.Dict(lineno=funcnode.args.lineno, col_offset=funcnode.args.col_offset, diff --git a/astroid/bases.py b/astroid/bases.py index 05633b8b..0762f528 100644 --- a/astroid/bases.py +++ b/astroid/bases.py @@ -131,7 +131,7 @@ class Instance(Proxy): def getattr(self, name, context=None, lookupclass=True): try: values = self._proxied.instance_attr(name, context) - except exceptions.AttributeInferenceError as exception: + except exceptions.AttributeInferenceError: if name == '__class__': return [self._proxied] if lookupclass: diff --git a/astroid/brain/brain_stdlib.py b/astroid/brain/brain_stdlib.py index 6a988d6b..2f5e46d9 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/exceptions.py b/astroid/exceptions.py index ede21d96..e92abc58 100644 --- a/astroid/exceptions.py +++ b/astroid/exceptions.py @@ -57,14 +57,14 @@ class AstroidBuildingException(AstroidError): class NoDefault(AstroidError): """raised by function's `default_value` method when an argument has no default value - + Standard attributes: func: Function node. name: Name of argument without a default. """ func = None name = None - + def __init__(self, message='{func!r} has no default for {name!r}.', **kws): super(NoDefault, self).__init__(message, **kws) @@ -125,7 +125,7 @@ class InferenceError(ResolveError): context: InferenceContext object. """ node = None - context= None + context = None def __init__(self, message='Inference failed for {node!r}.', **kws): super(InferenceError, self).__init__(message, **kws) diff --git a/astroid/inference.py b/astroid/inference.py index 47f60d9d..45f31ff6 100644 --- a/astroid/inference.py +++ b/astroid/inference.py @@ -747,6 +747,6 @@ def instance_getitem(self, index, context=None): except StopIteration: util.reraise(exceptions.InferenceError( message='Inference for {node!r}[{index!s}] failed.', - node=self, index=index.value, context=context)) + node=self, index=index, context=context)) bases.Instance.getitem = instance_getitem diff --git a/astroid/manager.py b/astroid/manager.py index 0f6673d2..55a09be1 100644 --- a/astroid/manager.py +++ b/astroid/manager.py @@ -239,7 +239,7 @@ class AstroidManager(object): name = klass.__name__ except AttributeError: util.reraise(exceptions.AstroidBuildingException( - 'Unable to get name for {class_repr}:\n', + 'Unable to get name for {class_repr}:\n', cls=klass, class_repr=safe_repr(klass))) except Exception as ex: # pylint: disable=broad-except util.reraise(exceptions.AstroidBuildingException( diff --git a/astroid/protocols.py b/astroid/protocols.py index 6b6c0c0f..41eeb85e 100644 --- a/astroid/protocols.py +++ b/astroid/protocols.py @@ -283,12 +283,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 ab130f5a..475cb381 100644 --- a/astroid/scoped_nodes.py +++ b/astroid/scoped_nodes.py @@ -27,7 +27,6 @@ import itertools import warnings import six -import wrapt from astroid import bases from astroid import context as contextmod @@ -777,43 +776,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 _type is not None: - return _type + 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: - 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 + current = next(node.func.infer()) + except exceptions.InferenceError: + continue + _type = _infer_decorator_callchain(current) + if _type is not None: + return _type - if not isinstance(inferred, ClassDef): + 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): + 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 @@ -1347,6 +1348,13 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG, """return Instance of ClassDef node, else return self""" return bases.Instance(self) + def instanciate_class(self): + warnings.warn('%s.instanciate_class() is deprecated and slated for ' + ' removal in astroid 2.0, use %s.instantiate_class() ' + ' instead.' % (type(self).__name__, type(self).__name__), + PendingDeprecationWarning, stacklevel=2) + return self.instantiate_class() + def getattr(self, name, context=None, class_context=True): """Get an attribute from this class, using Python's attribute semantic diff --git a/astroid/tests/unittest_brain.py b/astroid/tests/unittest_brain.py index 87f49139..07172c56 100644 --- a/astroid/tests/unittest_brain.py +++ b/astroid/tests/unittest_brain.py @@ -45,7 +45,11 @@ try: import enum # pylint: disable=unused-import HAS_ENUM = True except ImportError: - HAS_ENUM = False + try: + import enum34 as enum + HAS_ENUM = True + except ImportError: + HAS_ENUM = False try: import dateutil # pylint: disable=unused-import @@ -121,15 +125,13 @@ class NamedTupleTest(unittest.TestCase): """) self.assertIs(util.Uninferable, next(klass.infer())) - @unittest.skipIf(sys.version_info[0] > 2, - 'namedtuple inference is broken on Python 3') def test_namedtuple_advanced_inference(self): # urlparse return an object of class ParseResult, which has a # namedtuple call and a mixin as base classes result = test_utils.extract_node(""" - import urlparse + import six - result = __(urlparse.urlparse('gopher://')) + result = __(six.moves.urllib.parse.urlparse('gopher://')) """) instance = next(result.infer()) self.assertEqual(len(instance.getattr('scheme')), 1) diff --git a/astroid/tests/unittest_inference.py b/astroid/tests/unittest_inference.py index 19b83772..98a4daf0 100644 --- a/astroid/tests/unittest_inference.py +++ b/astroid/tests/unittest_inference.py @@ -3137,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): diff --git a/astroid/tests/unittest_manager.py b/astroid/tests/unittest_manager.py index 7e4eeae7..6fc07762 100644 --- a/astroid/tests/unittest_manager.py +++ b/astroid/tests/unittest_manager.py @@ -33,7 +33,7 @@ BUILTINS = six.moves.builtins.__name__ def _get_file_from_object(obj): if platform.python_implementation() == 'Jython': return obj.__file__.split("$py.class")[0] + ".py" - if sys.version_info > (3, 0): + if sys.version_info > (3, 0) or platform.python_implementation() == 'PyPy': return obj.__file__ else: return obj.__file__[:-1] diff --git a/astroid/tests/unittest_scoped_nodes.py b/astroid/tests/unittest_scoped_nodes.py index ddfb7a22..6f215e47 100644 --- a/astroid/tests/unittest_scoped_nodes.py +++ b/astroid/tests/unittest_scoped_nodes.py @@ -24,7 +24,6 @@ from functools import partial import unittest import warnings -import astroid from astroid import builder from astroid import nodes from astroid import scoped_nodes diff --git a/astroid/util.py b/astroid/util.py index 10c5415a..1d50f7be 100644 --- a/astroid/util.py +++ b/astroid/util.py @@ -101,3 +101,7 @@ def proxy_alias(alias_name, node_type): {'__class__': object.__dict__['__class__'], '__instancecheck__': _instancecheck}) return proxy(lambda: node_type) + + +# Backwards-compatibility aliases +YES = Uninferable @@ -1,33 +1,26 @@ [tox] -# official list is -# envlist = py27, py33, py34, pypy, jython +# Official list +# envlist = py27, py33, py34, py35, pypy, jython, pylint + +# drone.io envlist = py27, py33, pylint -# envlist = py27, py34 + +# For testing off drone.io---please don't delete. +# envlist = py27, py34, pypy, pylint [testenv:pylint] -deps = - lazy-object-proxy - singledispatch - six - wrapt - hg+https://bitbucket.org/logilab/pylint commands = pylint -rn --rcfile={toxinidir}/pylintrc {envsitepackagesdir}/astroid -# This is commented out because tox will try to load the interpreter -# for any defined environment even if it's not in envlist, which will -# then fail on drone.io. - -# [testenv:py34] -# deps = -# lazy-object-proxy -# six -# wrapt - [testenv] deps = + py27,py33,pypy,jython: enum34 lazy-object-proxy - singledispatch + nose + py27,py33,py34,py35: numpy + pytest + python-dateutil + py27,py33,pypy,jython: singledispatch six wrapt - + pylint: hg+https://bitbucket.org/logilab/pylint commands = python -m unittest discover -s {envsitepackagesdir}/astroid/tests -p "unittest*.py" |