summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCeridwen <ceridwenv@gmail.com>2015-11-06 18:16:41 -0500
committerCeridwen <ceridwenv@gmail.com>2015-11-06 18:16:41 -0500
commitd052e7e223d32c7afbe8d8a19ff3747025f59982 (patch)
treed336c42ff5f55840a4703ce6c2f3c133a7e8a34c
parentd3d9bcbc91dfb60ee88b1a6667db92fd00172d67 (diff)
parent2f88b3eb44b6e895f45dd45c709e29a8f365e04f (diff)
downloadastroid-git-d052e7e223d32c7afbe8d8a19ff3747025f59982.tar.gz
Merge upstream changes
-rw-r--r--astroid/__init__.py2
-rw-r--r--astroid/arguments.py12
-rw-r--r--astroid/bases.py2
-rw-r--r--astroid/brain/brain_stdlib.py1
-rw-r--r--astroid/exceptions.py6
-rw-r--r--astroid/inference.py2
-rw-r--r--astroid/manager.py2
-rw-r--r--astroid/protocols.py10
-rw-r--r--astroid/scoped_nodes.py76
-rw-r--r--astroid/tests/unittest_brain.py12
-rw-r--r--astroid/tests/unittest_inference.py42
-rw-r--r--astroid/tests/unittest_manager.py2
-rw-r--r--astroid/tests/unittest_scoped_nodes.py1
-rw-r--r--astroid/util.py4
-rw-r--r--tox.ini35
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
diff --git a/tox.ini b/tox.ini
index 8b23ed25..ea3a10c0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -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"