summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCeridwen <ceridwenv@gmail.com>2015-08-14 12:02:40 -0400
committerCeridwen <ceridwenv@gmail.com>2015-08-14 12:02:40 -0400
commitc39599ad4f32608cb03e680516678a8fd1ba4dee (patch)
treead7c279bb7a3b34241547ca3d0b079c396533f5f
parent43f27a00205f5b9b4a341a2c2e3f645dffe14904 (diff)
parentd7294bd79062a4f47db8a761d79e6c1dd166bc72 (diff)
downloadastroid-git-c39599ad4f32608cb03e680516678a8fd1ba4dee.tar.gz
Merge main into default
-rw-r--r--ChangeLog44
-rw-r--r--README3
-rw-r--r--astroid/__init__.py3
-rw-r--r--astroid/__pkginfo__.py2
-rw-r--r--astroid/as_string.py6
-rw-r--r--astroid/bases.py152
-rw-r--r--astroid/brain/builtin_inference.py66
-rw-r--r--astroid/brain/py2stdlib.py10
-rw-r--r--astroid/builder.py39
-rw-r--r--astroid/context.py141
-rw-r--r--astroid/decorators.py79
-rw-r--r--astroid/helpers.py16
-rw-r--r--astroid/inference.py339
-rw-r--r--astroid/manager.py100
-rw-r--r--astroid/mixins.py19
-rw-r--r--astroid/modutils.py33
-rw-r--r--astroid/node_classes.py187
-rw-r--r--astroid/nodes.py2
-rw-r--r--astroid/objects.py70
-rw-r--r--astroid/protocols.py151
-rw-r--r--astroid/raw_building.py105
-rw-r--r--astroid/rebuilder.py86
-rw-r--r--astroid/scoped_nodes.py294
-rw-r--r--astroid/tests/testdata/python2/data/module.py2
-rw-r--r--astroid/tests/testdata/python2/data/notamodule/file.py0
-rw-r--r--astroid/tests/testdata/python3/data/module.py2
-rw-r--r--astroid/tests/testdata/python3/data/notamodule/file.py0
-rw-r--r--astroid/tests/unittest_brain.py6
-rw-r--r--astroid/tests/unittest_builder.py21
-rw-r--r--astroid/tests/unittest_helpers.py10
-rw-r--r--astroid/tests/unittest_inference.py141
-rw-r--r--astroid/tests/unittest_lookup.py8
-rw-r--r--astroid/tests/unittest_modutils.py50
-rw-r--r--astroid/tests/unittest_nodes.py20
-rw-r--r--astroid/tests/unittest_protocols.py12
-rw-r--r--astroid/tests/unittest_regrtest.py5
-rw-r--r--astroid/tests/unittest_scoped_nodes.py147
-rw-r--r--astroid/tests/unittest_transforms.py218
-rw-r--r--astroid/transforms.py96
-rw-r--r--astroid/util.py38
-rw-r--r--tox.ini42
41 files changed, 1775 insertions, 990 deletions
diff --git a/ChangeLog b/ChangeLog
index f732985b..dc5445e3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,9 @@ Change log for the astroid package (used to be astng)
=====================================================
--
+ * The logilab-common dependency is not needed anymore as the needed code
+ was integrated into astroid.
+
* Generated enum member stubs now support IntEnum and multiple
base classes.
@@ -234,7 +237,46 @@ Change log for the astroid package (used to be astng)
in a non-package will finally result in an import-error on Pylint's side.
Until now relative_only was ignored, leading to the import of `something`,
if it was globally available.
-
+
+ * Add get_wrapping_class API to scoped_nodes, which can be used to
+ retrieve the class that wraps a node.
+
+ * Class.getattr looks by default in the implicit and the explicit metaclasses,
+ which is `type` on Python 3.
+
+ Closes issue #114.
+
+ * There's a new separate step for transforms.
+
+ Until now, the transforms were applied at the same time the tree was
+ being built. This was problematic if the transform functions were
+ using inference, since the inference was executed on a partially
+ constructed tree, which led to failures when post-building
+ information was needed (such as setting the _from_names
+ for the From imports).
+ Now there's a separate step for transforms, which are applied
+ using transform.TransformVisitor.
+ There's a couple of other related changes:
+
+ * astroid.parse and AstroidBuilder gained a new parameter
+ `apply_transforms`, which is a boolean flag, which will
+ control if the transforms are applied. We do this because
+ there are uses when the vanilla tree is wanted, without
+ any implicit modification.
+
+ * the transforms are also applied for builtin modules,
+ as a side effect of the fact that transform visiting
+ was moved in AstroidBuilder._post_build from
+ AstroidBuilder._data_build.
+
+ Closes issue #116.
+
+ * Class._explicit_metaclass is now a public API, in the form of
+ Class.declared_metaclass.
+
+ Class.mro remains the de facto method for retrieving the metaclass
+ of a class, which will also do an evaluation of what declared_metaclass
+ returns.
2015-03-14 -- 1.3.6
diff --git a/README b/README
index 1cca049c..b19aca87 100644
--- a/README
+++ b/README
@@ -59,7 +59,6 @@ Test
----
Tests are in the 'test' subdirectory. To launch the whole tests suite
-at once, you may use the 'pytest' utility from logilab-common (simply
-type 'pytest' from within this directory) or using unittest discover::
+at once, you can use unittest discover::
python -m unittest discover -p "unittest*.py"
diff --git a/astroid/__init__.py b/astroid/__init__.py
index 3a8fdc01..25f00bc4 100644
--- a/astroid/__init__.py
+++ b/astroid/__init__.py
@@ -60,10 +60,11 @@ from astroid import inference
# more stuff available
from astroid import raw_building
-from astroid.bases import YES, Instance, BoundMethod, UnboundMethod
+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 YES
# make a manager instance (borg) accessible from astroid package
from astroid.manager import AstroidManager
diff --git a/astroid/__pkginfo__.py b/astroid/__pkginfo__.py
index 9c184e6d..f70df7ce 100644
--- a/astroid/__pkginfo__.py
+++ b/astroid/__pkginfo__.py
@@ -23,7 +23,7 @@ modname = 'astroid'
numversion = (1, 3, 6)
version = '.'.join([str(num) for num in numversion])
-install_requires = ['logilab-common>=0.63.0', 'six']
+install_requires = ['six']
license = 'LGPL'
diff --git a/astroid/as_string.py b/astroid/as_string.py
index ecada1e1..cc0051cf 100644
--- a/astroid/as_string.py
+++ b/astroid/as_string.py
@@ -136,8 +136,14 @@ class AsStringVisitor(object):
"""return an astroid.CallFunc node as string"""
expr_str = node.func.accept(self)
args = [arg.accept(self) for arg in node.args]
+ if node.keywords:
+ keywords = [kwarg.accept(self) for kwarg in node.keywords]
+ else:
+ keywords = []
+
if node.starargs:
args.append('*' + node.starargs.accept(self))
+ args.extend(keywords)
if node.kwargs:
args.append('**' + node.kwargs.accept(self))
return '%s(%s)' % (expr_str, ', '.join(args))
diff --git a/astroid/bases.py b/astroid/bases.py
index 9b4c2fe1..98c8149e 100644
--- a/astroid/bases.py
+++ b/astroid/bases.py
@@ -18,16 +18,14 @@
"""This module contains base classes and functions for the nodes and some
inference utils.
"""
-__docformat__ = "restructuredtext en"
import sys
import warnings
-from contextlib import contextmanager
-from logilab.common.decorators import cachedproperty
-
-from astroid.exceptions import (InferenceError, AstroidError, NotFoundError,
- UnresolvableName, UseInferenceDefault)
+from astroid import context as contextmod
+from astroid import decorators as decoratorsmod
+from astroid import exceptions
+from astroid import util
if sys.version_info >= (3, 0):
@@ -56,7 +54,7 @@ def _is_property(meth):
if PROPERTIES.intersection(meth.decoratornames()):
return True
stripped = {name.split(".")[-1] for name in meth.decoratornames()
- if name is not YES}
+ if name is not util.YES}
return any(name in stripped for name in POSSIBLE_PROPERTIES)
@@ -80,51 +78,6 @@ class Proxy(object):
yield self
-class InferenceContext(object):
- __slots__ = ('path', 'lookupname', 'callcontext', 'boundnode', 'inferred')
-
- def __init__(self, path=None, inferred=None):
- self.path = path or set()
- self.lookupname = None
- self.callcontext = None
- self.boundnode = None
- self.inferred = inferred or {}
-
- def push(self, node):
- name = self.lookupname
- if (node, name) in self.path:
- raise StopIteration()
- self.path.add((node, name))
-
- def clone(self):
- # XXX copy lookupname/callcontext ?
- clone = InferenceContext(self.path, inferred=self.inferred)
- clone.callcontext = self.callcontext
- clone.boundnode = self.boundnode
- return clone
-
- def cache_generator(self, key, generator):
- results = []
- for result in generator:
- results.append(result)
- yield result
-
- self.inferred[key] = tuple(results)
- return
-
- @contextmanager
- def restore_path(self):
- path = set(self.path)
- yield
- self.path = path
-
-def copy_context(context):
- if context is not None:
- return context.clone()
- else:
- return InferenceContext()
-
-
def _infer_stmts(stmts, context, frame=None):
"""Return an iterator on statements inferred by each statement in *stmts*."""
stmt = None
@@ -134,10 +87,10 @@ def _infer_stmts(stmts, context, frame=None):
context = context.clone()
else:
name = None
- context = InferenceContext()
+ context = contextmod.InferenceContext()
for stmt in stmts:
- if stmt is YES:
+ if stmt is util.YES:
yield stmt
inferred = True
continue
@@ -146,32 +99,13 @@ def _infer_stmts(stmts, context, frame=None):
for inferred in stmt.infer(context=context):
yield inferred
inferred = True
- except UnresolvableName:
+ except excpetions.UnresolvableName:
continue
- except InferenceError:
- yield YES
+ except exceptions.InferenceError:
+ yield util.YES
inferred = True
if not inferred:
- raise InferenceError(str(stmt))
-
-
-class _Yes(object):
- """Special inference object, which is returned when inference fails."""
- def __repr__(self):
- return 'YES'
-
- def __getattribute__(self, name):
- if name == 'next':
- raise AttributeError('next method should not be called')
- if name.startswith('__') and name.endswith('__'):
- return super(_Yes, self).__getattribute__(name)
- return self
-
- def __call__(self, *args, **kwargs):
- return self
-
-
-YES = _Yes()
+ raise exceptions.InferenceError(str(stmt))
def _infer_method_result_truth(instance, method_name, context):
@@ -180,12 +114,12 @@ def _infer_method_result_truth(instance, method_name, context):
meth = next(instance.igetattr(method_name, context=context), None)
if meth and hasattr(meth, 'infer_call_result'):
for value in meth.infer_call_result(instance, context=context):
- if value is YES:
+ if value is util.YES:
return value
inferred = next(value.infer(context=context))
return inferred.bool_value()
- return YES
+ return util.YES
class Instance(Proxy):
@@ -194,7 +128,7 @@ class Instance(Proxy):
def getattr(self, name, context=None, lookupclass=True):
try:
values = self._proxied.instance_attr(name, context)
- except NotFoundError:
+ except exceptions.NotFoundError:
if name == '__class__':
return [self._proxied]
if lookupclass:
@@ -202,21 +136,23 @@ class Instance(Proxy):
# unless they are explicitly defined.
if name in ('__name__', '__bases__', '__mro__', '__subclasses__'):
return self._proxied.local_attr(name)
- return self._proxied.getattr(name, context)
- raise NotFoundError(name)
+ return self._proxied.getattr(name, context,
+ class_context=False)
+ raise exceptions.NotFoundError(name)
# since we've no context information, return matching class members as
# well
if lookupclass:
try:
- return values + self._proxied.getattr(name, context)
- except NotFoundError:
+ return values + self._proxied.getattr(name, context,
+ class_context=False)
+ except exceptions.NotFoundError:
pass
return values
def igetattr(self, name, context=None):
"""inferred getattr"""
if not context:
- context = InferenceContext()
+ context = contextmod.InferenceContext()
try:
# avoid recursively inferring the same attr on the same class
context.push((self._proxied, name))
@@ -227,14 +163,14 @@ class Instance(Proxy):
context,
frame=self,
)
- except NotFoundError:
+ except exceptions.NotFoundError:
try:
# fallback to class'igetattr since it has some logic to handle
# descriptors
return self._wrap_attr(self._proxied.igetattr(name, context),
context)
- except NotFoundError:
- raise InferenceError(name)
+ except exceptions.NotFoundError:
+ raise exceptions.InferenceError(name)
def _wrap_attr(self, attrs, context=None):
"""wrap bound methods of attrs in a InstanceMethod proxies"""
@@ -263,13 +199,13 @@ class Instance(Proxy):
"""infer what a class instance is returning when called"""
inferred = False
for node in self._proxied.igetattr('__call__', context):
- if node is YES:
+ if node is util.YES:
continue
for res in node.infer_call_result(caller, context):
inferred = True
yield res
if not inferred:
- raise InferenceError()
+ raise exceptions.InferenceError()
def __repr__(self):
return '<Instance of %s.%s at 0x%s>' % (self._proxied.root().name,
@@ -281,9 +217,9 @@ class Instance(Proxy):
def callable(self):
try:
- self._proxied.getattr('__call__')
+ self._proxied.getattr('__call__', class_context=False)
return True
- except NotFoundError:
+ except exceptions.NotFoundError:
return False
def pytype(self):
@@ -305,14 +241,14 @@ class Instance(Proxy):
nonzero. If a class defines neither __len__() nor __bool__(),
all its instances are considered true.
"""
- context = InferenceContext()
+ context = contextmod.InferenceContext()
try:
result = _infer_method_result_truth(self, BOOL_SPECIAL_METHOD, context)
- except (InferenceError, NotFoundError):
+ except (exceptions.InferenceError, exceptions.NotFoundError):
# Fallback to __len__.
try:
result = _infer_method_result_truth(self, '__len__', context)
- except (NotFoundError, InferenceError):
+ except (exceptions.NotFoundError, exceptions.InferenceError):
return True
return result
@@ -351,7 +287,7 @@ class UnboundMethod(Proxy):
if (self._proxied.name == '__new__' and
self._proxied.parent.frame().qname() == '%s.object' % BUILTINS):
infer = caller.args[0].infer() if caller.args else []
- return ((x is YES and x or Instance(x)) for x in infer)
+ return ((x is util.YES and x or Instance(x)) for x in infer)
return self._proxied.infer_call_result(caller, context)
def bool_value(self):
@@ -370,7 +306,7 @@ class BoundMethod(UnboundMethod):
def infer_call_result(self, caller, context):
context = context.clone()
context.boundnode = self.bound
- return self._proxied.infer_call_result(caller, context)
+ return super(BoundMethod, self).infer_call_result(caller, context)
def bool_value(self):
return True
@@ -407,7 +343,7 @@ def path_wrapper(func):
def wrapped(node, context=None, _func=func, **kwargs):
"""wrapper function handling context"""
if context is None:
- context = InferenceContext()
+ context = contextmod.InferenceContext()
context.push(node)
yielded = set()
for res in _func(node, context, **kwargs):
@@ -416,7 +352,7 @@ def path_wrapper(func):
ares = res._proxied
else:
ares = res
- if not ares in yielded:
+ if ares not in yielded:
yield res
yielded.add(ares)
return wrapped
@@ -428,7 +364,7 @@ def yes_if_nothing_inferred(func):
inferred = True
yield node
if not inferred:
- yield YES
+ yield util.YES
return wrapper
def raise_if_nothing_inferred(func):
@@ -438,7 +374,7 @@ def raise_if_nothing_inferred(func):
inferred = True
yield node
if not inferred:
- raise InferenceError()
+ raise exceptions.InferenceError()
return wrapper
@@ -479,7 +415,7 @@ class NodeNG(object):
try:
# pylint: disable=not-callable
return self._explicit_inference(self, context, **kwargs)
- except UseInferenceDefault:
+ except exceptions.UseInferenceDefault:
pass
if not context:
@@ -578,7 +514,7 @@ class NodeNG(object):
return node_or_sequence
msg = 'Could not find %s in %s\'s children'
- raise AstroidError(msg % (repr(child), repr(self)))
+ raise exceptions.AstroidError(msg % (repr(child), repr(self)))
def locate_child(self, child):
"""return a 2-uple (child attribute name, sequence or node)"""
@@ -590,7 +526,7 @@ class NodeNG(object):
if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence:
return field, node_or_sequence
msg = 'Could not find %s in %s\'s children'
- raise AstroidError(msg % (repr(child), repr(self)))
+ raise exceptions.AstroidError(msg % (repr(child), repr(self)))
# FIXME : should we merge child_sequence and locate_child ? locate_child
# is only used in are_exclusive, child_sequence one time in pylint.
@@ -623,14 +559,14 @@ class NodeNG(object):
# these are lazy because they're relatively expensive to compute for every
# single node, and they rarely get looked at
- @cachedproperty
+ @decoratorsmod.cachedproperty
def fromlineno(self):
if self.lineno is None:
return self._fixed_source_line()
else:
return self.lineno
- @cachedproperty
+ @decoratorsmod.cachedproperty
def tolineno(self):
if not self._astroid_fields:
# can't have children
@@ -694,7 +630,7 @@ class NodeNG(object):
def _infer(self, context=None):
"""we don't know how to resolve a statement by default"""
# this method is overridden by most concrete classes
- raise InferenceError(self.__class__.__name__)
+ raise exceptions.InferenceError(self.__class__.__name__)
def inferred(self):
'''return list of inferred values for a more simple inference usage'''
@@ -742,7 +678,7 @@ class NodeNG(object):
* YES: the inference engine is uncertain of the
node's value.
"""
- return YES
+ return util.YES
class Statement(NodeNG):
diff --git a/astroid/brain/builtin_inference.py b/astroid/brain/builtin_inference.py
index 0d713845..33f086f0 100644
--- a/astroid/brain/builtin_inference.py
+++ b/astroid/brain/builtin_inference.py
@@ -1,15 +1,18 @@
"""Astroid hooks for various builtins."""
+
from functools import partial
import sys
from textwrap import dedent
import six
from astroid import (MANAGER, UseInferenceDefault, NotFoundError,
- inference_tip, YES, InferenceError, UnresolvableName)
+ inference_tip, InferenceError, UnresolvableName)
from astroid.builder import AstroidBuilder
from astroid import helpers
from astroid import nodes
from astroid import objects
+from astroid import scoped_nodes
+from astroid import util
def _extend_str(class_node, rvalue):
"""function to extend builtin str/unicode class"""
@@ -116,10 +119,10 @@ def _generic_inference(node, context, node_type, transform):
inferred = next(arg.infer(context=context))
except (InferenceError, StopIteration):
raise UseInferenceDefault()
- if inferred is YES:
+ if infered is util.YES:
raise UseInferenceDefault()
transformed = transform(inferred)
- if not transformed or transformed is YES:
+ if not transformed or transformed is util.YES:
raise UseInferenceDefault()
return transformed
@@ -226,19 +229,16 @@ def infer_dict(node, context=None):
If a case can't be inferred, we'll fallback to default inference.
"""
- has_keywords = lambda args: all(isinstance(arg, nodes.Keyword)
- for arg in args)
- if not node.args and not node.kwargs:
+ if not node.args and not node.kwargs and not node.keywords:
# dict()
return nodes.Dict()
- elif has_keywords(node.args) and node.args:
+ elif node.keywords and not node.args:
# dict(a=1, b=2, c=4)
- items = [(nodes.Const(arg.arg), arg.value) for arg in node.args]
- elif (len(node.args) >= 2 and
- has_keywords(node.args[1:])):
+ items = [(nodes.Const(arg.arg), arg.value) for arg in node.keywords]
+ elif len(node.args) == 1 and node.keywords:
# dict(some_iterable, b=2, c=4)
elts = _get_elts(node.args[0], context)
- keys = [(nodes.Const(arg.arg), arg.value) for arg in node.args[1:]]
+ keys = [(nodes.Const(arg.arg), arg.value) for arg in node.keywords]
items = elts + keys
elif len(node.args) == 1:
items = _get_elts(node.args[0], context)
@@ -250,16 +250,6 @@ def infer_dict(node, context=None):
return empty
-def _node_class(node):
- klass = node.frame()
- while klass is not None and not isinstance(klass, nodes.ClassDef):
- if klass.parent is None:
- klass = None
- else:
- klass = klass.parent.frame()
- return klass
-
-
def infer_super(node, context=None):
"""Understand super calls.
@@ -285,7 +275,7 @@ def infer_super(node, context=None):
# Not interested in staticmethods.
raise UseInferenceDefault
- cls = _node_class(scope)
+ cls = scoped_nodes.get_wrapping_class(scope)
if not len(node.args):
mro_pointer = cls
# In we are in a classmethod, the interpreter will fill
@@ -305,7 +295,7 @@ def infer_super(node, context=None):
except InferenceError:
raise UseInferenceDefault
- if mro_pointer is YES or mro_type is YES:
+ if mro_pointer is util.YES or mro_type is util.YES:
# No way we could understand this.
raise UseInferenceDefault
@@ -329,11 +319,11 @@ def _infer_getattr_args(node, context):
except InferenceError:
raise UseInferenceDefault
- if obj is YES or attr is YES:
+ if obj is util.YES or attr is util.YES:
# If one of the arguments is something we can't infer,
# then also make the result of the getattr call something
# which is unknown.
- return YES, YES
+ return util.YES, util.YES
is_string = (isinstance(attr, nodes.Const) and
isinstance(attr.value, six.string_types))
@@ -351,8 +341,8 @@ def infer_getattr(node, context=None):
lookup will be done.
"""
obj, attr = _infer_getattr_args(node, context)
- if obj is YES or attr is YES:
- return YES
+ if obj is util.YES or attr is util.YES:
+ return util.YES
try:
return next(obj.igetattr(attr, context=context))
@@ -378,12 +368,12 @@ def infer_hasattr(node, context=None):
"""
try:
obj, attr = _infer_getattr_args(node, context)
- if obj is YES or attr is YES:
- return YES
+ if obj is util.YES or attr is util.YES:
+ return util.YES
obj.getattr(attr, context=context)
except UseInferenceDefault:
# Can't infer something from this function call.
- return YES
+ return util.YES
except NotFoundError:
# Doesn't have it.
return nodes.Const(False)
@@ -406,9 +396,9 @@ def infer_callable(node, context=None):
try:
inferred = next(argument.infer(context=context))
except InferenceError:
- return YES
- if inferred is YES:
- return YES
+ return util.YES
+ if inferred is util.YES:
+ return util.YES
return nodes.Const(inferred.callable())
@@ -425,13 +415,13 @@ def infer_bool(node, context=None):
try:
inferred = next(argument.infer(context=context))
except InferenceError:
- return YES
- if inferred is YES:
- return YES
+ return util.YES
+ if inferred is util.YES:
+ return util.YES
bool_value = inferred.bool_value()
- if bool_value is YES:
- return YES
+ if bool_value is util.YES:
+ return util.YES
return nodes.Const(bool_value)
diff --git a/astroid/brain/py2stdlib.py b/astroid/brain/py2stdlib.py
index b601a825..b742823c 100644
--- a/astroid/brain/py2stdlib.py
+++ b/astroid/brain/py2stdlib.py
@@ -12,10 +12,11 @@ from textwrap import dedent
from astroid import (
MANAGER, AsStringRegexpPredicate,
UseInferenceDefault, inference_tip, BoundMethod,
- YES, InferenceError, register_module_extender)
+ InferenceError, register_module_extender)
from astroid import exceptions
from astroid import nodes
from astroid.builder import AstroidBuilder
+from astroid import util
PY3K = sys.version_info > (3, 0)
PY33 = sys.version_info >= (3, 3)
@@ -28,7 +29,7 @@ def infer_func_form(node, base_type, context=None, enum=False):
def infer_first(node):
try:
value = next(node.infer(context=context))
- if value is YES:
+ if value is util.YES:
raise UseInferenceDefault()
else:
return value
@@ -190,6 +191,7 @@ def cleanup_resources(force=False):
def subprocess_transform():
if PY3K:
communicate = (bytes('string', 'ascii'), bytes('string', 'ascii'))
+ communicate_signature = 'def communicate(self, input=None, timeout=None)'
init = """
def __init__(self, args, bufsize=0, executable=None,
stdin=None, stdout=None, stderr=None,
@@ -201,6 +203,7 @@ def subprocess_transform():
"""
else:
communicate = ('string', 'string')
+ communicate_signature = 'def communicate(self, input=None)'
init = """
def __init__(self, args, bufsize=0, executable=None,
stdin=None, stdout=None, stderr=None,
@@ -228,7 +231,7 @@ def subprocess_transform():
%(init)s
- def communicate(self, input=None):
+ %(communicate_signature)s:
return %(communicate)r
%(wait_signature)s:
return self.returncode
@@ -243,6 +246,7 @@ def subprocess_transform():
%(ctx_manager)s
''' % {'init': init,
'communicate': communicate,
+ 'communicate_signature': communicate_signature,
'wait_signature': wait_signature,
'ctx_manager': ctx_manager})
return AstroidBuilder(MANAGER).string_build(code)
diff --git a/astroid/builder.py b/astroid/builder.py
index 8756fa42..fcd9892f 100644
--- a/astroid/builder.py
+++ b/astroid/builder.py
@@ -33,6 +33,7 @@ from astroid import manager
from astroid import modutils
from astroid import raw_building
from astroid import rebuilder
+from astroid import util
def _parse(string):
@@ -83,11 +84,19 @@ MANAGER = manager.AstroidManager()
class AstroidBuilder(raw_building.InspectBuilder):
- """Class for building an astroid tree from source code or from a live module."""
+ """Class for building an astroid tree from source code or from a live module.
- def __init__(self, manager=None):
+ The param *manager* specifies the manager class which should be used.
+ If no manager is given, then the default one will be used. The
+ param *apply_transforms* determines if the transforms should be
+ applied after the tree was built from source or from a live object,
+ by default being True.
+ """
+
+ def __init__(self, manager=None, apply_transforms=True):
super(AstroidBuilder, self).__init__()
self._manager = manager or MANAGER
+ self._apply_transforms = apply_transforms
def module_build(self, module, modname=None):
"""Build an astroid from a living module instance."""
@@ -101,12 +110,10 @@ class AstroidBuilder(raw_building.InspectBuilder):
# this is a built-in module
# get a partial representation by introspection
node = self.inspect_build(module, modname=modname, path=path)
- # we have to handle transformation by ourselves since the rebuilder
- # isn't called for builtin nodes
- #
- # XXX it's then only called for Module nodes, not for underlying
- # nodes
- node = self._manager.transform(node)
+ if self._apply_transforms:
+ # We have to handle transformation by ourselves since the
+ # rebuilder isn't called for builtin nodes
+ node = self._manager.visit_transforms(node)
return node
def file_build(self, path, modname=None):
@@ -153,6 +160,10 @@ class AstroidBuilder(raw_building.InspectBuilder):
# handle delayed assattr nodes
for delayed in module._delayed_assattr:
self.delayed_assattr(delayed)
+
+ # Visit the transforms
+ if self._apply_transforms:
+ module = self._manager.visit_transforms(module)
return module
def _data_build(self, data, modname, path):
@@ -206,7 +217,7 @@ class AstroidBuilder(raw_building.InspectBuilder):
try:
frame = node.frame()
for inferred in node.expr.infer():
- if inferred is bases.YES:
+ if inferred is util.YES:
continue
try:
if inferred.__class__ is bases.Instance:
@@ -236,13 +247,17 @@ class AstroidBuilder(raw_building.InspectBuilder):
pass
-def parse(code, module_name='', path=None):
+def parse(code, module_name='', path=None, apply_transforms=True):
"""Parses a source string in order to obtain an astroid AST from it
:param str code: The code for the module.
:param str module_name: The name for the module, if any
:param str path: The path for the module
+ :param bool apply_transforms:
+ Apply the transforms for the give code. Use it if you
+ don't want the default transforms to be applied.
"""
code = textwrap.dedent(code)
- return AstroidBuilder(MANAGER).string_build(
- code, modname=module_name, path=path)
+ builder = AstroidBuilder(manager=MANAGER,
+ apply_transforms=apply_transforms)
+ return builder.string_build(code, modname=module_name, path=path)
diff --git a/astroid/context.py b/astroid/context.py
new file mode 100644
index 00000000..3e420d76
--- /dev/null
+++ b/astroid/context.py
@@ -0,0 +1,141 @@
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of astroid.
+#
+# astroid is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 2.1 of the License, or (at your
+# option) any later version.
+#
+# astroid is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+
+"""Various context related utilities, including inference and call contexts."""
+
+import contextlib
+import itertools
+
+from astroid import exceptions
+from astroid import util
+
+
+class InferenceContext(object):
+ __slots__ = ('path', 'lookupname', 'callcontext', 'boundnode', 'infered')
+
+ def __init__(self, path=None, infered=None):
+ self.path = path or set()
+ self.lookupname = None
+ self.callcontext = None
+ self.boundnode = None
+ self.infered = infered or {}
+
+ def push(self, node):
+ name = self.lookupname
+ if (node, name) in self.path:
+ raise StopIteration()
+ self.path.add((node, name))
+
+ def clone(self):
+ # XXX copy lookupname/callcontext ?
+ clone = InferenceContext(self.path, infered=self.infered)
+ clone.callcontext = self.callcontext
+ clone.boundnode = self.boundnode
+ return clone
+
+ def cache_generator(self, key, generator):
+ results = []
+ for result in generator:
+ results.append(result)
+ yield result
+
+ self.infered[key] = tuple(results)
+ return
+
+ @contextlib.contextmanager
+ def restore_path(self):
+ path = set(self.path)
+ yield
+ self.path = path
+
+
+class CallContext(object):
+
+ def __init__(self, args, keywords=None, starargs=None, kwargs=None):
+ self.args = args
+ if keywords:
+ self.keywords = {arg.arg: arg.value for arg in keywords}
+ else:
+ self.keywords = {}
+
+ self.starargs = starargs
+ self.kwargs = kwargs
+
+ @staticmethod
+ def _infer_argument_container(container, key, context):
+ its = []
+ for infered in container.infer(context=context):
+ if infered is util.YES:
+ its.append((util.YES,))
+ continue
+ try:
+ its.append(infered.getitem(key, context).infer(context=context))
+ except (exceptions.InferenceError, AttributeError):
+ its.append((util.YES,))
+ except (IndexError, TypeError):
+ continue
+ if its:
+ return itertools.chain(*its)
+
+ def infer_argument(self, funcnode, name, context, boundnode):
+ """infer a function argument value according to the call context"""
+ # 1. search in named keywords
+ try:
+ return self.keywords[name].infer(context)
+ except KeyError:
+ pass
+
+ argindex = funcnode.args.find_argname(name)[0]
+ if argindex is not None:
+ # 2. first argument of instance/class method
+ if argindex == 0 and funcnode.type in ('method', 'classmethod'):
+ return iter((boundnode,))
+ # if we have a method, extract one position
+ # from the index, so we'll take in account
+ # the extra parameter represented by `self` or `cls`
+ if funcnode.type in ('method', 'classmethod'):
+ argindex -= 1
+ # 2. search arg index
+ try:
+ return self.args[argindex].infer(context)
+ except IndexError:
+ pass
+ # 3. search in *args (.starargs)
+ if self.starargs is not None:
+ its = self._infer_argument_container(
+ self.starargs, argindex, context)
+ if its:
+ return its
+ # 4. Search in **kwargs
+ if self.kwargs is not None:
+ its = self._infer_argument_container(
+ self.kwargs, name, context)
+ if its:
+ return its
+ # 5. return default value if any
+ try:
+ return funcnode.args.default_value(name).infer(context)
+ except exceptions.NoDefault:
+ raise exceptions.InferenceError(name)
+
+
+def copy_context(context):
+ if context is not None:
+ return context.clone()
+ else:
+ return InferenceContext()
diff --git a/astroid/decorators.py b/astroid/decorators.py
new file mode 100644
index 00000000..4bc7ff17
--- /dev/null
+++ b/astroid/decorators.py
@@ -0,0 +1,79 @@
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of astroid.
+#
+# astroid is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 2.1 of the License, or (at your
+# option) any later version.
+#
+# astroid is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+#
+# The code in this file was originally part of logilab-common, licensed under
+# the same license.
+
+""" A few useful function/method decorators."""
+
+import functools
+
+
+def cached(func):
+ """Simple decorator to cache result of method calls without args."""
+
+ @functools.wraps(func)
+ def wrapped(wrapped_self):
+ cache = getattr(wrapped_self, '__cache', None)
+ if cache is None:
+ wrapped_self.__cache = cache = {}
+ try:
+ return cache[func]
+ except KeyError:
+ cache[func] = result = func(wrapped_self)
+ return result
+
+ return wrapped
+
+
+class cachedproperty(object):
+ """ Provides a cached property equivalent to the stacking of
+ @cached and @property, but more efficient.
+
+ After first usage, the <property_name> becomes part of the object's
+ __dict__. Doing:
+
+ del obj.<property_name> empties the cache.
+
+ Idea taken from the pyramid_ framework and the mercurial_ project.
+
+ .. _pyramid: http://pypi.python.org/pypi/pyramid
+ .. _mercurial: http://pypi.python.org/pypi/Mercurial
+ """
+ __slots__ = ('wrapped',)
+
+ def __init__(self, wrapped):
+ try:
+ wrapped.__name__
+ except AttributeError:
+ raise TypeError('%s must have a __name__ attribute' %
+ wrapped)
+ self.wrapped = wrapped
+
+ @property
+ def __doc__(self):
+ doc = getattr(self.wrapped, '__doc__', None)
+ return ('<wrapped by the cachedproperty decorator>%s'
+ % ('\n%s' % doc if doc else ''))
+
+ def __get__(self, inst, objtype=None):
+ if inst is None:
+ return self
+ val = self.wrapped(inst)
+ setattr(inst, self.wrapped.__name__, val)
+ return val
diff --git a/astroid/helpers.py b/astroid/helpers.py
index c22bd9a1..26844082 100644
--- a/astroid/helpers.py
+++ b/astroid/helpers.py
@@ -23,10 +23,12 @@ Various helper utilities.
import six
from astroid import bases
+from astroid import context as contextmod
from astroid import exceptions
from astroid import manager
from astroid import raw_building
from astroid import scoped_nodes
+from astroid import util
BUILTINS = six.moves.builtins.__name__
@@ -60,7 +62,7 @@ def _function_type(function, builtins):
def _object_type(node, context=None):
astroid_manager = manager.AstroidManager()
builtins = astroid_manager.astroid_cache[BUILTINS]
- context = context or bases.InferenceContext()
+ context = context or contextmod.InferenceContext()
for inferred in node.infer(context=context):
if isinstance(inferred, scoped_nodes.ClassDef):
@@ -89,9 +91,9 @@ def object_type(node, context=None):
try:
types = set(_object_type(node, context))
except exceptions.InferenceError:
- return bases.YES
- if len(types) > 1:
- return bases.YES
+ return util.YES
+ if len(types) > 1 or not types:
+ return util.YES
return list(types)[0]
@@ -124,7 +126,7 @@ def has_known_bases(klass, context=None):
for base in klass.bases:
result = safe_infer(base, context=context)
# TODO: check for A->B->A->B pattern in class structure too?
- if (not isinstance(result, scoped_nodes.ClassDef) or
+ if (not isinstance(result, scoped_nodes.Class) or
result is klass or
not has_known_bases(result, context=context)):
klass._all_bases_known = False
@@ -135,7 +137,7 @@ def has_known_bases(klass, context=None):
def _type_check(type1, type2):
if not all(map(has_known_bases, (type1, type2))):
- return bases.YES
+ return util.YES
if not all([type1.newstyle, type2.newstyle]):
return False
@@ -143,7 +145,7 @@ def _type_check(type1, type2):
return type1 in type2.mro()[:-1]
except exceptions.MroError:
# The MRO is invalid.
- return bases.YES
+ return util.YES
def is_subtype(type1, type2):
diff --git a/astroid/inference.py b/astroid/inference.py
index 30e02288..05cbe5eb 100644
--- a/astroid/inference.py
+++ b/astroid/inference.py
@@ -22,111 +22,17 @@ import functools
import itertools
import operator
+from astroid import bases
+from astroid import context as contextmod
+from astroid import exceptions
from astroid import helpers
+from astroid import manager
from astroid import nodes
from astroid import protocols
-from astroid.manager import AstroidManager
-from astroid.exceptions import (
- AstroidError, InferenceError, NoDefault,
- NotFoundError, UnresolvableName,
- UnaryOperationError,
- BinaryOperationError,
-)
-from astroid.bases import (YES, Instance, InferenceContext, BoundMethod,
- _infer_stmts, copy_context, path_wrapper,
- raise_if_nothing_inferred, yes_if_nothing_inferred)
-
-MANAGER = AstroidManager()
-
-
-class CallContext(object):
- """when inferring a function call, this class is used to remember values
- given as argument
- """
- def __init__(self, args, starargs, dstarargs):
- self.args = []
- self.nargs = {}
- for arg in args:
- if isinstance(arg, nodes.Keyword):
- self.nargs[arg.arg] = arg.value
- else:
- self.args.append(arg)
- self.starargs = starargs
- self.dstarargs = dstarargs
+from astroid import util
- def infer_argument(self, funcnode, name, context):
- """infer a function argument value according to the call context"""
- # 1. search in named keywords
- try:
- return self.nargs[name].infer(context)
- except KeyError:
- # Function.args.args can be None in astroid (means that we don't have
- # information on argnames)
- argindex = funcnode.args.find_argname(name)[0]
- if argindex is not None:
- # 2. first argument of instance/class method
- if argindex == 0 and funcnode.type in ('method', 'classmethod'):
- if context.boundnode is not None:
- boundnode = context.boundnode
- else:
- # XXX can do better ?
- boundnode = funcnode.parent.frame()
- if funcnode.type == 'method':
- if not isinstance(boundnode, Instance):
- boundnode = Instance(boundnode)
- return iter((boundnode,))
- if funcnode.type == 'classmethod':
- return iter((boundnode,))
- # if we have a method, extract one position
- # from the index, so we'll take in account
- # the extra parameter represented by `self` or `cls`
- if funcnode.type in ('method', 'classmethod'):
- argindex -= 1
- # 2. search arg index
- try:
- return self.args[argindex].infer(context)
- except IndexError:
- pass
- # 3. search in *args (.starargs)
- if self.starargs is not None:
- its = []
- for inferred in self.starargs.infer(context):
- if inferred is YES:
- its.append((YES,))
- continue
- try:
- its.append(inferred.getitem(argindex, context).infer(context))
- except (InferenceError, AttributeError):
- its.append((YES,))
- except (IndexError, TypeError):
- continue
- if its:
- return itertools.chain(*its)
- # 4. XXX search in **kwargs (.dstarargs)
- if self.dstarargs is not None:
- its = []
- for inferred in self.dstarargs.infer(context):
- if inferred is YES:
- its.append((YES,))
- continue
- try:
- its.append(inferred.getitem(name, context).infer(context))
- except (InferenceError, AttributeError):
- its.append((YES,))
- except (IndexError, TypeError):
- continue
- if its:
- return itertools.chain(*its)
- # 5. */** argument, (Tuple or Dict)
- if name == funcnode.args.vararg:
- return iter((nodes.const_factory(())))
- if name == funcnode.args.kwarg:
- return iter((nodes.const_factory({})))
- # 6. return default value if any
- try:
- return funcnode.args.default_value(name).infer(context)
- except NoDefault:
- raise InferenceError(name)
+
+MANAGER = manager.AstroidManager()
# .infer method ###############################################################
@@ -176,72 +82,82 @@ def infer_name(self, context=None):
_, stmts = parent_function.lookup(self.name)
if not stmts:
- raise UnresolvableName(self.name)
+ raise exceptions.UnresolvableName(self.name)
context = context.clone()
context.lookupname = self.name
- return _infer_stmts(stmts, context, frame)
-nodes.Name._infer = path_wrapper(infer_name)
+ return bases._infer_stmts(stmts, context, frame)
+nodes.Name._infer = bases.path_wrapper(infer_name)
nodes.AssignName.infer_lhs = infer_name # won't work with a path wrapper
+@bases.raise_if_nothing_infered
+@bases.path_wrapper
def infer_callfunc(self, context=None):
"""infer a CallFunc node by trying to guess what the function returns"""
callcontext = context.clone()
- callcontext.callcontext = CallContext(self.args, self.starargs, self.kwargs)
+ callcontext.callcontext = contextmod.CallContext(args=self.args,
+ keywords=self.keywords,
+ starargs=self.starargs,
+ kwargs=self.kwargs)
callcontext.boundnode = None
for callee in self.func.infer(context):
- if callee is YES:
+ if callee is util.YES:
yield callee
continue
try:
if hasattr(callee, 'infer_call_result'):
for inferred in callee.infer_call_result(self, callcontext):
yield inferred
- except InferenceError:
+ except exceptions.InferenceError:
## XXX log error ?
continue
-nodes.Call._infer = path_wrapper(raise_if_nothing_inferred(infer_callfunc))
+nodes.Call._infer = infer_callfunc
+@bases.path_wrapper
def infer_import(self, context=None, asname=True):
"""infer an Import node: return the imported module/object"""
name = context.lookupname
if name is None:
- raise InferenceError()
+ raise exceptions.InferenceError()
if asname:
yield self.do_import_module(self.real_name(name))
else:
yield self.do_import_module(name)
-nodes.Import._infer = path_wrapper(infer_import)
+nodes.Import._infer = infer_import
+
def infer_name_module(self, name):
- context = InferenceContext()
+ context = contextmod.InferenceContext()
context.lookupname = name
return self.infer(context, asname=False)
nodes.Import.infer_name_module = infer_name_module
+@bases.path_wrapper
def infer_from(self, context=None, asname=True):
"""infer a From nodes: return the imported module/object"""
name = context.lookupname
if name is None:
- raise InferenceError()
+ raise exceptions.InferenceError()
if asname:
name = self.real_name(name)
module = self.do_import_module()
try:
- context = copy_context(context)
+ context = contextmod.copy_context(context)
context.lookupname = name
- return _infer_stmts(module.getattr(name, ignore_locals=module is self.root()), context)
- except NotFoundError:
- raise InferenceError(name)
-nodes.ImportFrom._infer = path_wrapper(infer_from)
+ stmts = module.getattr(name, ignore_locals=module is self.root())
+ return bases._infer_stmts(stmts, context)
+ except exceptions.NotFoundError:
+ raise exceptions.InferenceError(name)
+nodes.ImportFrom._infer = infer_from
+@bases.raise_if_nothing_infered
def infer_getattr(self, context=None):
"""infer a Getattr node by using getattr on the associated object"""
for owner in self.expr.infer(context):
- if owner is YES:
+ if owner is util.YES:
yield owner
continue
try:
@@ -249,61 +165,63 @@ def infer_getattr(self, context=None):
for obj in owner.igetattr(self.attrname, context):
yield obj
context.boundnode = None
- except (NotFoundError, InferenceError):
+ except (exceptions.NotFoundError, exceptions.InferenceError):
context.boundnode = None
except AttributeError:
# XXX method / function
context.boundnode = None
-nodes.Attribute._infer = path_wrapper(raise_if_nothing_inferred(infer_getattr))
-nodes.AssignAttr.infer_lhs = raise_if_nothing_inferred(infer_getattr) # # won't work with a path wrapper
+nodes.Attribute._infer = bases.path_wrapper(infer_getattr)
+nodes.AssignAttr.infer_lhs = infer_getattr # # won't work with a path wrapper
+@bases.path_wrapper
def infer_global(self, context=None):
if context.lookupname is None:
- raise InferenceError()
+ raise exceptions.InferenceError()
try:
- return _infer_stmts(self.root().getattr(context.lookupname), context)
- except NotFoundError:
- raise InferenceError()
-nodes.Global._infer = path_wrapper(infer_global)
+ return bases._infer_stmts(self.root().getattr(context.lookupname),
+ context)
+ except exceptions.NotFoundError:
+ raise exceptions.InferenceError()
+nodes.Global._infer = infer_global
def infer_subscript(self, context=None):
"""infer simple subscription such as [1,2,3][0] or (1,2,3)[-1]"""
value = next(self.value.infer(context))
- if value is YES:
- yield YES
+ if value is util.YES:
+ yield util.YES
return
index = next(self.slice.infer(context))
- if index is YES:
- yield YES
+ if index is util.YES:
+ yield util.YES
return
if isinstance(index, nodes.Const):
try:
assigned = value.getitem(index.value, context)
except AttributeError:
- raise InferenceError()
+ raise exceptions.InferenceError()
except (IndexError, TypeError):
- yield YES
+ yield util.YES
return
# Prevent inferring if the inferred subscript
# is the same as the original subscripted object.
- if self is assigned or assigned is YES:
- yield YES
+ if self is assigned or assigned is util.YES:
+ yield util.YES
return
for inferred in assigned.infer(context):
yield inferred
else:
- raise InferenceError()
-nodes.Subscript._infer = path_wrapper(infer_subscript)
-nodes.Subscript.infer_lhs = raise_if_nothing_inferred(infer_subscript)
+ raise exceptions.InferenceError()
+nodes.Subscript._infer = bases.path_wrapper(infer_subscript)
+nodes.Subscript.infer_lhs = bases.raise_if_nothing_infered(infer_subscript)
-@raise_if_nothing_inferred
-@path_wrapper
+@bases.raise_if_nothing_inferred
+@bases.path_wrapper
def _infer_boolop(self, context=None):
"""Infer a boolean operation (and / or / not).
@@ -312,30 +230,29 @@ def _infer_boolop(self, context=None):
node.
"""
values = self.values
- op = self.op
+ if self.op == 'or':
+ predicate = operator.truth
+ else:
+ predicate = operator.not_
+
try:
values = [value.infer(context=context) for value in values]
- except InferenceError:
- yield YES
+ except exceptions.InferenceError:
+ yield util.YES
return
for pair in itertools.product(*values):
- if any(item is YES for item in pair):
+ if any(item is util.YES for item in pair):
# Can't infer the final result, just yield YES.
- yield YES
+ yield util.YES
continue
bool_values = [item.bool_value() for item in pair]
- if any(item is YES for item in bool_values):
+ if any(item is util.YES for item in bool_values):
# Can't infer the final result, just yield YES.
- yield YES
+ yield util.YES
continue
- if op == 'or':
- predicate = operator.truth
- else:
- predicate = operator.not_
-
# Since the boolean operations are short circuited operations,
# this code yields the first value for which the predicate is True
# and if no value respected the predicate, then the last value will
@@ -345,7 +262,7 @@ def _infer_boolop(self, context=None):
# 0 and 1 -> 0
# 1 or 0 -> 1
# 0 or 1 -> 1
- value = YES
+ value = util.YES
for value, bool_value in zip(pair, bool_values):
if predicate(bool_value):
yield value
@@ -364,7 +281,7 @@ def _filter_operation_errors(self, infer_callable, context, error):
# For the sake of .infer(), we don't care about operation
# errors, which is the job of pylint. So return something
# which shows that we can't infer the result.
- yield YES
+ yield util.YES
else:
yield result
@@ -376,7 +293,7 @@ def _infer_unaryop(self, context=None):
yield operand.infer_unary_op(self.op)
except TypeError as exc:
# The operand doesn't support this operation.
- yield UnaryOperationError(operand, self.op, exc)
+ yield exceptions.UnaryOperationError(operand, self.op, exc)
except AttributeError as exc:
meth = protocols.UNARY_OP_METHOD[self.op]
if meth is None:
@@ -384,15 +301,15 @@ def _infer_unaryop(self, context=None):
# value and negate its result, unless it is
# YES, which will be returned as is.
bool_value = operand.bool_value()
- if bool_value is not YES:
+ if bool_value is not util.YES:
yield nodes.const_factory(not bool_value)
else:
- yield YES
+ yield util.YES
else:
- if not isinstance(operand, Instance):
+ if not isinstance(operand, bases.Instance):
# The operation was used on something which
# doesn't support it.
- yield UnaryOperationError(operand, self.op, exc)
+ yield exceptions.UnaryOperationError(operand, self.op, exc)
continue
try:
@@ -405,21 +322,21 @@ def _infer_unaryop(self, context=None):
yield operand
else:
yield result
- except NotFoundError as exc:
+ except exceptions.NotFoundError as exc:
# The unary operation special method was not found.
- yield UnaryOperationError(operand, self.op, exc)
- except InferenceError:
- yield YES
+ yield exceptions.UnaryOperationError(operand, self.op, exc)
+ except exceptions.InferenceError:
+ yield util.YES
-@path_wrapper
+@bases.path_wrapper
def infer_unaryop(self, context=None):
"""Infer what an UnaryOp should return when evaluated."""
- return _filter_operation_errors(self, _infer_unaryop,
- context, UnaryOperationError)
+ return _filter_operation_errors(self, _infer_unaryop, context,
+ exceptions.UnaryOperationError)
nodes.UnaryOp._infer_unaryop = _infer_unaryop
-nodes.UnaryOp._infer = raise_if_nothing_inferred(infer_unaryop)
+nodes.UnaryOp._infer = bases.raise_if_nothing_inferred(infer_unaryop)
def _is_not_implemented(const):
@@ -433,6 +350,7 @@ def _invoke_binop_inference(instance, op, other, context, method_name):
inferred = next(method.infer(context=context))
return instance.infer_binary_op(op, other, context, inferred)
+
def _aug_op(instance, op, other, context, reverse=False):
"""Get an inference callable for an augmented binary operation."""
method_name = protocols.AUGMENTED_OP_METHOD[op]
@@ -442,6 +360,7 @@ def _aug_op(instance, op, other, context, reverse=False):
context=context,
method_name=method_name)
+
def _bin_op(instance, op, other, context, reverse=False):
"""Get an inference callable for a normal binary operation.
@@ -469,11 +388,11 @@ def _get_binop_contexts(context, left, right):
# left.__op__(right).
for arg in (right, left):
new_context = context.clone()
- new_context.callcontext = CallContext(
- [arg], starargs=None, dstarargs=None)
+ new_context.callcontext = contextmod.CallContext(args=[arg])
new_context.boundnode = None
yield new_context
+
def _same_type(type1, type2):
"""Check if type1 is the same as type2."""
return type1.qname() == type2.qname()
@@ -560,26 +479,26 @@ def _infer_binary_operation(left, right, op, context, flow_factory):
results = list(method())
except AttributeError:
continue
- except NotFoundError:
+ except exceptions.NotFoundError:
continue
- except InferenceError:
- yield YES
+ except exceptions.InferenceError:
+ yield util.YES
return
else:
- if any(result is YES for result in results):
- yield YES
+ if any(result is util.YES for result in results):
+ yield util.YES
return
# TODO(cpopa): since the inferrence engine might return
# more values than are actually possible, we decide
- # to return YES if we have union types.
+ # to return util.YES if we have union types.
if all(map(_is_not_implemented, results)):
continue
not_implemented = sum(1 for result in results
if _is_not_implemented(result))
if not_implemented and not_implemented != len(results):
# Can't decide yet what this is, not yet though.
- yield YES
+ yield util.YES
return
for result in results:
@@ -587,21 +506,21 @@ def _infer_binary_operation(left, right, op, context, flow_factory):
return
# TODO(cpopa): yield a BinaryOperationError here,
# since the operation is not supported
- yield BinaryOperationError(left_type, op, right_type)
+ yield exceptions.BinaryOperationError(left_type, op, right_type)
def _infer_binop(self, context):
"""Binary operation inferrence logic."""
if context is None:
- context = InferenceContext()
+ context = contextmod.InferenceContext()
left = self.left
right = self.right
op = self.op
for lhs in left.infer(context=context):
- if lhs is YES:
+ if lhs is util.YES:
# Don't know how to process this.
- yield YES
+ yield util.YES
return
# TODO(cpopa): if we have A() * A(), trying to infer
@@ -611,9 +530,9 @@ def _infer_binop(self, context):
rhs_context = context.clone()
rhs_context.path = set()
for rhs in right.infer(context=rhs_context):
- if rhs is YES:
+ if rhs is util.YES:
# Don't know how to process this.
- yield YES
+ yield util.YES
return
results = _infer_binary_operation(lhs, rhs, op,
@@ -622,25 +541,25 @@ def _infer_binop(self, context):
yield result
-@path_wrapper
+@bases.path_wrapper
def infer_binop(self, context=None):
- return _filter_operation_errors(self, _infer_binop,
- context, BinaryOperationError)
+ return _filter_operation_errors(self, _infer_binop, context,
+ exceptions.BinaryOperationError)
nodes.BinOp._infer_binop = _infer_binop
-nodes.BinOp._infer = yes_if_nothing_inferred(infer_binop)
+nodes.BinOp._infer = bases.yes_if_nothing_inferred(infer_binop)
def _infer_augassign(self, context=None):
"""Inferrence logic for augmented binary operations."""
if context is None:
- context = InferenceContext()
+ context = contextmod.InferenceContext()
op = self.op
for lhs in self.target.infer_lhs(context=context):
- if lhs is YES:
+ if lhs is util.YES:
# Don't know how to process this.
- yield YES
+ yield util.YES
return
# TODO(cpopa): if we have A() * A(), trying to infer
@@ -650,9 +569,9 @@ def _infer_augassign(self, context=None):
rhs_context = context.clone()
rhs_context.path = set()
for rhs in self.value.infer(context=rhs_context):
- if rhs is YES:
+ if rhs is util.YES:
# Don't know how to process this.
- yield YES
+ yield util.YES
return
results = _infer_binary_operation(lhs, rhs, op,
@@ -661,10 +580,10 @@ def _infer_augassign(self, context=None):
yield result
-@path_wrapper
+@bases.path_wrapper
def infer_augassign(self, context=None):
- return _filter_operation_errors(self, _infer_augassign,
- context, BinaryOperationError)
+ return _filter_operation_errors(self, _infer_augassign, context,
+ exceptions.BinaryOperationError)
nodes.AugAssign._infer_augassign = _infer_augassign
nodes.AugAssign._infer = infer_augassign
@@ -675,11 +594,12 @@ nodes.AugAssign._infer = infer_augassign
def infer_arguments(self, context=None):
name = context.lookupname
if name is None:
- raise InferenceError()
+ raise exceptions.InferenceError()
return protocols._arguments_infer_argname(self, name, context)
nodes.Arguments._infer = infer_arguments
+@bases.path_wrapper
def infer_ass(self, context=None):
"""infer a AssName/AssAttr: need to inspect the RHS part of the
assign node
@@ -688,25 +608,25 @@ def infer_ass(self, context=None):
if isinstance(stmt, nodes.AugAssign):
return stmt.infer(context)
stmts = list(self.assigned_stmts(context=context))
- return _infer_stmts(stmts, context)
-nodes.AssignName._infer = path_wrapper(infer_ass)
-nodes.AssignAttr._infer = path_wrapper(infer_ass)
+ return bases._infer_stmts(stmts, context)
+nodes.AssignName._infer = infer_ass
+nodes.AssignAttr._infer = infer_ass
# no infer method on DelName and DelAttr (expected InferenceError)
-
+@bases.path_wrapper
def infer_empty_node(self, context=None):
if not self.has_underlying_object():
- yield YES
+ yield util.YES
else:
try:
for inferred in MANAGER.infer_ast_from_something(self.object,
- context=context):
+ context=context):
yield inferred
- except AstroidError:
- yield YES
-nodes.EmptyNode._infer = path_wrapper(infer_empty_node)
+ except exceptions.AstroidError:
+ yield util.YES
+nodes.EmptyNode._infer = infer_empty_node
def infer_index(self, context=None):
@@ -722,20 +642,19 @@ def instance_getitem(self, index, context=None):
if context:
new_context = context.clone()
else:
- context = new_context = InferenceContext()
+ context = new_context = contextmod.InferenceContext()
# Create a new callcontext for providing index as an argument.
- new_context.callcontext = CallContext(
- args=[index], starargs=None, dstarargs=None)
+ new_context.callcontext = contextmod.CallContext(args=[index])
new_context.boundnode = self
method = next(self.igetattr('__getitem__', context=context))
- if not isinstance(method, BoundMethod):
- raise InferenceError
+ if not isinstance(method, bases.BoundMethod):
+ raise exceptions.InferenceError
try:
return next(method.infer_call_result(self, new_context))
except StopIteration:
- raise InferenceError
+ raise exceptions.InferenceError
-Instance.getitem = instance_getitem
+bases.Instance.getitem = instance_getitem
diff --git a/astroid/manager.py b/astroid/manager.py
index bbc790b9..ede213b0 100644
--- a/astroid/manager.py
+++ b/astroid/manager.py
@@ -19,16 +19,16 @@
possible by providing a class responsible to get astroid representation
from various source and using a cache of built modules)
"""
-__docformat__ = "restructuredtext en"
+from __future__ import print_function
-import collections
import imp
import os
import warnings
import zipimport
-from astroid.exceptions import AstroidBuildingException
+from astroid import exceptions
from astroid import modutils
+from astroid import transforms
def safe_repr(obj):
@@ -54,11 +54,19 @@ class AstroidManager(object):
# NOTE: cache entries are added by the [re]builder
self.astroid_cache = {}
self._mod_file_cache = {}
- self.transforms = collections.defaultdict(list)
self._failed_import_hooks = []
self.always_load_extensions = False
self.optimize_ast = False
self.extension_package_whitelist = set()
+ self._transform = transforms.TransformVisitor()
+
+ # Export these APIs for convenience
+ self.register_transform = self._transform.register_transform
+ self.unregister_transform = self._transform.unregister_transform
+
+ def visit_transforms(self, node):
+ """Visit the transforms and apply them to the given *node*."""
+ return self._transform.visit(node)
def ast_from_file(self, filepath, modname=None, fallback=True, source=False):
"""given a module name, return the astroid object"""
@@ -79,8 +87,8 @@ class AstroidManager(object):
return AstroidBuilder(self).file_build(filepath, modname)
elif fallback and modname:
return self.ast_from_module_name(modname)
- raise AstroidBuildingException('unable to get astroid for file %s' %
- filepath)
+ raise exceptions.AstroidBuildingException(
+ 'unable to get astroid for file %s' % filepath)
def _build_stub_module(self, modname):
from astroid.builder import AstroidBuilder
@@ -118,18 +126,20 @@ class AstroidManager(object):
module = modutils.load_module_from_name(modname)
except Exception as ex:
msg = 'Unable to load module %s (%s)' % (modname, ex)
- raise AstroidBuildingException(msg)
+ raise exceptions.AstroidBuildingException(msg)
return self.ast_from_module(module, modname)
elif mp_type == imp.PY_COMPILED:
- raise AstroidBuildingException("Unable to load compiled module %s" % (modname,))
+ msg = "Unable to load compiled module %s" % (modname,)
+ raise exceptions.AstroidBuildingException(msg)
if filepath is None:
- raise AstroidBuildingException("Unable to load module %s" % (modname,))
+ msg = "Unable to load module %s" % (modname,)
+ raise exceptions.AstroidBuildingException(msg)
return self.ast_from_file(filepath, modname, fallback=False)
- except AstroidBuildingException as e:
+ except exceptions.AstroidBuildingException as e:
for hook in self._failed_import_hooks:
try:
return hook(modname)
- except AstroidBuildingException:
+ except exceptions.AstroidBuildingException:
pass
raise e
finally:
@@ -166,9 +176,9 @@ class AstroidManager(object):
modname.split('.'), context_file=contextfile)
except ImportError as ex:
msg = 'Unable to load module %s (%s)' % (modname, ex)
- value = AstroidBuildingException(msg)
+ value = exceptions.AstroidBuildingException(msg)
self._mod_file_cache[(modname, contextfile)] = value
- if isinstance(value, AstroidBuildingException):
+ if isinstance(value, exceptions.AstroidBuildingException):
raise value
return value
@@ -193,8 +203,8 @@ class AstroidManager(object):
try:
modname = klass.__module__
except AttributeError:
- raise AstroidBuildingException(
- 'Unable to get module for class %s' % safe_repr(klass))
+ msg = 'Unable to get module for class %s' % safe_repr(klass)
+ raise exceptions.AstroidBuildingException(msg)
modastroid = self.ast_from_module_name(modname)
return modastroid.getattr(klass.__name__)[0] # XXX
@@ -207,44 +217,30 @@ class AstroidManager(object):
try:
modname = klass.__module__
except AttributeError:
- raise AstroidBuildingException(
- 'Unable to get module for %s' % safe_repr(klass))
+ msg = 'Unable to get module for %s' % safe_repr(klass)
+ raise exceptions.AstroidBuildingException(msg)
except Exception as ex:
- raise AstroidBuildingException(
- 'Unexpected error while retrieving module for %s: %s'
- % (safe_repr(klass), ex))
+ msg = ('Unexpected error while retrieving module for %s: %s'
+ % (safe_repr(klass), ex))
+ raise exceptions.AstroidBuildingException(msg)
try:
name = klass.__name__
except AttributeError:
- raise AstroidBuildingException(
- 'Unable to get name for %s' % safe_repr(klass))
+ msg = 'Unable to get name for %s' % safe_repr(klass)
+ raise exceptions.AstroidBuildingException(msg)
except Exception as ex:
- raise AstroidBuildingException(
- 'Unexpected error while retrieving name for %s: %s'
- % (safe_repr(klass), ex))
+ exc = ('Unexpected error while retrieving name for %s: %s'
+ % (safe_repr(klass), ex))
+ raise exceptions.AstroidBuildingException(exc)
# take care, on living object __module__ is regularly wrong :(
modastroid = self.ast_from_module_name(modname)
if klass is obj:
- for inferred in modastroid.igetattr(name, context):
+ for inferred in modastroid.igetattr(name, context):
yield inferred
else:
for inferred in modastroid.igetattr(name, context):
yield inferred.instanciate_class()
- def register_transform(self, node_class, transform, predicate=None):
- """Register `transform(node)` function to be applied on the given
- Astroid's `node_class` if `predicate` is None or returns true
- when called with the node as argument.
-
- The transform function may return a value which is then used to
- substitute the original node in the tree.
- """
- self.transforms[node_class].append((transform, predicate))
-
- def unregister_transform(self, node_class, transform, predicate=None):
- """Unregister the given transform."""
- self.transforms[node_class].remove((transform, predicate))
-
def register_failed_import_hook(self, hook):
"""Registers a hook to resolve imports that cannot be found otherwise.
@@ -255,30 +251,6 @@ class AstroidManager(object):
"""
self._failed_import_hooks.append(hook)
- def transform(self, node):
- """Call matching transforms for the given node if any and return the
- transformed node.
- """
- cls = node.__class__
- if cls not in self.transforms:
- # no transform registered for this class of node
- return node
-
- transforms = self.transforms[cls]
- orig_node = node # copy the reference
- for transform_func, predicate in transforms:
- if predicate is None or predicate(node):
- ret = transform_func(node)
- # if the transformation function returns something, it's
- # expected to be a replacement for the node
- if ret is not None:
- if node is not orig_node:
- # node has already be modified by some previous
- # transformation, warn about it
- warnings.warn('node %s substituted multiple times' % node)
- node = ret
- return node
-
def cache_module(self, module):
"""Cache a module if no module with the same name is known yet."""
self.astroid_cache.setdefault(module.name, module)
diff --git a/astroid/mixins.py b/astroid/mixins.py
index ec7b7b69..87834f10 100644
--- a/astroid/mixins.py
+++ b/astroid/mixins.py
@@ -20,16 +20,14 @@
import warnings
-from logilab.common.decorators import cachedproperty
-
-from astroid.exceptions import (AstroidBuildingException, InferenceError,
- NotFoundError)
+from astroid import decorators
+from astroid import exceptions
class BlockRangeMixIn(object):
"""override block range """
- @cachedproperty
+ @decorators.cachedproperty
def blockstart_tolineno(self):
return self.lineno
@@ -129,12 +127,12 @@ class ImportFromMixin(FilterStmtsMixin):
try:
return mymodule.import_module(modname, level=level,
relative_only=level and level >= 1)
- except AstroidBuildingException as ex:
+ except exceptions.AstroidBuildingException as ex:
if isinstance(ex.args[0], SyntaxError):
- raise InferenceError(str(ex))
- raise InferenceError(modname)
+ raise exceptions.InferenceError(str(ex))
+ raise exceptions.InferenceError(modname)
except SyntaxError as ex:
- raise InferenceError(str(ex))
+ raise exceptions.InferenceError(str(ex))
def real_name(self, asname):
"""get name from 'as' name"""
@@ -146,5 +144,4 @@ class ImportFromMixin(FilterStmtsMixin):
_asname = name
if asname == _asname:
return name
- raise NotFoundError(asname)
-
+ raise exceptions.NotFoundError(asname)
diff --git a/astroid/modutils.py b/astroid/modutils.py
index 5a6c3471..3798c2fa 100644
--- a/astroid/modutils.py
+++ b/astroid/modutils.py
@@ -28,8 +28,6 @@
"""
from __future__ import with_statement
-__docformat__ = "restructuredtext en"
-
import imp
import os
import platform
@@ -43,8 +41,6 @@ try:
except ImportError:
pkg_resources = None
-from logilab.common import _handle_blacklist
-
PY_ZIPMODULE = object()
if sys.platform.startswith('win'):
@@ -111,6 +107,18 @@ def _path_from_filename(filename, is_jython=IS_JYTHON):
return filename
+def _handle_blacklist(blacklist, dirnames, filenames):
+ """remove files/directories in the black list
+
+ dirnames/filenames are usually from os.walk
+ """
+ for norecurs in blacklist:
+ if norecurs in dirnames:
+ dirnames.remove(norecurs)
+ elif norecurs in filenames:
+ filenames.remove(norecurs)
+
+
_NORM_PATH_CACHE = {}
def _cache_normalize_path(path):
@@ -337,8 +345,8 @@ def file_info_from_modpath(modpath, path=None, context_file=None):
def get_module_part(dotted_name, context_file=None):
"""given a dotted name return the module part of the name :
- >>> get_module_part('logilab.common.modutils.get_module_part')
- 'logilab.common.modutils'
+ >>> get_module_part('astroid.as_string.dump')
+ 'astroid.as_string'
:type dotted_name: str
:param dotted_name: full name of the identifier we are interested in
@@ -393,7 +401,7 @@ def get_module_part(dotted_name, context_file=None):
return dotted_name
-def get_module_files(src_directory, blacklist):
+def get_module_files(src_directory, blacklist, list_all=False):
"""given a package directory return a list of all available python
module's files in the package and its subpackages
@@ -402,9 +410,12 @@ def get_module_files(src_directory, blacklist):
path of the directory corresponding to the package
:type blacklist: list or tuple
- :param blacklist:
- optional list of files or directory to ignore, default to the value of
- `logilab.common.STD_BLACKLIST`
+ :param blacklist: iterable
+ list of files or directories to ignore.
+
+ :type list_all: bool
+ :param list_all:
+ get files from all paths, including ones without __init__.py
:rtype: list
:return:
@@ -415,7 +426,7 @@ def get_module_files(src_directory, blacklist):
for directory, dirnames, filenames in os.walk(src_directory):
_handle_blacklist(blacklist, dirnames, filenames)
# check for __init__.py
- if not '__init__.py' in filenames:
+ if not list_all and not '__init__.py' in filenames:
dirnames[:] = ()
continue
for filename in filenames:
diff --git a/astroid/node_classes.py b/astroid/node_classes.py
index 85f57dc9..1c25fac1 100644
--- a/astroid/node_classes.py
+++ b/astroid/node_classes.py
@@ -21,16 +21,15 @@
import lazy_object_proxy
import six
-from logilab.common.decorators import cachedproperty
+from astroid import bases
+from astroid import context as contextmod
+from astroid import decorators
+from astroid import exceptions
+from astroid import mixins
+from astroid import util
-from astroid.exceptions import (
- NoDefault, UnaryOperationError,
- InferenceError, BinaryOperationError
-)
-from astroid.bases import (NodeNG, Statement, Instance, InferenceContext,
- _infer_stmts, YES, BUILTINS)
-from astroid.mixins import (BlockRangeMixIn, AssignTypeMixin,
- ParentAssignTypeMixin, ImportFromMixin)
+
+BUILTINS = six.moves.builtins.__name__
def unpack_infer(stmt, context=None):
@@ -49,7 +48,7 @@ def unpack_infer(stmt, context=None):
return
# else, infer recursivly, except YES object that should be returned as is
for inferred in stmt.infer(context):
- if inferred is YES:
+ if inferred is util.YES:
yield inferred
else:
for inf_inf in unpack_infer(inferred, context):
@@ -133,8 +132,8 @@ class LookupMixIn(object):
the lookup method
"""
frame, stmts = self.lookup(name)
- context = InferenceContext()
- return _infer_stmts(stmts, context, frame)
+ context = contextmod.InferenceContext()
+ return bases._infer_stmts(stmts, context, frame)
def _filter_stmts(self, stmts, frame, offset):
"""filter statements to remove ignorable statements.
@@ -258,7 +257,7 @@ class LookupMixIn(object):
# Name classes
-class AssignName(LookupMixIn, ParentAssignTypeMixin, NodeNG):
+class AssignName(LookupMixIn, mixins.ParentAssignTypeMixin, bases.NodeNG):
"""class representing an AssignName node"""
_other_fields = ('name',)
@@ -267,7 +266,7 @@ class AssignName(LookupMixIn, ParentAssignTypeMixin, NodeNG):
super(AssignName, self).__init__(lineno, col_offset, parent)
-class DelName(LookupMixIn, ParentAssignTypeMixin, NodeNG):
+class DelName(LookupMixIn, mixins.ParentAssignTypeMixin, bases.NodeNG):
"""class representing a DelName node"""
_other_fields = ('name',)
@@ -276,7 +275,7 @@ class DelName(LookupMixIn, ParentAssignTypeMixin, NodeNG):
super(DelName, self).__init__(lineno, col_offset, parent)
-class Name(LookupMixIn, NodeNG):
+class Name(LookupMixIn, bases.NodeNG):
"""class representing a Name node"""
_other_fields = ('name',)
@@ -285,7 +284,7 @@ class Name(LookupMixIn, NodeNG):
super(Name, self).__init__(lineno, col_offset, parent)
-class Arguments(NodeNG, AssignTypeMixin):
+class Arguments(mixins.AssignTypeMixin, bases.NodeNG):
"""class representing an Arguments node"""
if six.PY3:
# Python 3.4+ uses a different approach regarding annotations,
@@ -338,7 +337,7 @@ class Arguments(NodeNG, AssignTypeMixin):
return name
return None
- @cachedproperty
+ @decorators.cachedproperty
def fromlineno(self):
lineno = super(Arguments, self).fromlineno
return max(lineno, self.parent.fromlineno or 0)
@@ -374,7 +373,7 @@ class Arguments(NodeNG, AssignTypeMixin):
i = _find_arg(argname, self.kwonlyargs)[0]
if i is not None and self.kw_defaults[i] is not None:
return self.kw_defaults[i]
- raise NoDefault()
+ raise exceptions.NoDefault()
def is_argument(self, name):
"""return True if the name is defined in arguments"""
@@ -433,7 +432,7 @@ def _format_args(args, annotations=None, defaults=None):
return ', '.join(values)
-class AssignAttr(NodeNG, ParentAssignTypeMixin):
+class AssignAttr(mixins.ParentAssignTypeMixin, bases.NodeNG):
"""class representing an AssignAttr node"""
_astroid_fields = ('expr',)
_other_fields = ('attrname',)
@@ -447,7 +446,7 @@ class AssignAttr(NodeNG, ParentAssignTypeMixin):
self.expr = expr
-class Assert(Statement):
+class Assert(bases.Statement):
"""class representing an Assert node"""
_astroid_fields = ('test', 'fail',)
test = None
@@ -458,7 +457,7 @@ class Assert(Statement):
self.test = test
-class Assign(Statement, AssignTypeMixin):
+class Assign(mixins.Statement, bases.AssignTypeMixin):
"""class representing an Assign node"""
_astroid_fields = ('targets', 'value',)
targets = None
@@ -469,7 +468,7 @@ class Assign(Statement, AssignTypeMixin):
self.value = value
-class AugAssign(Statement, AssignTypeMixin):
+class AugAssign(mixins.AssignTypeMixin, bases.Statement):
"""class representing an AugAssign node"""
_astroid_fields = ('target', 'value')
_other_fields = ('op',)
@@ -497,12 +496,12 @@ class AugAssign(Statement, AssignTypeMixin):
try:
results = self._infer_augassign(context=context)
return [result for result in results
- if isinstance(result, BinaryOperationError)]
- except InferenceError:
+ if isinstance(result, exceptions.BinaryOperationError)]
+ except exceptions.InferenceError:
return []
-class Repr(NodeNG):
+class Repr(bases.NodeNG):
"""class representing a Repr node"""
_astroid_fields = ('value',)
value = None
@@ -511,7 +510,7 @@ class Repr(NodeNG):
self.value = value
-class BinOp(NodeNG):
+class BinOp(bases.NodeNG):
"""class representing a BinOp node"""
_astroid_fields = ('left', 'right')
_other_fields = ('op',)
@@ -539,12 +538,12 @@ class BinOp(NodeNG):
try:
results = self._infer_binop(context=context)
return [result for result in results
- if isinstance(result, BinaryOperationError)]
- except InferenceError:
+ if isinstance(result, exceptions.BinaryOperationError)]
+ except exceptions.InferenceError:
return []
-class BoolOp(NodeNG):
+class BoolOp(bases.NodeNG):
"""class representing a BoolOp node"""
_astroid_fields = ('values',)
_other_fields = ('op',)
@@ -558,15 +557,16 @@ class BoolOp(NodeNG):
self.values = values
-class Break(Statement):
+class Break(bases.Statement):
"""class representing a Break node"""
-class Call(NodeNG):
+class Call(bases.NodeNG):
"""class representing a Call node"""
- _astroid_fields = ('func', 'args', 'starargs', 'kwargs')
+ _astroid_fields = ('func', 'args', 'keywords', 'starargs', 'kwargs')
func = None
args = None
+ keywords = None
starargs = None
kwargs = None
@@ -577,7 +577,7 @@ class Call(NodeNG):
self.kwargs = kwargs
-class Compare(NodeNG):
+class Compare(bases.NodeNG):
"""class representing a Compare node"""
_astroid_fields = ('left', 'ops',)
left = None
@@ -600,7 +600,7 @@ class Compare(NodeNG):
#return self.left
-class Comprehension(NodeNG):
+class Comprehension(bases.NodeNG):
"""class representing a Comprehension node"""
_astroid_fields = ('target', 'iter', 'ifs')
target = None
@@ -619,6 +619,13 @@ class Comprehension(NodeNG):
def assign_type(self):
return self
+ def ass_type(self):
+ warnings.warn('%s.ass_type() is deprecated, '
+ 'use %s.assign_type() instead.'
+ % (type(self).__name__, type(self).__name__),
+ PendingDeprecationWarning)
+ return self.assign_type()
+
def _get_filtered_stmts(self, lookup_node, node, stmts, mystmt):
"""method used in filter_stmts"""
if self is mystmt:
@@ -634,7 +641,7 @@ class Comprehension(NodeNG):
return stmts, False
-class Const(NodeNG, Instance):
+class Const(bases.NodeNG, bases.Instance):
"""represent a constant node like num, str, bool, None, bytes"""
_other_fields = ('value',)
@@ -667,11 +674,11 @@ class Const(NodeNG, Instance):
return bool(self.value)
-class Continue(Statement):
+class Continue(bases.Statement):
"""class representing a Continue node"""
-class Decorators(NodeNG):
+class Decorators(bases.NodeNG):
"""class representing a Decorators node"""
_astroid_fields = ('nodes',)
nodes = None
@@ -684,7 +691,7 @@ class Decorators(NodeNG):
return self.parent.parent.scope()
-class DelAttr(NodeNG, ParentAssignTypeMixin):
+class DelAttr(mixins.ParentAssignTypeMixin, bases.NodeNG):
"""class representing a DelAttr node"""
_astroid_fields = ('expr',)
_other_fields = ('attrname',)
@@ -698,7 +705,7 @@ class DelAttr(NodeNG, ParentAssignTypeMixin):
self.expr = expr
-class Delete(Statement, AssignTypeMixin):
+class Delete(mixins.AssignTypeMixin, bases.Statement):
"""class representing a Delete node"""
_astroid_fields = ('targets',)
targets = None
@@ -707,7 +714,7 @@ class Delete(Statement, AssignTypeMixin):
self.targets = targets
-class Dict(NodeNG, Instance):
+class Dict(bases.NodeNG, bases.Instance):
"""class representing a Dict node"""
_astroid_fields = ('items',)
@@ -750,7 +757,7 @@ class Dict(NodeNG, Instance):
def getitem(self, lookup_key, context=None):
for key, value in self.items:
for inferredkey in key.infer(context):
- if inferredkey is YES:
+ if inferredkey is util.YES:
continue
if isinstance(inferredkey, Const) \
and inferredkey.value == lookup_key:
@@ -763,7 +770,7 @@ class Dict(NodeNG, Instance):
return bool(self.items)
-class Expr(Statement):
+class Expr(bases.Statement):
"""class representing a Expr node"""
_astroid_fields = ('value',)
value = None
@@ -772,18 +779,18 @@ class Expr(Statement):
self.value = value
-class Ellipsis(NodeNG): # pylint: disable=redefined-builtin
+class Ellipsis(bases.NodeNG): # pylint: disable=redefined-builtin
"""class representing an Ellipsis node"""
def bool_value(self):
return True
-class EmptyNode(NodeNG):
+class EmptyNode(bases.NodeNG):
"""class representing an EmptyNode node"""
-class ExceptHandler(Statement, AssignTypeMixin):
+class ExceptHandler(mixins.AssignTypeMixin, bases.Statement):
"""class representing an ExceptHandler node"""
_astroid_fields = ('type', 'name', 'body',)
type = None
@@ -795,7 +802,7 @@ class ExceptHandler(Statement, AssignTypeMixin):
self.name = name
self.body = body
- @cachedproperty
+ @decorators.cachedproperty
def blockstart_tolineno(self):
if self.name:
return self.name.tolineno
@@ -812,7 +819,7 @@ class ExceptHandler(Statement, AssignTypeMixin):
return True
-class Exec(Statement):
+class Exec(bases.Statement):
"""class representing an Exec node"""
_astroid_fields = ('expr', 'globals', 'locals',)
expr = None
@@ -825,16 +832,23 @@ class Exec(Statement):
self.locals = locals
-class ExtSlice(NodeNG):
+class ExtSlice(bases.NodeNG):
"""class representing an ExtSlice node"""
_astroid_fields = ('dims',)
dims = None
+<<<<<<< variant A
def postinit(self, dims=None):
self.dims = dims
class For(BlockRangeMixIn, AssignTypeMixin, Statement):
+>>>>>>> variant B
+
+class For(mixins.BlockRangeMixIn, mixins.AssignTypeMixin, bases.Statement):
+####### Ancestor
+class For(BlockRangeMixIn, AssignTypeMixin, Statement):
+======= end
"""class representing a For node"""
_astroid_fields = ('target', 'iter', 'body', 'orelse',)
target = None
@@ -849,12 +863,12 @@ class For(BlockRangeMixIn, AssignTypeMixin, Statement):
self.orelse = orelse
optional_assign = True
- @cachedproperty
+ @decorators.cachedproperty
def blockstart_tolineno(self):
return self.iter.tolineno
-class ImportFrom(ImportFromMixin, Statement):
+class ImportFrom(mixins.ImportFromMixin, bases.Statement):
"""class representing a ImportFrom node"""
_other_fields = ('modname', 'names', 'level')
@@ -866,7 +880,7 @@ class ImportFrom(ImportFromMixin, Statement):
super(ImportFrom, self).__init__(lineno, col_offset, parent)
-class Attribute(NodeNG):
+class Attribute(bases.NodeNG):
"""class representing a Attribute node"""
_astroid_fields = ('expr',)
_other_fields = ('attrname')
@@ -880,7 +894,7 @@ class Attribute(NodeNG):
self.expr = expr
-class Global(Statement):
+class Global(bases.Statement):
"""class representing a Global node"""
_other_fields = ('names',)
@@ -892,7 +906,7 @@ class Global(Statement):
return name
-class If(BlockRangeMixIn, Statement):
+class If(mixins.BlockRangeMixIn, bases.Statement):
"""class representing an If node"""
_astroid_fields = ('test', 'body', 'orelse')
test = None
@@ -904,7 +918,7 @@ class If(BlockRangeMixIn, Statement):
self.body = body
self.orelse = orelse
- @cachedproperty
+ @decorators.cachedproperty
def blockstart_tolineno(self):
return self.test.tolineno
@@ -918,7 +932,7 @@ class If(BlockRangeMixIn, Statement):
self.body[0].fromlineno - 1)
-class IfExp(NodeNG):
+class IfExp(bases.NodeNG):
"""class representing an IfExp node"""
_astroid_fields = ('test', 'body', 'orelse')
test = None
@@ -931,7 +945,7 @@ class IfExp(NodeNG):
self.orelse = orelse
-class Import(ImportFromMixin, Statement):
+class Import(mixins.ImportFromMixin, bases.Statement):
"""class representing an Import node"""
_other_fields = ('names',)
@@ -940,7 +954,7 @@ class Import(ImportFromMixin, Statement):
super(Import, self).__init__(lineno, col_offset, parent)
-class Index(NodeNG):
+class Index(bases.NodeNG):
"""class representing an Index node"""
_astroid_fields = ('value',)
value = None
@@ -949,7 +963,7 @@ class Index(NodeNG):
self.value = value
-class Keyword(NodeNG):
+class Keyword(bases.NodeNG):
"""class representing a Keyword node"""
_astroid_fields = ('value',)
_other_fields = ('arg',)
@@ -963,7 +977,7 @@ class Keyword(NodeNG):
self.value = value
-class List(NodeNG, Instance, ParentAssignTypeMixin):
+class List(mixins.ParentAssignTypeMixin, bases.NodeNG, bases.Instance):
"""class representing a List node"""
_astroid_fields = ('elts',)
@@ -996,7 +1010,7 @@ class List(NodeNG, Instance, ParentAssignTypeMixin):
return bool(self.elts)
-class Nonlocal(Statement):
+class Nonlocal(bases.Statement):
"""class representing a Nonlocal node"""
_other_fields = ('names',)
@@ -1008,11 +1022,11 @@ class Nonlocal(Statement):
return name
-class Pass(Statement):
+class Pass(bases.Statement):
"""class representing a Pass node"""
-class Print(Statement):
+class Print(bases.Statement):
"""class representing a Print node"""
_astroid_fields = ('dest', 'values',)
dest = None
@@ -1027,7 +1041,7 @@ class Print(Statement):
self.values = values
-class Raise(Statement):
+class Raise(bases.Statement):
"""class representing a Raise node"""
exc = None
if six.PY2:
@@ -1041,6 +1055,7 @@ class Raise(Statement):
self.tback = tback
else:
_astroid_fields = ('exc', 'cause')
+ exc = None
cause = None
def postinit(self, exc=None, cause=None):
@@ -1055,7 +1070,7 @@ class Raise(Statement):
return True
-class Return(Statement):
+class Return(bases.Statement):
"""class representing a Return node"""
_astroid_fields = ('value',)
value = None
@@ -1064,7 +1079,7 @@ class Return(Statement):
self.value = value
-class Set(NodeNG, Instance, ParentAssignTypeMixin):
+class Set(mixins.ParentAssignTypeMixin, bases.NodeNG, bases.Instance):
"""class representing a Set node"""
_astroid_fields = ('elts',)
@@ -1094,7 +1109,7 @@ class Set(NodeNG, Instance, ParentAssignTypeMixin):
return bool(self.elts)
-class Slice(NodeNG):
+class Slice(bases.NodeNG):
"""class representing a Slice node"""
_astroid_fields = ('lower', 'upper', 'step')
lower = None
@@ -1107,7 +1122,7 @@ class Slice(NodeNG):
self.step = step
-class Starred(NodeNG, ParentAssignTypeMixin):
+class Starred(mixins.ParentAssignTypeMixin, bases.NodeNG):
"""class representing a Starred node"""
_astroid_fields = ('value',)
value = None
@@ -1116,7 +1131,7 @@ class Starred(NodeNG, ParentAssignTypeMixin):
self.value = value
-class Subscript(NodeNG):
+class Subscript(bases.NodeNG):
"""class representing a Subscript node"""
_astroid_fields = ('value', 'slice')
value = None
@@ -1127,7 +1142,7 @@ class Subscript(NodeNG):
self.slice = slice
-class TryExcept(BlockRangeMixIn, Statement):
+class TryExcept(mixins.BlockRangeMixIn, bases.Statement):
"""class representing a TryExcept node"""
_astroid_fields = ('body', 'handlers', 'orelse',)
body = None
@@ -1155,7 +1170,7 @@ class TryExcept(BlockRangeMixIn, Statement):
return self._elsed_block_range(lineno, self.orelse, last)
-class TryFinally(BlockRangeMixIn, Statement):
+class TryFinally(mixins.BlockRangeMixIn, bases.Statement):
"""class representing a TryFinally node"""
_astroid_fields = ('body', 'finalbody',)
body = None
@@ -1175,7 +1190,7 @@ class TryFinally(BlockRangeMixIn, Statement):
return self._elsed_block_range(lineno, self.finalbody)
-class Tuple(NodeNG, Instance, ParentAssignTypeMixin):
+class Tuple(mixins.ParentAssignTypeMixin, bases.NodeNG, bases.Instance):
"""class representing a Tuple node"""
_astroid_fields = ('elts',)
@@ -1208,7 +1223,7 @@ class Tuple(NodeNG, Instance, ParentAssignTypeMixin):
return bool(self.elts)
-class UnaryOp(NodeNG):
+class UnaryOp(bases.NodeNG):
"""class representing an UnaryOp node"""
_astroid_fields = ('operand',)
_other_fields = ('op',)
@@ -1234,12 +1249,12 @@ class UnaryOp(NodeNG):
try:
results = self._infer_unaryop(context=context)
return [result for result in results
- if isinstance(result, UnaryOperationError)]
- except InferenceError:
+ if isinstance(result, exceptions.UnaryOperationError)]
+ except exceptions.InferenceError:
return []
-class While(BlockRangeMixIn, Statement):
+class While(mixins.BlockRangeMixIn, bases.Statement):
"""class representing a While node"""
_astroid_fields = ('test', 'body', 'orelse',)
test = None
@@ -1251,7 +1266,7 @@ class While(BlockRangeMixIn, Statement):
self.body = body
self.orelse = orelse
- @cachedproperty
+ @decorators.cachedproperty
def blockstart_tolineno(self):
return self.test.tolineno
@@ -1260,7 +1275,7 @@ class While(BlockRangeMixIn, Statement):
return self. _elsed_block_range(lineno, self.orelse)
-class With(BlockRangeMixIn, AssignTypeMixin, Statement):
+class With(mixins.BlockRangeMixIn, mixins.AssignTypeMixin, bases.Statement):
"""class representing a With node"""
_astroid_fields = ('items', 'body')
items = None
@@ -1270,7 +1285,7 @@ class With(BlockRangeMixIn, AssignTypeMixin, Statement):
self.items = items
self.body = body
- @cachedproperty
+ @decorators.cachedproperty
def blockstart_tolineno(self):
return self.items[-1][0].tolineno
@@ -1283,7 +1298,7 @@ class With(BlockRangeMixIn, AssignTypeMixin, Statement):
yield elt
-class Yield(NodeNG):
+class Yield(bases.NodeNG):
"""class representing a Yield node"""
_astroid_fields = ('value',)
value = None
@@ -1317,15 +1332,15 @@ def _update_const_classes():
CONST_CLS[kls] = Const
_update_const_classes()
+
def const_factory(value):
"""return an astroid node for a python value"""
- # XXX we should probably be stricter here and only consider stuff
- # in CONST_CLS or do better treatment: in case where value is not
- # in CONST_CLS, we should rather recall the builder on this value
- # than returning an empty node (another option being that
- # const_factory shouldn't be called with something not in
- # CONST_CLS)
- assert not isinstance(value, NodeNG)
+ # XXX we should probably be stricter here and only consider stuff in
+ # CONST_CLS or do better treatment: in case where value is not in CONST_CLS,
+ # we should rather recall the builder on this value than returning an empty
+ # node (another option being that const_factory shouldn't be called with something
+ # not in CONST_CLS)
+ assert not isinstance(value, bases.NodeNG)
try:
return CONST_CLS[value.__class__](value)
except (KeyError, AttributeError):
diff --git a/astroid/nodes.py b/astroid/nodes.py
index 820a8d49..8bbd4f1b 100644
--- a/astroid/nodes.py
+++ b/astroid/nodes.py
@@ -36,8 +36,6 @@ on ImportFrom and Import :
"""
# pylint: disable=unused-import,redefined-builtin
-__docformat__ = "restructuredtext en"
-
from astroid.node_classes import (
Arguments, AssignAttr, Assert, Assign,
AssignName, AugAssign, Repr, BinOp, BoolOp, Break, Call, Compare,
diff --git a/astroid/objects.py b/astroid/objects.py
index 6f4c91c1..a78dcb41 100644
--- a/astroid/objects.py
+++ b/astroid/objects.py
@@ -26,24 +26,21 @@ leads to an inferred FrozenSet:
"""
-from logilab.common.decorators import cachedproperty
import six
+from astroid import bases
+from astroid import decorators
+from astroid import exceptions
from astroid import MANAGER
-from astroid.bases import (
- BUILTINS, NodeNG, Instance, _infer_stmts,
- BoundMethod,
-)
-from astroid.exceptions import (
- SuperError, SuperArgumentTypeError,
- NotFoundError, MroError
-)
-from astroid.node_classes import const_factory
-from astroid.scoped_nodes import ClassDef, FunctionDef
-from astroid.mixins import ParentAssignTypeMixin
-
-
-class FrozenSet(NodeNG, Instance, ParentAssignTypeMixin):
+from astroid import mixins
+from astroid import node_classes
+from astroid import scoped_nodes
+
+
+BUILTINS = six.moves.builtins.__name__
+
+
+class FrozenSet(bases.NodeNG, bases.Instance, mixins.ParentAssignTypeMixin):
"""class representing a FrozenSet composite node"""
_astroid_fields = ('elts',)
@@ -63,7 +60,7 @@ class FrozenSet(NodeNG, Instance, ParentAssignTypeMixin):
if elts is None:
self.elts = []
else:
- self.elts = [const_factory(e) for e in elts]
+ self.elts = [node_classes.const_factory(e) for e in elts]
def pytype(self):
return '%s.frozenset' % BUILTINS
@@ -74,7 +71,7 @@ class FrozenSet(NodeNG, Instance, ParentAssignTypeMixin):
def _infer(self, context=None):
yield self
- @cachedproperty
+ @decorators.cachedproperty
def _proxied(self):
builtins = MANAGER.astroid_cache[BUILTINS]
return builtins.getattr('frozenset')[0]
@@ -83,7 +80,7 @@ class FrozenSet(NodeNG, Instance, ParentAssignTypeMixin):
return bool(self.elts)
-class Super(NodeNG):
+class Super(bases.NodeNG):
"""Proxy class over a super call.
This class offers almost the same behaviour as Python's super,
@@ -114,31 +111,34 @@ class Super(NodeNG):
def super_mro(self):
"""Get the MRO which will be used to lookup attributes in this super."""
- if not isinstance(self.mro_pointer, ClassDef):
- raise SuperArgumentTypeError("The first super argument must be type.")
+ if not isinstance(self.mro_pointer, scoped_nodes.ClassDef):
+ raise exceptions.SuperArgumentTypeError(
+ "The first super argument must be type.")
- if isinstance(self.type, ClassDef):
+ if isinstance(self.type, scoped_nodes.ClassDef):
# `super(type, type)`, most likely in a class method.
self._class_based = True
mro_type = self.type
else:
mro_type = getattr(self.type, '_proxied', None)
- if not isinstance(mro_type, (Instance, ClassDef)):
- raise SuperArgumentTypeError("super(type, obj): obj must be an "
- "instance or subtype of type")
+ if not isinstance(mro_type, (bases.Instance, scoped_nodes.ClassDef)):
+ raise exceptions.SuperArgumentTypeError(
+ "super(type, obj): obj must be an "
+ "instance or subtype of type")
if not mro_type.newstyle:
- raise SuperError("Unable to call super on old-style classes.")
+ raise exceptions.SuperError("Unable to call super on old-style classes.")
mro = mro_type.mro()
if self.mro_pointer not in mro:
- raise SuperArgumentTypeError("super(type, obj): obj must be an "
- "instance or subtype of type")
+ raise exceptions.SuperArgumentTypeError(
+ "super(type, obj): obj must be an "
+ "instance or subtype of type")
index = mro.index(self.mro_pointer)
return mro[index + 1:]
- @cachedproperty
+ @decorators.cachedproperty
def _proxied(self):
builtins = MANAGER.astroid_cache[BUILTINS]
return builtins.getattr('super')[0]
@@ -164,10 +164,10 @@ class Super(NodeNG):
try:
mro = self.super_mro()
- except (MroError, SuperError) as exc:
+ except (exceptions.MroError, exceptions.SuperError) as exc:
# Don't let invalid MROs or invalid super calls
# to leak out as is from this function.
- six.raise_from(NotFoundError, exc)
+ six.raise_from(exceptions.NotFoundError, exc)
found = False
for cls in mro:
@@ -175,24 +175,24 @@ class Super(NodeNG):
continue
found = True
- for inferred in _infer_stmts([cls[name]], context, frame=self):
- if not isinstance(inferred, FunctionDef):
+ for inferred in bases._infer_stmts([cls[name]], context, frame=self):
+ if not isinstance(inferred, scoped_nodes.FunctionDef):
yield inferred
continue
# We can obtain different descriptors from a super depending
# on what we are accessing and where the super call is.
if inferred.type == 'classmethod':
- yield BoundMethod(inferred, cls)
+ yield bases.BoundMethod(inferred, cls)
elif self._scope.type == 'classmethod' and inferred.type == 'method':
yield inferred
elif self._class_based or inferred.type == 'staticmethod':
yield inferred
else:
- yield BoundMethod(inferred, cls)
+ yield bases.BoundMethod(inferred, cls)
if not found:
- raise NotFoundError(name)
+ raise exceptions.NotFoundError(name)
def getattr(self, name, context=None):
return list(self.igetattr(name, context=context))
diff --git a/astroid/protocols.py b/astroid/protocols.py
index 3600840b..72f78984 100644
--- a/astroid/protocols.py
+++ b/astroid/protocols.py
@@ -25,16 +25,12 @@ import sys
import six
-from astroid.exceptions import InferenceError, NoDefault, NotFoundError
-from astroid.node_classes import unpack_infer
-from astroid.bases import (
- InferenceContext, copy_context,
- raise_if_nothing_inferred, yes_if_nothing_inferred,
- Instance, YES, BoundMethod,
- Generator,
-)
-from astroid.nodes import const_factory
+from astroid import bases
+from astroid import context as contextmod
+from astroid import exceptions
+from astroid import node_classes
from astroid import nodes
+from astroid import util
def _reflected_name(name):
@@ -85,7 +81,7 @@ _UNARY_OPERATORS = {
def _infer_unary_op(obj, op):
func = _UNARY_OPERATORS[op]
value = func(obj)
- return const_factory(value)
+ return nodes.const_factory(value)
nodes.Tuple.infer_unary_op = lambda self, op: _infer_unary_op(tuple(self.elts), op)
nodes.List.infer_unary_op = lambda self, op: _infer_unary_op(self.elts, op)
@@ -115,45 +111,48 @@ if sys.version_info >= (3, 5):
for _KEY, _IMPL in list(BIN_OP_IMPL.items()):
BIN_OP_IMPL[_KEY + '='] = _IMPL
+
+@bases.yes_if_nothing_inferred
def const_infer_binary_op(self, operator, other, context, _):
not_implemented = nodes.Const(NotImplemented)
if isinstance(other, nodes.Const):
try:
impl = BIN_OP_IMPL[operator]
try:
- yield const_factory(impl(self.value, other.value))
+ yield nodes.const_factory(impl(self.value, other.value))
except TypeError:
# ArithmeticError is not enough: float >> float is a TypeError
yield not_implemented
except Exception: # pylint: disable=broad-except
- yield YES
+ yield util.YES
except TypeError:
yield not_implemented
elif isinstance(self.value, six.string_types) and operator == '%':
# TODO(cpopa): implement string interpolation later on.
- yield YES
+ yield util.YES
else:
yield not_implemented
-nodes.Const.infer_binary_op = yes_if_nothing_inferred(const_infer_binary_op)
+nodes.Const.infer_binary_op = const_infer_binary_op
def _multiply_seq_by_int(self, other, context):
node = self.__class__()
elts = [n for elt in self.elts for n in elt.infer(context)
- if not n is YES] * other.value
+ if not n is util.YES] * other.value
node.elts = elts
return node
+@bases.yes_if_nothing_inferred
def tl_infer_binary_op(self, operator, other, context, method):
not_implemented = nodes.Const(NotImplemented)
if isinstance(other, self.__class__) and operator == '+':
node = self.__class__()
elts = [n for elt in self.elts for n in elt.infer(context)
- if not n is YES]
+ if not n is util.YES]
elts += [n for elt in other.elts for n in elt.infer(context)
- if not n is YES]
+ if not n is util.YES]
node.elts = elts
yield node
elif isinstance(other, nodes.Const) and operator == '*':
@@ -161,24 +160,25 @@ def tl_infer_binary_op(self, operator, other, context, method):
yield not_implemented
return
yield _multiply_seq_by_int(self, other, context)
- elif isinstance(other, Instance) and operator == '*':
+ elif isinstance(other, bases.Instance) and operator == '*':
# Verify if the instance supports __index__.
as_index = class_as_index(other, context)
if not as_index:
- yield YES
+ yield util.YES
else:
yield _multiply_seq_by_int(self, as_index, context)
else:
yield not_implemented
-nodes.Tuple.infer_binary_op = yes_if_nothing_inferred(tl_infer_binary_op)
-nodes.List.infer_binary_op = yes_if_nothing_inferred(tl_infer_binary_op)
+nodes.Tuple.infer_binary_op = tl_infer_binary_op
+nodes.List.infer_binary_op = tl_infer_binary_op
+@bases.yes_if_nothing_inferred
def instance_infer_binary_op(self, operator, other, context, method):
return method.infer_call_result(self, context)
-Instance.infer_binary_op = yes_if_nothing_inferred(instance_infer_binary_op)
+bases.Instance.infer_binary_op = instance_infer_binary_op
# assignment ##################################################################
@@ -199,7 +199,7 @@ def _resolve_looppart(parts, asspath, context):
asspath = asspath[:]
index = asspath.pop(0)
for part in parts:
- if part is YES:
+ if part is util.YES:
continue
# XXX handle __iter__ and log potentially detected errors
if not hasattr(part, 'itered'):
@@ -219,7 +219,7 @@ def _resolve_looppart(parts, asspath, context):
# we achieved to resolved the assignment path,
# don't infer the last part
yield assigned
- elif assigned is YES:
+ elif assigned is util.YES:
break
else:
# we are not yet on the last part of the path
@@ -228,10 +228,11 @@ def _resolve_looppart(parts, asspath, context):
for inferred in _resolve_looppart(assigned.infer(context),
asspath, context):
yield inferred
- except InferenceError:
+ except exceptions.InferenceError:
break
+@bases.raise_if_nothing_inferred
def for_assigned_stmts(self, node, context=None, asspath=None):
if asspath is None:
for lst in self.iter.infer(context):
@@ -243,8 +244,8 @@ def for_assigned_stmts(self, node, context=None, asspath=None):
asspath, context):
yield inferred
-nodes.For.assigned_stmts = raise_if_nothing_inferred(for_assigned_stmts)
-nodes.Comprehension.assigned_stmts = raise_if_nothing_inferred(for_assigned_stmts)
+nodes.For.assigned_stmts = for_assigned_stmts
+nodes.Comprehension.assigned_stmts = for_assigned_stmts
def mulass_assigned_stmts(self, node, context=None, asspath=None):
@@ -252,6 +253,7 @@ def mulass_assigned_stmts(self, node, context=None, asspath=None):
asspath = []
asspath.insert(0, self.elts.index(node))
return self.parent.assigned_stmts(self, context, asspath)
+
nodes.Tuple.assigned_stmts = mulass_assigned_stmts
nodes.List.assigned_stmts = mulass_assigned_stmts
@@ -266,57 +268,70 @@ def _arguments_infer_argname(self, name, context):
# arguments information may be missing, in which case we can't do anything
# more
if not (self.args or self.vararg or self.kwarg):
- yield YES
+ yield util.YES
return
# first argument of instance/class method
if self.args and getattr(self.args[0], 'name', None) == name:
functype = self.parent.type
if functype == 'method':
- yield Instance(self.parent.parent.frame())
+ yield bases.Instance(self.parent.parent.frame())
return
if functype == 'classmethod':
yield self.parent.parent.frame()
return
+
if name == self.vararg:
- vararg = const_factory(())
+ vararg = nodes.const_factory(())
vararg.parent = self
yield vararg
return
if name == self.kwarg:
- kwarg = const_factory({})
+ kwarg = nodes.const_factory({})
kwarg.parent = self
yield kwarg
return
# if there is a default value, yield it. And then yield YES to reflect
# we can't guess given argument value
try:
- context = copy_context(context)
+ context = contextmod.copy_context(context)
for inferred in self.default_value(name).infer(context):
yield inferred
- yield YES
- except NoDefault:
- yield YES
+ yield util.YES
+ except exceptions.NoDefault:
+ yield util.YES
def arguments_assigned_stmts(self, node, context, asspath=None):
if context.callcontext:
# reset call context/name
callcontext = context.callcontext
- context = copy_context(context)
+ context = contextmod.copy_context(context)
context.callcontext = None
- return callcontext.infer_argument(self.parent, node.name, context)
+ # TODO(cpopa): make this an API
+ if context.boundnode is None:
+ boundnode = self.parent.parent.frame()
+ else:
+ boundnode = context.boundnode
+ if self.parent.type == 'method':
+ if not isinstance(boundnode, bases.Instance):
+ boundnode = bases.Instance(boundnode)
+ return callcontext.infer_argument(
+ self.parent, node.name, context, boundnode)
return _arguments_infer_argname(self, node.name, context)
+
nodes.Arguments.assigned_stmts = arguments_assigned_stmts
+@bases.raise_if_nothing_inferred
def assign_assigned_stmts(self, node, context=None, asspath=None):
if not asspath:
yield self.value
return
for inferred in _resolve_asspart(self.value.infer(context), asspath, context):
yield inferred
-nodes.Assign.assigned_stmts = raise_if_nothing_inferred(assign_assigned_stmts)
-nodes.AugAssign.assigned_stmts = raise_if_nothing_inferred(assign_assigned_stmts)
+
+nodes.Assign.assigned_stmts = assign_assigned_stmts
+nodes.AugAssign.assigned_stmts = assign_assigned_stmts
def _resolve_asspart(parts, asspath, context):
@@ -335,7 +350,7 @@ def _resolve_asspart(parts, asspath, context):
# we achieved to resolved the assignment path, don't infer the
# last part
yield assigned
- elif assigned is YES:
+ elif assigned is util.YES:
return
else:
# we are not yet on the last part of the path search on each
@@ -344,24 +359,26 @@ def _resolve_asspart(parts, asspath, context):
for inferred in _resolve_asspart(assigned.infer(context),
asspath, context):
yield inferred
- except InferenceError:
+ except exceptions.InferenceError:
return
+@bases.raise_if_nothing_inferred
def excepthandler_assigned_stmts(self, node, context=None, asspath=None):
- for assigned in unpack_infer(self.type):
+ for assigned in node_classes.unpack_infer(self.type):
if isinstance(assigned, nodes.ClassDef):
- assigned = Instance(assigned)
+ assigned = bases.Instance(assigned)
yield assigned
-nodes.ExceptHandler.assigned_stmts = raise_if_nothing_inferred(excepthandler_assigned_stmts)
+
+nodes.ExceptHandler.assigned_stmts = excepthandler_assigned_stmts
def _infer_context_manager(self, mgr, context):
try:
inferred = next(mgr.infer(context=context))
- except InferenceError:
+ except exceptions.InferenceError:
return
- if isinstance(inferred, Generator):
+ if isinstance(inferred, bases.Generator):
# Check if it is decorated with contextlib.contextmanager.
func = inferred.parent
if not func.decorators:
@@ -389,16 +406,18 @@ def _infer_context_manager(self, mgr, context):
else:
for inferred in yield_point.value.infer(context=context):
yield inferred
- elif isinstance(inferred, Instance):
+ elif isinstance(inferred, bases.Instance):
try:
enter = next(inferred.igetattr('__enter__', context=context))
- except (InferenceError, NotFoundError):
+ except (exceptions.InferenceError, exceptions.NotFoundError):
return
- if not isinstance(enter, BoundMethod):
+ if not isinstance(enter, bases.BoundMethod):
return
for result in enter.infer_call_result(self, context):
yield result
+
+@bases.raise_if_nothing_inferred
def with_assigned_stmts(self, node, context=None, asspath=None):
"""Infer names and other nodes from a *with* statement.
@@ -428,22 +447,28 @@ def with_assigned_stmts(self, node, context=None, asspath=None):
obj = result
for index in asspath:
if not hasattr(obj, 'elts'):
- raise InferenceError
+ raise exceptions.InferenceError
try:
obj = obj.elts[index]
except IndexError:
- raise InferenceError
+ raise exceptions.InferenceError
yield obj
+<<<<<<< variant A
nodes.With.assigned_stmts = raise_if_nothing_inferred(with_assigned_stmts)
+>>>>>>> variant B
+nodes.With.assigned_stmts = with_assigned_stmts
+####### Ancestor
+nodes.With.assigned_stmts = raise_if_nothing_infered(with_assigned_stmts)
+======= end
-@yes_if_nothing_inferred
+@bases.yes_if_nothing_inferred
def starred_assigned_stmts(self, node=None, context=None, asspath=None):
stmt = self.statement()
if not isinstance(stmt, (nodes.Assign, nodes.For)):
- raise InferenceError()
+ raise exceptions.InferenceError()
if isinstance(stmt, nodes.Assign):
value = stmt.value
@@ -451,24 +476,24 @@ def starred_assigned_stmts(self, node=None, context=None, asspath=None):
if sum(1 for node in lhs.nodes_of_class(nodes.Starred)) > 1:
# Too many starred arguments in the expression.
- raise InferenceError()
+ raise exceptions.InferenceError()
if context is None:
- context = InferenceContext()
+ context = contextmod.InferenceContext()
try:
rhs = next(value.infer(context))
- except InferenceError:
- yield YES
+ except exceptions.InferenceError:
+ yield util.YES
return
- if rhs is YES or not hasattr(rhs, 'elts'):
+ if rhs is util.YES or not hasattr(rhs, 'elts'):
# Not interested in inferred values without elts.
- yield YES
+ yield util.YES
return
elts = collections.deque(rhs.elts[:])
if len(lhs.elts) > len(rhs.elts):
# a, *b, c = (1, 2)
- raise InferenceError()
+ raise exceptions.InferenceError()
# Unpack iteratively the values from the rhs of the assignment,
# until the find the starred node. What will remain will
@@ -504,13 +529,13 @@ def class_as_index(node, context):
"""
try:
for inferred in node.igetattr('__index__', context=context):
- if not isinstance(inferred, BoundMethod):
+ if not isinstance(inferred, bases.BoundMethod):
continue
for result in inferred.infer_call_result(node, context=context):
if (isinstance(result, nodes.Const)
and isinstance(result.value, int)):
return result
- except InferenceError:
+ except exceptions.InferenceError:
pass
- \ No newline at end of file
+
diff --git a/astroid/raw_building.py b/astroid/raw_building.py
index 42fc0853..ce80b0e2 100644
--- a/astroid/raw_building.py
+++ b/astroid/raw_building.py
@@ -19,26 +19,23 @@
(build_* functions) or from living object (object_build_* functions)
"""
-__docformat__ = "restructuredtext en"
-
-import sys
+import inspect
import logging
import os
-from os.path import abspath
-from inspect import (getargspec, isdatadescriptor, isfunction, ismethod,
- ismethoddescriptor, isclass, isbuiltin, ismodule,
- isroutine)
+import sys
+import types
+
import six
-from astroid.node_classes import CONST_CLS
-from astroid.nodes import (Module, ClassDef, Const, const_factory, ImportFrom,
- FunctionDef, EmptyNode, Name, Arguments)
-from astroid.bases import BUILTINS, Generator
-from astroid.manager import AstroidManager
+from astroid import bases
+from astroid import manager
+from astroid import node_classes
+from astroid import nodes
-MANAGER = AstroidManager()
-_CONSTANTS = tuple(CONST_CLS) # the keys of CONST_CLS eg python builtin types
+MANAGER = manager.AstroidManager()
+# the keys of CONST_CLS eg python builtin types
+_CONSTANTS = tuple(node_classes.CONST_CLS)
_JYTHON = os.name == 'java'
_BUILTINS = vars(six.moves.builtins)
_LOG = logging.getLogger(__name__)
@@ -48,7 +45,7 @@ def _io_discrepancy(member):
# _io module names itself `io`: http://bugs.python.org/issue18602
member_self = getattr(member, '__self__', None)
return (member_self and
- ismodule(member_self) and
+ inspect.ismodule(member_self) and
member_self.__name__ == '_io' and
member.__module__ == 'io')
@@ -63,58 +60,63 @@ def attach_dummy_node(node, name, object=_marker):
"""create a dummy node and register it in the locals of the given
node with the specified name
"""
- enode = EmptyNode()
+ enode = nodes.EmptyNode()
enode.object = object
_attach_local_node(node, enode, name)
def _has_underlying_object(self):
return hasattr(self, 'object') and self.object is not _marker
-EmptyNode.has_underlying_object = _has_underlying_object
+nodes.EmptyNode.has_underlying_object = _has_underlying_object
def attach_const_node(node, name, value):
"""create a Const node and register it in the locals of the given
node with the specified name
"""
- if not name in node.special_attributes:
- _attach_local_node(node, const_factory(value), name)
+ if name not in node.special_attributes:
+ _attach_local_node(node, nodes.const_factory(value), name)
def attach_import_node(node, modname, membername):
"""create a ImportFrom node and register it in the locals of the given
node with the specified name
"""
- from_node = ImportFrom(modname, [(membername, None)])
+ from_node = nodes.ImportFrom(modname, [(membername, None)])
_attach_local_node(node, from_node, membername)
def build_module(name, doc=None):
"""create and initialize a astroid Module node"""
- return Module(name, doc, package=False, parent=None, pure_python=False)
+ node = nodes.Module(name, doc, pure_python=False)
+ node.package = False
+ node.parent = None
+ return node
+
def build_class(name, basenames=(), doc=None):
"""create and initialize a astroid ClassDef node"""
node = ClassDef(name, doc)
for base in basenames:
- basenode = Name()
+ basenode = nodes.Name()
basenode.name = base
node.bases.append(basenode)
basenode.parent = node
return node
+
def build_function(name, args=None, defaults=None, flag=0, doc=None):
"""create and initialize a astroid FunctionDef node"""
args, defaults = args or [], defaults or []
# first argument is now a list of decorators
- func = FunctionDef(name, doc)
- func.args = argsnode = Arguments()
+ func = nodes.FunctionDef(name, doc)
+ func.args = argsnode = nodes.Arguments()
argsnode.args = []
for arg in args:
- argsnode.args.append(Name())
+ argsnode.args.append(nodes.Name())
argsnode.args[-1].name = arg
argsnode.args[-1].parent = argsnode
argsnode.defaults = []
for default in defaults:
- argsnode.defaults.append(const_factory(default))
+ argsnode.defaults.append(nodes.const_factory(default))
argsnode.defaults[-1].parent = argsnode
argsnode.kwarg = None
argsnode.vararg = None
@@ -126,7 +128,7 @@ def build_function(name, args=None, defaults=None, flag=0, doc=None):
def build_from_import(fromname, names):
"""create and initialize an astroid ImportFrom import statement"""
- return ImportFrom(fromname, [(name, None) for name in names])
+ return nodes.ImportFrom(fromname, [(name, None) for name in names])
def register_arguments(func, args=None):
"""add given arguments to local
@@ -141,32 +143,37 @@ def register_arguments(func, args=None):
if func.args.kwarg:
func.set_local(func.args.kwarg, func.args)
for arg in args:
- if isinstance(arg, Name):
+ if isinstance(arg, nodes.Name):
func.set_local(arg.name, arg)
else:
register_arguments(func, arg.elts)
+
def object_build_class(node, member, localname):
"""create astroid for a living class object"""
basenames = [base.__name__ for base in member.__bases__]
return _base_class_object_build(node, member, basenames,
localname=localname)
+
def object_build_function(node, member, localname):
"""create astroid for a living function object"""
- args, varargs, varkw, defaults = getargspec(member)
+ args, varargs, varkw, defaults = inspect.getargspec(member)
if varargs is not None:
args.append(varargs)
if varkw is not None:
args.append(varkw)
func = build_function(getattr(member, '__name__', None) or localname, args,
- defaults, six.get_function_code(member).co_flags, member.__doc__)
+ defaults, six.get_function_code(member).co_flags,
+ member.__doc__)
node.add_local_node(func, localname)
+
def object_build_datadescriptor(node, member, name):
"""create astroid for a living data descriptor object"""
return _base_class_object_build(node, member, [], name)
+
def object_build_methoddescriptor(node, member, localname):
"""create astroid for a living method descriptor object"""
# FIXME get arguments ?
@@ -177,6 +184,7 @@ def object_build_methoddescriptor(node, member, localname):
func.args.args = None
node.add_local_node(func, localname)
+
def _base_class_object_build(node, member, basenames, name=None, localname=None):
"""create astroid for a living class object, with a given set of base names
(e.g. ancestors)
@@ -198,7 +206,7 @@ def _base_class_object_build(node, member, basenames, name=None, localname=None)
pass
else:
for name, obj in instdict.items():
- valnode = EmptyNode()
+ valnode = nodes.EmptyNode()
valnode.object = obj
valnode.parent = klass
valnode.lineno = 1
@@ -250,7 +258,7 @@ class InspectBuilder(object):
except AttributeError:
# in jython, java modules have no __doc__ (see #109562)
node = build_module(modname)
- node.file = node.path = path and abspath(path) or path
+ node.file = node.path = path and os.path.abspath(path) or path
node.name = modname
MANAGER.cache_module(node)
node.package = hasattr(module, '__path__')
@@ -272,21 +280,21 @@ class InspectBuilder(object):
# damned ExtensionClass.Base, I know you're there !
attach_dummy_node(node, name)
continue
- if ismethod(member):
+ if inspect.ismethod(member):
member = six.get_method_function(member)
- if isfunction(member):
+ if inspect.isfunction(member):
_build_from_function(node, name, member, self._module)
- elif isbuiltin(member):
+ elif inspect.isbuiltin(member):
if (not _io_discrepancy(member) and
self.imported_member(node, member, name)):
continue
object_build_methoddescriptor(node, member, name)
- elif isclass(member):
+ elif inspect.isclass(member):
if self.imported_member(node, member, name):
continue
if member in self._done:
class_node = self._done[member]
- if not class_node in node.locals.get(name, ()):
+ if class_node not in node.locals.get(name, ()):
node.add_local_node(class_node, name)
else:
class_node = object_build_class(node, member, name)
@@ -294,15 +302,15 @@ class InspectBuilder(object):
self.object_build(class_node, member)
if name == '__class__' and class_node.parent is None:
class_node.parent = self._done[self._module]
- elif ismethoddescriptor(member):
+ elif inspect.ismethoddescriptor(member):
assert isinstance(member, object)
object_build_methoddescriptor(node, member, name)
- elif isdatadescriptor(member):
+ elif inspect.isdatadescriptor(member):
assert isinstance(member, object)
object_build_datadescriptor(node, member, name)
elif isinstance(member, _CONSTANTS):
attach_const_node(node, name, member)
- elif isroutine(member):
+ elif inspect.isroutine(member):
# This should be called for Jython, where some builtin
# methods aren't catched by isbuiltin branch.
_build_from_function(node, name, member, self._module)
@@ -327,7 +335,7 @@ class InspectBuilder(object):
# Python 2.5.1 (r251:54863, Sep 1 2010, 22:03:14)
# >>> print object.__new__.__module__
# None
- modname = BUILTINS
+ modname = six.moves.builtins.__name__
else:
attach_dummy_node(node, name, member)
return True
@@ -359,10 +367,10 @@ def _astroid_bootstrapping(astroid_builtin=None):
# this boot strapping is necessary since we need the Const nodes to
# inspect_build builtins, and then we can proxy Const
if astroid_builtin is None:
- from logilab.common.compat import builtins
+ from six.moves import builtins
astroid_builtin = Astroid_BUILDER.inspect_build(builtins)
- for cls, node_cls in CONST_CLS.items():
+ for cls, node_cls in node_classes.CONST_CLS.items():
if cls is type(None):
proxy = build_class('NoneType')
proxy.parent = astroid_builtin
@@ -383,10 +391,9 @@ _astroid_bootstrapping()
# infinite recursion (see https://bugs.launchpad.net/pylint/+bug/456870)
def _set_proxied(const):
return _CONST_PROXY[const.value.__class__]
-Const._proxied = property(_set_proxied)
+nodes.Const._proxied = property(_set_proxied)
-from types import GeneratorType
-_GeneratorType = ClassDef(GeneratorType.__name__, GeneratorType.__doc__)
-_GeneratorType.parent = MANAGER.astroid_cache[BUILTINS]
-Generator._proxied = _GeneratorType
-Astroid_BUILDER.object_build(Generator._proxied, GeneratorType)
+_GeneratorType = nodes.ClassDef(types.GeneratorType.__name__, types.GeneratorType.__doc__)
+_GeneratorType.parent = MANAGER.astroid_cache[six.moves.builtins.__name__]
+bases.Generator._proxied = _GeneratorType
+Astroid_BUILDER.object_build(bases.Generator._proxied, types.GeneratorType)
diff --git a/astroid/rebuilder.py b/astroid/rebuilder.py
index 8b2ef48f..6399ef01 100644
--- a/astroid/rebuilder.py
+++ b/astroid/rebuilder.py
@@ -18,62 +18,51 @@
"""this module contains utilities for rebuilding a _ast tree in
order to get a single Astroid representation
"""
-from astroid.as_string import dump
+import _ast
import sys
-from _ast import (
- Expr, Str,
- # binary operators
- Add, BinOp, Div, FloorDiv, Mod, Mult, Pow, Sub, BitAnd, BitOr, BitXor,
- LShift, RShift,
- # logical operators
- And, Or,
- # unary operators
- UAdd, USub, Not, Invert,
- # comparison operators
- Eq, Gt, GtE, In, Is, IsNot, Lt, LtE, NotEq, NotIn,
- )
-from astroid import nodes
from astroid import astpeephole
+from astroid import nodes
+
+
-_BIN_OP_CLASSES = {Add: '+',
- BitAnd: '&',
- BitOr: '|',
- BitXor: '^',
- Div: '/',
- FloorDiv: '//',
- Mod: '%',
- Mult: '*',
- Pow: '**',
- Sub: '-',
- LShift: '<<',
- RShift: '>>',
+_BIN_OP_CLASSES = {_ast.Add: '+',
+ _ast.BitAnd: '&',
+ _ast.BitOr: '|',
+ _ast.BitXor: '^',
+ _ast.Div: '/',
+ _ast.FloorDiv: '//',
+ _ast.Mod: '%',
+ _ast.Mult: '*',
+ _ast.Pow: '**',
+ _ast.Sub: '-',
+ _ast.LShift: '<<',
+ _ast.RShift: '>>',
}
if sys.version_info >= (3, 5):
- from _ast import MatMult
- _BIN_OP_CLASSES[MatMult] = '@'
+ _BIN_OP_CLASSES[_ast.MatMult] = '@'
-_BOOL_OP_CLASSES = {And: 'and',
- Or: 'or',
+_BOOL_OP_CLASSES = {_ast.And: 'and',
+ _ast.Or: 'or',
}
-_UNARY_OP_CLASSES = {UAdd: '+',
- USub: '-',
- Not: 'not',
- Invert: '~',
+_UNARY_OP_CLASSES = {_ast.UAdd: '+',
+ _ast.USub: '-',
+ _ast.Not: 'not',
+ _ast.Invert: '~',
}
-_CMP_OP_CLASSES = {Eq: '==',
- Gt: '>',
- GtE: '>=',
- In: 'in',
- Is: 'is',
- IsNot: 'is not',
- Lt: '<',
- LtE: '<=',
- NotEq: '!=',
- NotIn: 'not in',
+_CMP_OP_CLASSES = {_ast.Eq: '==',
+ _ast.Gt: '>',
+ _ast.GtE: '>=',
+ _ast.In: 'in',
+ _ast.Is: 'is',
+ _ast.IsNot: 'is not',
+ _ast.Lt: '<',
+ _ast.LtE: '<=',
+ _ast.NotEq: '!=',
+ _ast.NotIn: 'not in',
}
CONST_NAME_TRANSFORMS = {'None': None,
@@ -93,7 +82,7 @@ PY34 = sys.version_info >= (3, 4)
def _get_doc(node):
try:
- if isinstance(node.body[0], Expr) and isinstance(node.body[0].value, Str):
+ if isinstance(node.body[0], _ast.Expr) and isinstance(node.body[0].value, _ast.Str):
doc = node.body[0].value.s
node.body = node.body[1:]
return node, doc
@@ -111,7 +100,6 @@ class TreeRebuilder(object):
self._import_from_nodes = []
self._delayed_assattr = []
self._visit_meths = {}
- self._transform = manager.transform
self._peepholer = astpeephole.ASTPeepholeOptimizer()
def visit_module(self, node, modname, modpath, package):
@@ -131,7 +119,7 @@ class TreeRebuilder(object):
visit_name = 'visit_' + REDIRECT.get(cls_name, cls_name).lower()
visit_method = getattr(self, visit_name)
self._visit_meths[cls] = visit_method
- return self._transform(visit_method(node, parent, assign_ctx))
+ return visit_method(node, parent, assign_ctx)
def _save_assignment(self, node, name=None):
"""save assignement situation since node.parent is not available yet"""
@@ -259,7 +247,7 @@ class TreeRebuilder(object):
def visit_binop(self, node, parent, assign_ctx=None):
"""visit a BinOp node by returning a fresh instance of it"""
- if isinstance(node.left, BinOp) and self._manager.optimize_ast:
+ if isinstance(node.left, _ast.BinOp) and self._manager.optimize_ast:
# Optimize BinOp operations in order to remove
# redundant recursion. For instance, if the
# following code is parsed in order to obtain
@@ -272,7 +260,7 @@ class TreeRebuilder(object):
# problem for the correctness of the program).
#
# ("a" + "b" + # one thousand more + "c")
- newnode = self._peepholer.optimize_binop(node, parent)
+ newnode = self._peepholer.optimize_binop(node)
if newnode:
return newnode
diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py
index 8d053c8f..fbe58759 100644
--- a/astroid/scoped_nodes.py
+++ b/astroid/scoped_nodes.py
@@ -28,13 +28,15 @@ import sys
import warnings
import six
-from logilab.common import decorators as decorators_mod
from astroid import bases
+from astroid import context as contextmod
from astroid import exceptions
from astroid import manager
from astroid import mixins
from astroid import node_classes
+from astroid import decorators as decorators_mod
+from astroid import util
BUILTINS = six.moves.builtins.__name__
@@ -244,7 +246,6 @@ class LocalsDictNodeNG(node_classes.LookupMixIn, bases.NodeNG):
def __contains__(self, name):
return name in self.locals
- has_key = __contains__
class Module(LocalsDictNodeNG):
@@ -376,7 +377,7 @@ class Module(LocalsDictNodeNG):
"""inferred getattr"""
# set lookup name since this is necessary to infer on import nodes for
# instance
- context = bases.copy_context(context)
+ context = contextmod.copy_context(context)
context.lookupname = name
try:
return bases._infer_stmts(self.getattr(name, context),
@@ -462,19 +463,6 @@ class Module(LocalsDictNodeNG):
It doesn't include the '__builtins__' name which is added by the
current CPython implementation of wildcard imports.
"""
- # take advantage of a living module if it exists
- try:
- living = sys.modules[self.name]
- except KeyError:
- pass
- else:
- try:
- return living.__all__
- except AttributeError:
- return [name for name in living.__dict__.keys()
- if not name.startswith('_')]
- # else lookup the astroid
- #
# We separate the different steps of lookup in try/excepts
# to avoid catching too many Exceptions
default = [name for name in self.keys() if not name.startswith('_')]
@@ -566,7 +554,7 @@ class DictComp(ComprehensionScope):
self.generators = generators
def bool_value(self):
- return bases.YES
+ return util.YES
class SetComp(ComprehensionScope):
@@ -587,7 +575,7 @@ class SetComp(ComprehensionScope):
self.generators = generators
def bool_value(self):
- return bases.YES
+ return util.YES
class _ListComp(bases.NodeNG):
@@ -601,7 +589,7 @@ class _ListComp(bases.NodeNG):
self.generators = generators
def bool_value(self):
- return bases.YES
+ return util.YES
if six.PY3:
@@ -641,50 +629,6 @@ def _infer_decorator_callchain(node):
return 'staticmethod'
-def _function_type(self):
- """
- FunctionDef type, possible values are:
- method, function, staticmethod, classmethod.
- """
- # Can't infer that this node is decorated
- # with a subclass of `classmethod` where `type` is first set,
- # so do it here.
- if self.decorators:
- for node in self.decorators.nodes:
- 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
-
- if not isinstance(inferred, 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
- return self._type
-
-
class Lambda(LocalsDictNodeNG, mixins.FilterStmtsMixin):
_astroid_fields = ('args', 'body',)
_other_fields = ('locals',)
@@ -759,16 +703,13 @@ class FunctionDef(bases.Statement, Lambda):
special_attributes = set(('__name__', '__doc__', '__dict__'))
is_function = True
# attributes below are set by the builder module or by raw factories
- type = decorators_mod.cachedproperty(_function_type)
-
- _type = "function"
+ decorators = None
_other_fields = ('locals', 'name', 'doc', '_type', 'decorators')
def __init__(self, name=None, doc=None, lineno=None,
col_offset=None, parent=None):
self.name = name
self.doc = doc
- self.extra_decorators = []
self.instance_attrs = {}
super(FunctionDef, self).__init__(lineno, col_offset, parent)
if parent:
@@ -794,6 +735,100 @@ class FunctionDef(bases.Statement, Lambda):
self.returns = returns
@decorators_mod.cachedproperty
+ def extra_decorators(self):
+ """Get the extra decorators that this function can have
+
+ Additional decorators are considered when they are used as
+ assignments, as in `method = staticmethod(method)`.
+ The property will return all the callables that are used for
+ decoration.
+ """
+ frame = self.parent.frame()
+ if not isinstance(frame, Class):
+ return []
+
+ decorators = []
+ for assign in frame.nodes_of_class(node_classes.Assign):
+ if (isinstance(assign.value, node_classes.CallFunc)
+ and isinstance(assign.value.func, node_classes.Name)):
+ for assign_node in assign.targets:
+ if not isinstance(assign_node, node_classes.AssName):
+ # Support only `name = callable(name)`
+ continue
+
+ if assign_node.name != self.name:
+ # Interested only in the assignment nodes that
+ # decorates the current method.
+ continue
+ try:
+ meth = frame[self.name]
+ except KeyError:
+ continue
+ else:
+ if isinstance(meth, Function):
+ decorators.append(assign.value)
+ return decorators
+
+ @decorators_mod.cachedproperty
+ def type(self):
+ """Get the function type for this node.
+
+ Possible values are: method, function, staticmethod, classmethod.
+ """
+ builtin_descriptors = {'classmethod', 'staticmethod'}
+
+ for decorator in self.extra_decorators:
+ if decorator.func.name in builtin_descriptors:
+ return decorator.func.name
+
+ frame = self.parent.frame()
+ type_name = 'function'
+ if isinstance(frame, Class):
+ if self.name == '__new__':
+ return'classmethod'
+ 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.CallFunc):
+ # 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 infered in node.infer():
+ # Check to see if this returns a static or a class method.
+ _type = _infer_decorator_callchain(infered)
+ if _type is not None:
+ return _type
+
+ if not isinstance(infered, Class):
+ continue
+ for ancestor in infered.ancestors():
+ if not isinstance(ancestor, Class):
+ 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
+ return type_name
+
+ @decorators_mod.cachedproperty
def fromlineno(self):
# lineno is the line number of the first decorator, we want the def
# statement lineno
@@ -912,7 +947,7 @@ class FunctionDef(bases.Statement, Lambda):
c.hide = True
c.parent = self
class_bases = [next(b.infer(context)) for b in caller.args[1:]]
- c.bases = [base for base in class_bases if base != bases.YES]
+ c.bases = [base for base in class_bases if base != util.YES]
c._metaclass = metaclass
yield c
return
@@ -925,7 +960,7 @@ class FunctionDef(bases.Statement, Lambda):
for inferred in returnnode.value.infer(context):
yield inferred
except exceptions.InferenceError:
- yield bases.YES
+ yield util.YES
def bool_value(self):
return True
@@ -962,7 +997,7 @@ def _is_metaclass(klass, seen=None):
if isinstance(baseobj, bases.Instance):
# not abstract
return False
- if baseobj is bases.YES:
+ if baseobj is util.YES:
continue
if baseobj is klass:
continue
@@ -1011,7 +1046,24 @@ def _class_type(klass, ancestors=None):
return klass._type
-class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
+def get_wrapping_class(node):
+ """Obtain the class that *wraps* this node
+
+ We consider that a class wraps a node if the class
+ is a parent for the said node.
+ """
+
+ klass = node.frame()
+ while klass is not None and not isinstance(klass, Class):
+ if klass.parent is None:
+ klass = None
+ else:
+ klass = klass.parent.frame()
+ return klass
+
+
+
+class Class(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
# some of the attributes below are set by the builder module or
# by a raw factories
@@ -1054,14 +1106,14 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
def _newstyle_impl(self, context=None):
if context is None:
- context = bases.InferenceContext()
+ context = contextmod.InferenceContext()
if self._newstyle is not None:
return self._newstyle
for base in self.ancestors(recurs=False, context=context):
if base._newstyle_impl(context):
self._newstyle = True
break
- klass = self._explicit_metaclass()
+ klass = self.declared_metaclass()
# could be any callable, we'd need to infer the result of klass(name,
# bases, dict). punt if it's not a class node.
if klass is not None and isinstance(klass, ClassDef):
@@ -1113,7 +1165,7 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
isinstance(name_node.value, six.string_types)):
name = name_node.value
else:
- return bases.YES
+ return util.YES
result = ClassDef(name, None)
@@ -1125,7 +1177,7 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
# There is currently no AST node that can represent an 'unknown'
# node (YES is not an AST node), therefore we simply return YES here
# although we know at least the name of the class.
- return bases.YES
+ return util.YES
# Get the members of the class
try:
@@ -1192,7 +1244,7 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
# FIXME: inference make infinite loops possible here
yielded = set([self])
if context is None:
- context = bases.InferenceContext()
+ context = contextmod.InferenceContext()
if six.PY3:
if not self.bases and self.qname() != 'builtins.object':
yield builtin_lookup("object")[1][0]
@@ -1294,12 +1346,18 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
"""return Instance of ClassDef node, else return self"""
return bases.Instance(self)
- def getattr(self, name, context=None):
- """this method doesn't look in the instance_attrs dictionary since it's
- done by an Instance proxy at inference time.
+ def getattr(self, name, context=None, class_context=True):
+ """Get an attribute from this class, using Python's attribute semantic
+ This method doesn't look in the instance_attrs dictionary
+ since it's done by an Instance proxy at inference time.
It may return a YES object if the attribute has not been actually
- found but a __getattr__ or __getattribute__ method is defined
+ found but a __getattr__ or __getattribute__ method is defined.
+ If *class_context* is given, then it's considered that the attribute
+ is accessed from a class context, e.g. Class.attribute, otherwise
+ it might have been accessed from an instance as well.
+ If *class_context* is used in that case, then a lookup in the
+ implicit metaclass and the explicit metaclass will be done.
"""
values = self.locals.get(name, [])
if name in self.special_attributes:
@@ -1318,17 +1376,62 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
values = list(values)
for classnode in self.ancestors(recurs=True, context=context):
values += classnode.locals.get(name, [])
+
+ if class_context:
+ values += self._metaclass_lookup_attribute(name, context)
if not values:
raise exceptions.NotFoundError(name)
return values
+ def _metaclass_lookup_attribute(self, name, context):
+ """Search the given name in the implicit and the explicit metaclass."""
+ attrs = set()
+ implicit_meta = self.implicit_metaclass()
+ metaclass = self.metaclass()
+ for cls in {implicit_meta, metaclass}:
+ if cls and cls != self:
+ cls_attributes = self._get_attribute_from_metaclass(
+ cls, name, context)
+ attrs.update(set(cls_attributes))
+ return attrs
+
+ def _get_attribute_from_metaclass(self, cls, name, context):
+ try:
+ attrs = cls.getattr(name, context=context,
+ class_context=True)
+ except exceptions.NotFoundError:
+ return
+
+ for attr in bases._infer_stmts(attrs, context, frame=cls):
+ if not isinstance(attr, Function):
+ yield attr
+ continue
+
+ if bases._is_property(attr):
+ # TODO(cpopa): don't use a private API.
+ for infered in attr.infer_call_result(self, context):
+ yield infered
+ continue
+ if attr.type == 'classmethod':
+ # If the method is a classmethod, then it will
+ # be bound to the metaclass, not to the class
+ # from where the attribute is retrieved.
+ # get_wrapping_class could return None, so just
+ # default to the current class.
+ frame = get_wrapping_class(attr) or self
+ yield bases.BoundMethod(attr, frame)
+ elif attr.type == 'staticmethod':
+ yield attr
+ else:
+ yield bases.BoundMethod(attr, self)
+
def igetattr(self, name, context=None):
"""inferred getattr, need special treatment in class to handle
descriptors
"""
# set lookup name since this is necessary to infer on import nodes for
# instance
- context = bases.copy_context(context)
+ context = contextmod.copy_context(context)
context.lookupname = name
try:
for inferred in bases._infer_stmts(self.getattr(name, context),
@@ -1341,13 +1444,13 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
except exceptions.NotFoundError:
yield inferred
else:
- yield bases.YES
+ yield util.YES
else:
yield function_to_method(inferred, self)
except exceptions.NotFoundError:
if not name.startswith('__') and self.has_dynamic_getattr(context):
# class handle some dynamic attributes, return a YES object
- yield bases.YES
+ yield util.YES
else:
raise exceptions.InferenceError(name)
@@ -1405,11 +1508,10 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
return builtin_lookup('type')[1][0]
_metaclass = None
- def _explicit_metaclass(self):
- """ Return the explicit defined metaclass
- for the current class.
+ def declared_metaclass(self):
+ """Return the explicit declared metaclass for the current class.
- An explicit defined metaclass is defined
+ An explicit declared metaclass is defined
either by passing the ``metaclass`` keyword argument
in the class definition line (Python 3) or (Python 2) by
having a ``__metaclass__`` class attribute, or if there are
@@ -1429,7 +1531,7 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
# Expects this from Py3k TreeRebuilder
try:
return next(node for node in self._metaclass.infer()
- if node is not bases.YES)
+ if node is not util.YES)
except (exceptions.InferenceError, StopIteration):
return None
if six.PY3:
@@ -1452,18 +1554,24 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
inferred = next(assignment.infer())
except exceptions.InferenceError:
return
+<<<<<<< variant A
if inferred is bases.YES: # don't expose this
+>>>>>>> variant B
+ if infered is util.YES: # don't expose this
+####### Ancestor
+ if infered is bases.YES: # don't expose this
+======= end
return None
return inferred
def metaclass(self):
- """ Return the metaclass of this class.
+ """Return the metaclass of this class.
If this class does not define explicitly a metaclass,
then the first defined metaclass in ancestors will be used
instead.
"""
- klass = self._explicit_metaclass()
+ klass = self.declared_metaclass()
if klass is None:
for parent in self.ancestors():
klass = parent.metaclass()
@@ -1503,7 +1611,7 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
values = [item[0] for item in slots.items]
else:
values = slots.itered()
- if values is bases.YES:
+ if values is util.YES:
continue
if not values:
# Stop the iteration, because the class
@@ -1513,7 +1621,7 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
for elt in values:
try:
for inferred in elt.infer():
- if inferred is bases.YES:
+ if inferred is util.YES:
continue
if (not isinstance(inferred, node_classes.Const) or
not isinstance(inferred.value,
@@ -1566,7 +1674,7 @@ class ClassDef(bases.Statement, LocalsDictNodeNG, mixins.FilterStmtsMixin):
# only in SomeClass.
if context is None:
- context = bases.InferenceContext()
+ context = contextmod.InferenceContext()
if six.PY3:
if not self.bases and self.qname() != 'builtins.object':
yield builtin_lookup("object")[1][0]
diff --git a/astroid/tests/testdata/python2/data/module.py b/astroid/tests/testdata/python2/data/module.py
index dc206e22..6a67b9b6 100644
--- a/astroid/tests/testdata/python2/data/module.py
+++ b/astroid/tests/testdata/python2/data/module.py
@@ -2,7 +2,7 @@
"""
__revision__ = '$Id: module.py,v 1.2 2005-11-02 11:56:54 syt Exp $'
-from logilab.common.shellutils import ProgressBar as pb
+from astroid.node_classes import Name as NameNode
from astroid import modutils
from astroid.utils import *
import os.path
diff --git a/astroid/tests/testdata/python2/data/notamodule/file.py b/astroid/tests/testdata/python2/data/notamodule/file.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/astroid/tests/testdata/python2/data/notamodule/file.py
diff --git a/astroid/tests/testdata/python3/data/module.py b/astroid/tests/testdata/python3/data/module.py
index ec5b64aa..6b47d9d5 100644
--- a/astroid/tests/testdata/python3/data/module.py
+++ b/astroid/tests/testdata/python3/data/module.py
@@ -2,7 +2,7 @@
"""
__revision__ = '$Id: module.py,v 1.2 2005-11-02 11:56:54 syt Exp $'
-from logilab.common.shellutils import ProgressBar as pb
+from astroid.node_classes import Name as NameNode
from astroid import modutils
from astroid.utils import *
import os.path
diff --git a/astroid/tests/testdata/python3/data/notamodule/file.py b/astroid/tests/testdata/python3/data/notamodule/file.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/astroid/tests/testdata/python3/data/notamodule/file.py
diff --git a/astroid/tests/unittest_brain.py b/astroid/tests/unittest_brain.py
index ddae51f9..06883a2f 100644
--- a/astroid/tests/unittest_brain.py
+++ b/astroid/tests/unittest_brain.py
@@ -25,6 +25,7 @@ from astroid import bases
from astroid import builder
from astroid import nodes
from astroid import test_utils
+from astroid import util
import astroid
@@ -105,7 +106,7 @@ class NamedTupleTest(unittest.TestCase):
def foo(fields):
return __(namedtuple("foo", fields))
""")
- self.assertIs(bases.YES, next(klass.infer()))
+ self.assertIs(util.YES, next(klass.infer()))
@unittest.skipIf(sys.version_info[0] > 2,
'namedtuple inference is broken on Python 3')
@@ -137,7 +138,8 @@ class NamedTupleTest(unittest.TestCase):
class ModuleExtenderTest(unittest.TestCase):
def testExtensionModules(self):
- for extender, _ in MANAGER.transforms[nodes.Module]:
+ transformer = MANAGER._transform
+ for extender, _ in transformer.transforms[nodes.Module]:
n = nodes.Module('__main__', None)
extender(n)
diff --git a/astroid/tests/unittest_builder.py b/astroid/tests/unittest_builder.py
index 252f096f..84bf50c2 100644
--- a/astroid/tests/unittest_builder.py
+++ b/astroid/tests/unittest_builder.py
@@ -23,12 +23,12 @@ import unittest
import six
-from astroid import bases
from astroid import builder
from astroid import exceptions
from astroid import manager
from astroid import nodes
from astroid import test_utils
+from astroid import util
from astroid.tests import resources
MANAGER = manager.AstroidManager()
@@ -482,7 +482,7 @@ class BuilderTest(unittest.TestCase):
n = test_utils.get_name_node(astroid, 'n')
self.assertIsNot(n.scope(), astroid)
self.assertEqual([i.__class__ for i in n.infer()],
- [bases.YES.__class__])
+ [util.YES.__class__])
def test_no_future_imports(self):
mod = builder.parse("import sys")
@@ -567,19 +567,6 @@ class BuilderTest(unittest.TestCase):
self.assertIsInstance(chain, nodes.Const)
self.assertEqual(chain.value, 'None')
- def test_lgc_classproperty(self):
- '''test expected values of constants after rebuilding'''
- code = '''
- from logilab.common.decorators import classproperty
-
- class A(object):
- @classproperty
- def hop(cls): #@
- return None
- '''
- method = test_utils.extract_node(code)
- self.assertEqual('classmethod', method.type)
-
def test_not_implemented(self):
node = test_utils.extract_node('''
NotImplemented #@
@@ -615,9 +602,9 @@ class FileBuildTest(unittest.TestCase):
_locals = module.locals
self.assertIs(_locals, module.globals)
keys = sorted(_locals.keys())
- should = ['MY_DICT', 'YO', 'YOUPI',
+ should = ['MY_DICT', 'NameNode', 'YO', 'YOUPI',
'__revision__', 'global_access', 'modutils', 'four_args',
- 'os', 'redirect', 'pb']
+ 'os', 'redirect']
should.sort()
self.assertEqual(keys, sorted(should))
diff --git a/astroid/tests/unittest_helpers.py b/astroid/tests/unittest_helpers.py
index a69c1361..3065250c 100644
--- a/astroid/tests/unittest_helpers.py
+++ b/astroid/tests/unittest_helpers.py
@@ -21,12 +21,12 @@ import unittest
import six
from six.moves import builtins
-from astroid import bases
from astroid import builder
from astroid import helpers
from astroid import manager
from astroid import raw_building
from astroid import test_utils
+from astroid import util
class TestHelpers(unittest.TestCase):
@@ -154,7 +154,7 @@ class TestHelpers(unittest.TestCase):
from unknown import Unknown
u = Unknown #@
''')
- self.assertEqual(helpers.object_type(node), bases.YES)
+ self.assertEqual(helpers.object_type(node), util.YES)
def test_object_type_too_many_types(self):
node = test_utils.extract_node('''
@@ -166,7 +166,7 @@ class TestHelpers(unittest.TestCase):
return 1
test(Unknown) #@
''')
- self.assertEqual(helpers.object_type(node), bases.YES)
+ self.assertEqual(helpers.object_type(node), util.YES)
def test_is_subtype(self):
ast_nodes = test_utils.extract_node('''
@@ -216,8 +216,8 @@ class TestHelpers(unittest.TestCase):
class F(D, E): pass #@
''')
self.assertFalse(helpers.is_subtype(cls_e, cls_f))
- self.assertEqual(helpers.is_subtype(cls_f, cls_e), bases.YES)
- self.assertEqual(helpers.is_supertype(cls_e, cls_f), bases.YES)
+ self.assertEqual(helpers.is_subtype(cls_f, cls_e), util.YES)
+ self.assertEqual(helpers.is_supertype(cls_e, cls_f), util.YES)
self.assertFalse(helpers.is_supertype(cls_f, cls_e))
def test_is_subtype_supertype_unknown_bases(self):
diff --git a/astroid/tests/unittest_inference.py b/astroid/tests/unittest_inference.py
index 4674e92b..5fb85430 100644
--- a/astroid/tests/unittest_inference.py
+++ b/astroid/tests/unittest_inference.py
@@ -27,10 +27,11 @@ import six
from astroid import InferenceError, builder, nodes
from astroid.builder import parse
from astroid.inference import infer_end as inference_infer_end
-from astroid.bases import YES, Instance, BoundMethod, UnboundMethod,\
+from astroid.bases import Instance, BoundMethod, UnboundMethod,\
path_wrapper, BUILTINS
from astroid import objects
from astroid import test_utils
+from astroid import util
from astroid.tests import resources
@@ -307,7 +308,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
self.assertIsInstance(obj1, nodes.Const)
self.assertEqual(obj1.value, 0)
obj1 = next(inferred)
- self.assertIs(obj1, YES, obj1)
+ self.assertIs(obj1, util.YES, obj1)
self.assertRaises(StopIteration, partial(next, inferred))
def test_args_default_inference2(self):
@@ -316,13 +317,13 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
self.assertIsInstance(obj1, nodes.Const)
self.assertEqual(obj1.value, 4)
obj1 = next(inferred)
- self.assertIs(obj1, YES, obj1)
+ self.assertIs(obj1, util.YES, obj1)
self.assertRaises(StopIteration, partial(next, inferred))
def test_inference_restrictions(self):
inferred = test_utils.get_name_node(self.ast['C']['meth1'], 'arg1').infer()
obj1 = next(inferred)
- self.assertIs(obj1, YES, obj1)
+ self.assertIs(obj1, util.YES, obj1)
self.assertRaises(StopIteration, partial(next, inferred))
def test_ancestors_inference(self):
@@ -544,7 +545,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
ast = parse(code, __name__)
xxx = ast['xxx']
self.assertSetEqual({n.__class__ for n in xxx.inferred()},
- {nodes.Const, YES.__class__})
+ {nodes.Const, util.YES.__class__})
def test_method_argument(self):
code = '''
@@ -560,13 +561,13 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
ast = parse(code, __name__)
arg = test_utils.get_name_node(ast['ErudiEntitySchema']['__init__'], 'e_type')
self.assertEqual([n.__class__ for n in arg.infer()],
- [YES.__class__])
+ [util.YES.__class__])
arg = test_utils.get_name_node(ast['ErudiEntitySchema']['__init__'], 'kwargs')
self.assertEqual([n.__class__ for n in arg.infer()],
[nodes.Dict])
arg = test_utils.get_name_node(ast['ErudiEntitySchema']['meth'], 'e_type')
self.assertEqual([n.__class__ for n in arg.infer()],
- [YES.__class__])
+ [util.YES.__class__])
arg = test_utils.get_name_node(ast['ErudiEntitySchema']['meth'], 'args')
self.assertEqual([n.__class__ for n in arg.infer()],
[nodes.Tuple])
@@ -704,7 +705,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
for node in ast_nodes[:3]:
self.assertRaises(InferenceError, next, node.infer())
for node in ast_nodes[3:]:
- self.assertEqual(next(node.infer()), YES)
+ self.assertEqual(next(node.infer()), util.YES)
def test_bytes_subscript(self):
node = test_utils.extract_node('''b'a'[0]''')
@@ -947,7 +948,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
self.assertEqual(first.value, 43)
second = next(ast_nodes[1].infer())
- self.assertEqual(second, YES)
+ self.assertEqual(second, util.YES)
def test_binary_op_other_type_using_reflected_operands(self):
ast_nodes = test_utils.extract_node('''
@@ -958,7 +959,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
1 + A() #@
''')
first = next(ast_nodes[0].infer())
- self.assertEqual(first, YES)
+ self.assertEqual(first, util.YES)
second = next(ast_nodes[1].infer())
self.assertIsInstance(second, nodes.Const)
@@ -972,7 +973,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
1 + A() #@
''')
first = next(ast_node.infer())
- self.assertEqual(first, YES)
+ self.assertEqual(first, util.YES)
def test_binary_op_list_mul(self):
for code in ('a = [[]] * 2', 'a = 2 * [[]]'):
@@ -987,12 +988,28 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
def test_binary_op_list_mul_none(self):
'test correct handling on list multiplied by None'
ast = builder.string_build('a = [1] * None\nb = [1] * "r"')
+<<<<<<< variant A
inferred = ast['a'].inferred()
self.assertEqual(len(inferred), 1)
self.assertEqual(inferred[0], YES)
inferred = ast['b'].inferred()
self.assertEqual(len(inferred), 1)
self.assertEqual(inferred[0], YES)
+>>>>>>> variant B
+ infered = ast['a'].infered()
+ self.assertEqual(len(infered), 1)
+ self.assertEqual(infered[0], util.YES)
+ infered = ast['b'].infered()
+ self.assertEqual(len(infered), 1)
+ self.assertEqual(infered[0], util.YES)
+####### Ancestor
+ infered = ast['a'].infered()
+ self.assertEqual(len(infered), 1)
+ self.assertEqual(infered[0], YES)
+ infered = ast['b'].infered()
+ self.assertEqual(len(infered), 1)
+ self.assertEqual(infered[0], YES)
+======= end
def test_binary_op_tuple_add(self):
ast = builder.string_build('a = (1,) + (2,)', __name__, __file__)
@@ -1039,7 +1056,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
callfuncnode = test_utils.extract_node(code)
inferred = list(callfuncnode.infer())
self.assertEqual(len(inferred), 2, inferred)
- inferred.remove(YES)
+ inferred.remove(util.YES)
self.assertIsInstance(inferred[0], nodes.Const)
self.assertIsNone(inferred[0].value)
@@ -1051,7 +1068,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
ast = parse(code, __name__)
inferred = list(ast['f'].ilookup('a'))
self.assertEqual(len(inferred), 1)
- self.assertEqual(inferred[0], YES)
+ self.assertEqual(inferred[0], util.YES)
def test_nonregr_instance_attrs(self):
"""non regression for instance_attrs infinite loop : pylint / #4"""
@@ -1103,7 +1120,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
self.assertTrue(ast.absolute_import_activated(), True)
inferred = next(test_utils.get_name_node(ast, 'import_package_subpackage_module').infer())
# failed to import since absolute_import is activated
- self.assertIs(inferred, YES)
+ self.assertIs(inferred, util.YES)
def test_nonregr_absolute_import(self):
ast = resources.build_file('data/absimp/string.py', 'data.absimp.string')
@@ -1221,7 +1238,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
ast = parse(code, __name__)
inferred = list(test_utils.get_name_node(ast['foo'], 'spam').infer())
self.assertEqual(len(inferred), 1)
- self.assertIs(inferred[0], YES)
+ self.assertIs(inferred[0], util.YES)
def test_nonregr_func_global(self):
code = '''
@@ -1291,6 +1308,14 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
inferred = list(n.igetattr('arg'))
self.assertEqual(len(inferred), 1, inferred)
+ def test__new__bound_methods(self):
+ node = test_utils.extract_node('''
+ class cls(object): pass
+ cls().__new__(cls) #@
+ ''')
+ inferred = next(node.infer())
+ self.assertIsInstance(inferred, Instance)
+ self.assertEqual(inferred._proxied, node.root()['cls'])
def test_two_parents_from_same_module(self):
code = '''
@@ -1404,7 +1429,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
ast = parse(code, __name__)
sub = ast['sub'].inferred()[0]
mul = ast['mul'].inferred()[0]
- self.assertIs(sub, YES)
+ self.assertIs(sub, util.YES)
self.assertIsInstance(mul, nodes.Const)
self.assertEqual(mul.value, 42)
@@ -1423,7 +1448,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
ast = parse(code, __name__)
sub = ast['sub'].inferred()[0]
mul = ast['mul'].inferred()[0]
- self.assertIs(sub, YES)
+ self.assertIs(sub, util. YES)
self.assertIsInstance(mul, nodes.Const)
self.assertEqual(mul.value, 42)
@@ -1443,7 +1468,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
ast = parse(code, __name__)
sub = ast['sub'].inferred()[0]
mul = ast['mul'].inferred()[0]
- self.assertIs(sub, YES)
+ self.assertIs(sub, util.YES)
self.assertIsInstance(mul, nodes.List)
self.assertIsInstance(mul.elts[0], nodes.Const)
self.assertEqual(mul.elts[0].value, 42)
@@ -1460,12 +1485,12 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
"""
ast = parse(code, __name__)
node = ast['c']
- self.assertEqual(node.inferred(), [YES])
+ self.assertEqual(node.inferred(), [util.YES])
def test_infer_empty_nodes(self):
# Should not crash when trying to infer EmptyNodes.
node = nodes.EmptyNode()
- self.assertEqual(node.inferred(), [YES])
+ self.assertEqual(node.inferred(), [util.YES])
def test_infinite_loop_for_decorators(self):
# Issue https://bitbucket.org/logilab/astroid/issue/50
@@ -1933,7 +1958,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
def test_unary_op_leaks_stop_iteration(self):
node = test_utils.extract_node('+[] #@')
- self.assertEqual(YES, next(node.infer()))
+ self.assertEqual(util.YES, next(node.infer()))
def test_unary_operands(self):
ast_nodes = test_utils.extract_node('''
@@ -1984,7 +2009,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
for bad_node in ast_nodes[4:]:
inferred = next(bad_node.infer())
- self.assertEqual(inferred, YES)
+ self.assertEqual(inferred, util.YES)
def test_binary_op_type_errors(self):
ast_nodes = test_utils.extract_node('''
@@ -2173,11 +2198,11 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
genexpr = next(module['genexpr'].infer())
self.assertTrue(genexpr.bool_value())
dict_comp = next(module['dict_comp'].infer())
- self.assertEqual(dict_comp, YES)
+ self.assertEqual(dict_comp, util.YES)
set_comp = next(module['set_comp'].infer())
- self.assertEqual(set_comp, YES)
+ self.assertEqual(set_comp, util.YES)
list_comp = next(module['list_comp'].infer())
- self.assertEqual(list_comp, YES)
+ self.assertEqual(list_comp, util.YES)
lambda_func = next(module['lambda_func'].infer())
self.assertTrue(lambda_func)
unbound_method = next(module['unbound_method'].infer())
@@ -2191,13 +2216,13 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
bin_op = module['bin_op'].parent.value
self.assertTrue(bin_op.bool_value())
bool_op = module['bool_op'].parent.value
- self.assertEqual(bool_op.bool_value(), YES)
+ self.assertEqual(bool_op.bool_value(), util.YES)
callfunc = module['callfunc'].parent.value
- self.assertEqual(callfunc.bool_value(), YES)
+ self.assertEqual(callfunc.bool_value(), util.YES)
good_callfunc = next(module['good_callfunc'].infer())
self.assertTrue(good_callfunc.bool_value())
compare = module['compare'].parent.value
- self.assertEqual(compare.bool_value(), YES)
+ self.assertEqual(compare.bool_value(), util.YES)
def test_bool_value_instances(self):
instances = test_utils.extract_node('''
@@ -2230,7 +2255,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
AlwaysTrueInstance() #@
ErrorInstance() #@
'''.format(bool=BOOL_SPECIAL_METHOD))
- expected = (False, True, False, True, True, YES, YES)
+ expected = (False, True, False, True, True, util.YES, util.YES)
for node, expected_value in zip(instances, expected):
inferred = next(node.infer())
self.assertEqual(inferred.bool_value(), expected_value)
@@ -2302,7 +2327,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
A() + B() #@
''')
inferred = next(node.infer())
- self.assertEqual(inferred, YES)
+ self.assertEqual(inferred, util.YES)
def test_binop_different_types_reflected_and_normal_not_implemented(self):
node = test_utils.extract_node('''
@@ -2313,7 +2338,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
A() + B() #@
''')
inferred = next(node.infer())
- self.assertEqual(inferred, YES)
+ self.assertEqual(inferred, util.YES)
def test_binop_subtype(self):
node = test_utils.extract_node('''
@@ -2346,7 +2371,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
B() + A() #@
''')
inferred = next(node.infer())
- self.assertEqual(inferred, YES)
+ self.assertEqual(inferred, util.YES)
def test_binop_supertype(self):
node = test_utils.extract_node('''
@@ -2385,7 +2410,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
A() + B() #@
''')
inferred = next(node.infer())
- self.assertEqual(inferred, YES)
+ self.assertEqual(inferred, util.YES)
def test_binop_inferrence_errors(self):
ast_nodes = test_utils.extract_node('''
@@ -2400,7 +2425,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
A() + B() #@
''')
for node in ast_nodes:
- self.assertEqual(next(node.infer()), YES)
+ self.assertEqual(next(node.infer()), util.YES)
def test_binop_ambiguity(self):
ast_nodes = test_utils.extract_node('''
@@ -2423,7 +2448,27 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
C() + A() #@
''')
for node in ast_nodes:
- self.assertEqual(next(node.infer()), YES)
+ self.assertEqual(next(node.infer()), util.YES)
+
+ def test_bin_op_supertype_more_complicated_example(self):
+ ast_node = test_utils.extract_node('''
+ class A(object):
+ def __init__(self):
+ self.foo = 42
+ def __add__(self, other):
+ return other.bar + self.foo / 2
+
+ class B(A):
+ def __init__(self):
+ self.bar = 24
+ def __radd__(self, other):
+ return NotImplemented
+
+ A() + B() #@
+ ''')
+ inferred = next(ast_node.infer())
+ self.assertIsInstance(inferred, nodes.Const)
+ self.assertEqual(int(inferred.value), 45)
def test_aug_op_same_type_not_implemented(self):
ast_node = test_utils.extract_node('''
@@ -2432,7 +2477,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
def __add__(self, other): return NotImplemented
A() + A() #@
''')
- self.assertEqual(next(ast_node.infer()), YES)
+ self.assertEqual(next(ast_node.infer()), util.YES)
def test_aug_op_same_type_aug_implemented(self):
ast_node = test_utils.extract_node('''
@@ -2467,7 +2512,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
b = B()
b+=A() #@
''')
- self.assertEqual(next(ast_node.infer()), YES)
+ self.assertEqual(next(ast_node.infer()), util.YES)
def test_aug_op_subtype_aug_op_is_implemented(self):
ast_node = test_utils.extract_node('''
@@ -2502,7 +2547,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
f = A()
f += B() #@
''')
- self.assertEqual(next(ast_node.infer()), YES)
+ self.assertEqual(next(ast_node.infer()), util.YES)
def test_aug_different_types_augop_implemented(self):
ast_node = test_utils.extract_node('''
@@ -2550,7 +2595,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
a = A()
a += B() #@
''')
- self.assertEqual(next(ast_node.infer()), YES)
+ self.assertEqual(next(ast_node.infer()), util.YES)
def test_augop_supertypes_not_implemented_returned_for_all(self):
ast_node = test_utils.extract_node('''
@@ -2562,7 +2607,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
a = A()
a += B() #@
''')
- self.assertEqual(next(ast_node.infer()), YES)
+ self.assertEqual(next(ast_node.infer()), util.YES)
def test_augop_supertypes_augop_implemented(self):
ast_node = test_utils.extract_node('''
@@ -2634,7 +2679,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
[1, 2, 1, 2])
for rest in ast_nodes[1:]:
inferred = next(rest.infer())
- self.assertEqual(inferred, YES)
+ self.assertEqual(inferred, util.YES)
def test_special_method_masquerading_as_another(self):
ast_node = test_utils.extract_node('''
@@ -2666,7 +2711,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
class GetattrTest(unittest.TestCase):
- def test_yes(self):
+ def test_yes_when_unknown(self):
ast_nodes = test_utils.extract_node('''
from missing import Missing
getattr(1, Unknown) #@
@@ -2684,7 +2729,7 @@ class GetattrTest(unittest.TestCase):
for node in ast_nodes[4:]:
inferred = next(node.infer())
- self.assertEqual(inferred, YES, node)
+ self.assertEqual(inferred, util.YES, node)
def test_attrname_not_string(self):
ast_nodes = test_utils.extract_node('''
@@ -2781,7 +2826,7 @@ class HasattrTest(unittest.TestCase):
''')
for node in ast_nodes:
inferred = next(node.infer())
- self.assertEqual(inferred, YES)
+ self.assertEqual(inferred, util.YES)
def test_attribute_is_missing(self):
ast_nodes = test_utils.extract_node('''
@@ -2854,7 +2899,7 @@ class BoolOpTest(unittest.TestCase):
''')
for node in ast_nodes:
inferred = next(node.infer())
- self.assertEqual(inferred, YES)
+ self.assertEqual(inferred, util.YES)
def test_other_nodes(self):
ast_nodes = test_utils.extract_node('''
@@ -2934,7 +2979,7 @@ class TestCallable(unittest.TestCase):
''')
for node in ast_nodes:
inferred = next(node.infer())
- self.assertEqual(inferred, YES)
+ self.assertEqual(inferred, util.YES)
def test_not_callable(self):
ast_nodes = test_utils.extract_node('''
@@ -2960,12 +3005,12 @@ class TestBool(unittest.TestCase):
('bool(True)', True),
('bool(False)', False),
('bool(None)', False),
- ('from unknown import Unknown; __(bool(Unknown))', YES),
+ ('from unknown import Unknown; __(bool(Unknown))', util.YES),
]
for code, expected in pairs:
node = test_utils.extract_node(code)
inferred = next(node.infer())
- if expected is YES:
+ if expected is util.YES:
self.assertEqual(expected, inferred)
else:
self.assertEqual(inferred.value, expected)
diff --git a/astroid/tests/unittest_lookup.py b/astroid/tests/unittest_lookup.py
index 28c60209..805efd9e 100644
--- a/astroid/tests/unittest_lookup.py
+++ b/astroid/tests/unittest_lookup.py
@@ -21,12 +21,12 @@ import functools
import sys
import unittest
-from astroid import bases
from astroid import builder
from astroid import exceptions
from astroid import nodes
from astroid import scoped_nodes
from astroid import test_utils
+from astroid import util
from astroid.tests import resources
@@ -172,7 +172,7 @@ class LookupTest(resources.SysPathSetup, unittest.TestCase):
""")
var = astroid.body[1].value
if sys.version_info < (3, 0):
- self.assertEqual(var.inferred(), [bases.YES])
+ self.assertEqual(var.inferred(), [util.YES])
else:
self.assertRaises(exceptions.UnresolvableName, var.inferred)
@@ -221,8 +221,8 @@ class LookupTest(resources.SysPathSetup, unittest.TestCase):
iterer = count()
num = iterer.next()
""")
- next = tree.body[2].value.func
- gener = next.expr.inferred()[0]
+ next_node = tree.body[2].value.func
+ gener = next_node.expr.inferred()[0]
if sys.version_info < (3, 0):
self.assertIsInstance(gener.getattr('next')[0], nodes.FunctionDef)
else:
diff --git a/astroid/tests/unittest_modutils.py b/astroid/tests/unittest_modutils.py
index 95cb1f74..644d33f5 100644
--- a/astroid/tests/unittest_modutils.py
+++ b/astroid/tests/unittest_modutils.py
@@ -22,7 +22,6 @@ import os
import sys
import unittest
-from logilab.common import configuration
from astroid import modutils
from astroid.tests import resources
@@ -99,8 +98,9 @@ class ModPathFromFileTest(unittest.TestCase):
""" given an absolute file path return the python module's path as a list """
def test_knownValues_modpath_from_file_1(self):
- self.assertEqual(modutils.modpath_from_file(configuration.__file__),
- ['logilab', 'common', 'configuration'])
+ from xml.etree import ElementTree
+ self.assertEqual(modutils.modpath_from_file(ElementTree.__file__),
+ ['xml', 'etree', 'ElementTree'])
def test_knownValues_modpath_from_file_2(self):
self.assertEqual(modutils.modpath_from_file('unittest_modutils.py',
@@ -189,7 +189,7 @@ class StandardLibModuleTest(resources.SysPathSetup, unittest.TestCase):
self.assertEqual(modutils.is_standard_module('marshal'), True)
def test_nonstandard(self):
- self.assertEqual(modutils.is_standard_module('logilab'), False)
+ self.assertEqual(modutils.is_standard_module('astroid'), False)
def test_unknown(self):
self.assertEqual(modutils.is_standard_module('unknown'), False)
@@ -210,14 +210,14 @@ class StandardLibModuleTest(resources.SysPathSetup, unittest.TestCase):
self.assertEqual(modutils.is_standard_module('data.module', (os.path.abspath(datadir),)), True)
def test_failing_edge_cases(self):
- from logilab import common
+ from xml import etree
# using a subpackage/submodule path as std_path argument
- self.assertEqual(modutils.is_standard_module('logilab.common', common.__path__), False)
+ self.assertEqual(modutils.is_standard_module('xml.etree', etree.__path__), False)
# using a module + object name as modname argument
self.assertEqual(modutils.is_standard_module('sys.path'), True)
# this is because only the first package/module is considered
self.assertEqual(modutils.is_standard_module('sys.whatever'), True)
- self.assertEqual(modutils.is_standard_module('logilab.whatever', common.__path__), False)
+ self.assertEqual(modutils.is_standard_module('xml.whatever', etree.__path__), False)
class IsRelativeTest(unittest.TestCase):
@@ -229,8 +229,8 @@ class IsRelativeTest(unittest.TestCase):
True)
def test_knownValues_is_relative_2(self):
- from logilab.common import tree
- self.assertEqual(modutils.is_relative('modutils', tree.__file__),
+ from xml.etree import ElementTree
+ self.assertEqual(modutils.is_relative('ElementPath', ElementTree.__file__),
True)
def test_knownValues_is_relative_3(self):
@@ -242,24 +242,32 @@ class IsRelativeTest(unittest.TestCase):
class GetModuleFilesTest(unittest.TestCase):
def test_get_module_files_1(self):
- """given a directory return a list of all available python module's files, even
- in subdirectories
- """
package = resources.find('data/find_test')
modules = set(modutils.get_module_files(package, []))
+ expected = ['__init__.py', 'module.py', 'module2.py',
+ 'noendingnewline.py', 'nonregr.py']
+ self.assertEqual(modules,
+ {os.path.join(package, x) for x in expected})
+
+ def test_get_all_files(self):
+ """test that list_all returns all Python files from given location
+ """
+ non_package = resources.find('data/notamodule')
+ modules = modutils.get_module_files(non_package, [], list_all=True)
self.assertEqual(
modules,
- {os.path.join(package, x) for x in ['__init__.py', 'module.py', 'module2.py', 'noendingnewline.py', 'nonregr.py']})
+ [os.path.join(non_package, 'file.py')],
+ )
def test_load_module_set_attribute(self):
- import logilab.common.fileutils
- import logilab
- del logilab.common.fileutils
- del sys.modules['logilab.common.fileutils']
- m = modutils.load_module_from_modpath(['logilab', 'common', 'fileutils'])
- self.assertTrue(hasattr(logilab, 'common'))
- self.assertTrue(hasattr(logilab.common, 'fileutils'))
- self.assertTrue(m is logilab.common.fileutils)
+ import xml.etree.ElementTree
+ import xml
+ del xml.etree.ElementTree
+ del sys.modules['xml.etree.ElementTree']
+ m = modutils.load_module_from_modpath(['xml', 'etree', 'ElementTree'])
+ self.assertTrue(hasattr(xml, 'etree'))
+ self.assertTrue(hasattr(xml.etree, 'ElementTree'))
+ self.assertTrue(m is xml.etree.ElementTree)
if __name__ == '__main__':
diff --git a/astroid/tests/unittest_nodes.py b/astroid/tests/unittest_nodes.py
index 9c873e17..511a8c77 100644
--- a/astroid/tests/unittest_nodes.py
+++ b/astroid/tests/unittest_nodes.py
@@ -26,9 +26,11 @@ import six
from astroid import bases
from astroid import builder
+from astroid import context as contextmod
from astroid import exceptions
from astroid import node_classes
from astroid import nodes
+from astroid import util
from astroid import test_utils
from astroid.tests import resources
@@ -80,7 +82,6 @@ class AsStringTest(resources.SysPathSetup, unittest.TestCase):
def test_module2_as_string(self):
"""check as_string on a whole module prepared to be returned identically
"""
- self.maxDiff = None
module2 = resources.build_file('data/module2.py', 'data.module2')
with open(resources.find('data/module2.py'), 'r') as fobj:
self.assertMultiLineEqual(module2.as_string(), fobj.read())
@@ -302,14 +303,11 @@ class ImportNodeTest(resources.SysPathSetup, unittest.TestCase):
self.assertEqual(myos.pytype(), '%s.module' % BUILTINS)
def test_from_self_resolve(self):
- pb = next(self.module.igetattr('pb'))
- self.assertTrue(isinstance(pb, nodes.ClassDef), pb)
- self.assertEqual(pb.root().name, 'logilab.common.shellutils')
- self.assertEqual(pb.qname(), 'logilab.common.shellutils.ProgressBar')
- if pb.newstyle:
- self.assertEqual(pb.pytype(), '%s.type' % BUILTINS)
- else:
- self.assertEqual(pb.pytype(), '%s.classobj' % BUILTINS)
+ namenode = next(self.module.igetattr('NameNode'))
+ self.assertTrue(isinstance(namenode, nodes.ClassDef), namenode)
+ self.assertEqual(namenode.root().name, 'astroid.node_classes')
+ self.assertEqual(namenode.qname(), 'astroid.node_classes.Name')
+ self.assertEqual(namenode.pytype(), '%s.type' % BUILTINS)
abspath = next(self.module2.igetattr('abspath'))
self.assertTrue(isinstance(abspath, nodes.FunctionDef), abspath)
self.assertEqual(abspath.root().name, 'os.path')
@@ -373,11 +371,11 @@ from ..cave import wine\n\n"""
# present in the other version.
self.assertIsInstance(excs[0], nodes.ClassDef)
self.assertEqual(excs[0].name, 'PickleError')
- self.assertIs(excs[-1], bases.YES)
+ self.assertIs(excs[-1], util.YES)
def test_absolute_import(self):
astroid = resources.build_file('data/absimport.py')
- ctx = bases.InferenceContext()
+ ctx = contextmod.InferenceContext()
# will fail if absolute import failed
ctx.lookupname = 'message'
next(astroid['message'].infer(ctx))
diff --git a/astroid/tests/unittest_protocols.py b/astroid/tests/unittest_protocols.py
index 12a5c609..c82fdd01 100644
--- a/astroid/tests/unittest_protocols.py
+++ b/astroid/tests/unittest_protocols.py
@@ -18,10 +18,10 @@
import unittest
-from astroid import YES
from astroid.test_utils import extract_node, require_version
from astroid import InferenceError
from astroid import nodes
+from astroid import util
from astroid.node_classes import AssignName, Const, Name, Starred
@@ -67,7 +67,7 @@ class ProtocolTests(unittest.TestCase):
for1_starred = next(assign_stmts.nodes_of_class(Starred))
assigned = next(for1_starred.assigned_stmts())
- self.assertEqual(assigned, YES)
+ self.assertEqual(assigned, util.YES)
def _get_starred_stmts(self, code):
assign_stmt = extract_node("{} #@".format(code))
@@ -108,16 +108,16 @@ class ProtocolTests(unittest.TestCase):
@require_version(minver='3.0')
def test_assigned_stmts_starred_yes(self):
# Not something iterable and known
- self._helper_starred_expected("a, *b = range(3) #@", YES)
+ self._helper_starred_expected("a, *b = range(3) #@", util.YES)
# Not something inferrable
- self._helper_starred_expected("a, *b = balou() #@", YES)
+ self._helper_starred_expected("a, *b = balou() #@", util.YES)
# In function, unknown.
self._helper_starred_expected("""
def test(arg):
- head, *tail = arg #@""", YES)
+ head, *tail = arg #@""", util.YES)
# These cases aren't worth supporting.
self._helper_starred_expected(
- "a, (*b, c), d = (1, (2, 3, 4), 5) #@", YES)
+ "a, (*b, c), d = (1, (2, 3, 4), 5) #@", util.YES)
@require_version(minver='3.0')
def test_assign_stmts_starred_fails(self):
diff --git a/astroid/tests/unittest_regrtest.py b/astroid/tests/unittest_regrtest.py
index a299e77a..ba7e2bf1 100644
--- a/astroid/tests/unittest_regrtest.py
+++ b/astroid/tests/unittest_regrtest.py
@@ -28,6 +28,8 @@ from astroid.raw_building import build_module
from astroid.manager import AstroidManager
from astroid.test_utils import require_version, extract_node
from astroid.tests import resources
+from astroid import transforms
+
class NonRegressionTests(resources.AstroidCacheSetupMixin,
unittest.TestCase):
@@ -35,6 +37,7 @@ class NonRegressionTests(resources.AstroidCacheSetupMixin,
def setUp(self):
sys.path.insert(0, resources.find('data'))
MANAGER.always_load_extensions = True
+ MANAGER.astroid_cache[BUILTINS] = self._builtins
def tearDown(self):
# Since we may have created a brainless manager, leading
@@ -53,7 +56,7 @@ class NonRegressionTests(resources.AstroidCacheSetupMixin,
manager._failed_import_hooks = []
manager.astroid_cache = {}
manager._mod_file_cache = {}
- manager.transforms = {}
+ manager._transform = transforms.TransformVisitor()
manager.clear_cache() # trigger proper bootstraping
return manager
diff --git a/astroid/tests/unittest_scoped_nodes.py b/astroid/tests/unittest_scoped_nodes.py
index 1a1134fd..f7088d87 100644
--- a/astroid/tests/unittest_scoped_nodes.py
+++ b/astroid/tests/unittest_scoped_nodes.py
@@ -24,7 +24,10 @@ from functools import partial
import unittest
import warnings
-from astroid import YES, builder, nodes, scoped_nodes
+from astroid import builder
+from astroid import nodes
+from astroid import scoped_nodes
+from astroid import util
from astroid.exceptions import (
InferenceError, NotFoundError,
NoDefault, ResolveError, MroError,
@@ -86,9 +89,9 @@ class ModuleNodeTest(ModuleLoader, unittest.TestCase):
red = next(self.module.igetattr('redirect'))
self.assertIsInstance(red, nodes.FunctionDef)
self.assertEqual(red.name, 'four_args')
- pb = next(self.module.igetattr('pb'))
- self.assertIsInstance(pb, nodes.ClassDef)
- self.assertEqual(pb.name, 'ProgressBar')
+ namenode = next(self.module.igetattr('NameNode'))
+ self.assertIsInstance(namenode, nodes.Class)
+ self.assertEqual(namenode.name, 'Name')
# resolve packageredirection
mod = resources.build_file('data/appl/myConnection.py',
'data.appl.myConnection')
@@ -1088,7 +1091,7 @@ class ClassNodeTest(ModuleLoader, unittest.TestCase):
instance = astroid['tgts']
# used to raise "'_Yes' object is not iterable", see
# https://bitbucket.org/logilab/astroid/issue/17
- self.assertEqual(list(instance.infer()), [YES])
+ self.assertEqual(list(instance.infer()), [util.YES])
def test_slots(self):
astroid = builder.parse("""
@@ -1324,6 +1327,87 @@ class ClassNodeTest(ModuleLoader, unittest.TestCase):
type_cls = scoped_nodes.builtin_lookup("type")[1][0]
self.assertEqual(cls.implicit_metaclass(), type_cls)
+ def test_implicit_metaclass_lookup(self):
+ cls = test_utils.extract_node('''
+ class A(object):
+ pass
+ ''')
+ instance = cls.instanciate_class()
+ func = cls.getattr('mro')
+ self.assertEqual(len(func), 1)
+ self.assertRaises(NotFoundError, instance.getattr, 'mro')
+
+ def test_metaclass_lookup_using_same_class(self):
+ # Check that we don't have recursive attribute access for metaclass
+ cls = test_utils.extract_node('''
+ class A(object): pass
+ ''')
+ self.assertEqual(len(cls.getattr('mro')), 1)
+
+ def test_metaclass_lookup_inferrence_errors(self):
+ module = builder.parse('''
+ import six
+
+ class Metaclass(type):
+ foo = lala
+
+ @six.add_metaclass(Metaclass)
+ class B(object): pass
+ ''')
+ cls = module['B']
+ self.assertEqual(util.YES, next(cls.igetattr('foo')))
+
+ def test_metaclass_lookup(self):
+ module = builder.parse('''
+ import six
+
+ class Metaclass(type):
+ foo = 42
+ @classmethod
+ def class_method(cls):
+ pass
+ def normal_method(cls):
+ pass
+ @property
+ def meta_property(cls):
+ return 42
+ @staticmethod
+ def static():
+ pass
+
+ @six.add_metaclass(Metaclass)
+ class A(object):
+ pass
+ ''')
+ acls = module['A']
+ normal_attr = next(acls.igetattr('foo'))
+ self.assertIsInstance(normal_attr, nodes.Const)
+ self.assertEqual(normal_attr.value, 42)
+
+ class_method = next(acls.igetattr('class_method'))
+ self.assertIsInstance(class_method, BoundMethod)
+ self.assertEqual(class_method.bound, module['Metaclass'])
+
+ normal_method = next(acls.igetattr('normal_method'))
+ self.assertIsInstance(normal_method, BoundMethod)
+ self.assertEqual(normal_method.bound, module['A'])
+
+ # Attribute access for properties:
+ # from the metaclass is a property object
+ # from the class that uses the metaclass, the value
+ # of the property
+ property_meta = next(module['Metaclass'].igetattr('meta_property'))
+ self.assertIsInstance(property_meta, UnboundMethod)
+ wrapping = scoped_nodes.get_wrapping_class(property_meta)
+ self.assertEqual(wrapping, module['Metaclass'])
+
+ property_class = next(acls.igetattr('meta_property'))
+ self.assertIsInstance(property_class, nodes.Const)
+ self.assertEqual(property_class.value, 42)
+
+ static = next(acls.igetattr('static'))
+ self.assertIsInstance(static, scoped_nodes.Function)
+
@test_utils.require_version(maxver='3.0')
def test_implicit_metaclass_is_none(self):
cls = test_utils.extract_node("""
@@ -1397,6 +1481,59 @@ class ClassNodeTest(ModuleLoader, unittest.TestCase):
not_method = next(instance.igetattr('not_method'))
self.assertIsInstance(not_method, scoped_nodes.Lambda)
+ def test_class_extra_decorators_frame_is_not_class(self):
+ ast_node = test_utils.extract_node('''
+ def ala():
+ def bala(): #@
+ func = 42
+ ''')
+ self.assertEqual(ast_node.extra_decorators, [])
+
+ def test_class_extra_decorators_only_callfunc_are_considered(self):
+ ast_node = test_utils.extract_node('''
+ class Ala(object):
+ def func(self): #@
+ pass
+ func = 42
+ ''')
+ self.assertEqual(ast_node.extra_decorators, [])
+
+ def test_class_extra_decorators_only_assignment_names_are_considered(self):
+ ast_node = test_utils.extract_node('''
+ class Ala(object):
+ def func(self): #@
+ pass
+ def __init__(self):
+ self.func = staticmethod(func)
+
+ ''')
+ self.assertEqual(ast_node.extra_decorators, [])
+
+ def test_class_extra_decorators_only_same_name_considered(self):
+ ast_node = test_utils.extract_node('''
+ class Ala(object):
+ def func(self): #@
+ pass
+ bala = staticmethod(func)
+ ''')
+ self.assertEqual(ast_node.extra_decorators, [])
+ self.assertEqual(ast_node.type, 'method')
+
+ def test_class_extra_decorators(self):
+ static_method, clsmethod = test_utils.extract_node('''
+ class Ala(object):
+ def static(self): #@
+ pass
+ def class_method(self): #@
+ pass
+ class_method = classmethod(class_method)
+ static = staticmethod(static)
+ ''')
+ self.assertEqual(len(clsmethod.extra_decorators), 1)
+ self.assertEqual(clsmethod.type, 'classmethod')
+ self.assertEqual(len(static_method.extra_decorators), 1)
+ self.assertEqual(static_method.type, 'staticmethod')
+
if __name__ == '__main__':
unittest.main()
diff --git a/astroid/tests/unittest_transforms.py b/astroid/tests/unittest_transforms.py
new file mode 100644
index 00000000..3bb0cc2d
--- /dev/null
+++ b/astroid/tests/unittest_transforms.py
@@ -0,0 +1,218 @@
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of astroid.
+#
+# astroid is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 2.1 of the License, or (at your
+# option) any later version.
+#
+# astroid is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+
+import contextlib
+import time
+import unittest
+
+from astroid import builder
+from astroid import nodes
+from astroid import parse
+from astroid import transforms
+
+
+@contextlib.contextmanager
+def add_transform(manager, node, transform, predicate=None):
+ manager.register_transform(node, transform, predicate)
+ try:
+ yield
+ finally:
+ manager.unregister_transform(node, transform, predicate)
+
+
+class TestTransforms(unittest.TestCase):
+
+ def setUp(self):
+ self.transformer = transforms.TransformVisitor()
+
+ def parse_transform(self, code):
+ module = parse(code, apply_transforms=False)
+ return self.transformer.visit(module)
+
+ def test_function_inlining_transform(self):
+ def transform_callfunc(node):
+ # Let's do some function inlining
+ inferred = next(node.infer())
+ return inferred
+
+ self.transformer.register_transform(nodes.CallFunc,
+ transform_callfunc)
+
+ module = self.parse_transform('''
+ def test(): return 42
+ test() #@
+ ''')
+
+ self.assertIsInstance(module.body[1], nodes.Discard)
+ self.assertIsInstance(module.body[1].value, nodes.Const)
+ self.assertEqual(module.body[1].value.value, 42)
+
+ def test_recursive_transforms_into_astroid_fields(self):
+ # Test that the transformer walks properly the tree
+ # by going recursively into the _astroid_fields per each node.
+ def transform_compare(node):
+ # Let's check the values of the ops
+ _, right = node.ops[0]
+ # Assume they are Consts and they were transformed before
+ # us.
+ return nodes.const_factory(node.left.value < right.value)
+
+ def transform_name(node):
+ # Should be Consts
+ return next(node.infer())
+
+ self.transformer.register_transform(nodes.Compare, transform_compare)
+ self.transformer.register_transform(nodes.Name, transform_name)
+
+ module = self.parse_transform('''
+ a = 42
+ b = 24
+ a < b
+ ''')
+
+ self.assertIsInstance(module.body[2], nodes.Discard)
+ self.assertIsInstance(module.body[2].value, nodes.Const)
+ self.assertFalse(module.body[2].value.value)
+
+ def test_transform_patches_locals(self):
+ def transform_function(node):
+ assign = nodes.Assign()
+ name = nodes.AssName()
+ name.name = 'value'
+ assign.targets = [name]
+ assign.value = nodes.const_factory(42)
+ node.body.append(assign)
+
+ self.transformer.register_transform(nodes.Function,
+ transform_function)
+
+ module = self.parse_transform('''
+ def test():
+ pass
+ ''')
+
+ func = module.body[0]
+ self.assertEqual(len(func.body), 2)
+ self.assertIsInstance(func.body[1], nodes.Assign)
+ self.assertEqual(func.body[1].as_string(), 'value = 42')
+
+ def test_predicates(self):
+ def transform_callfunc(node):
+ inferred = next(node.infer())
+ return inferred
+
+ def should_inline(node):
+ return node.func.name.startswith('inlineme')
+
+ self.transformer.register_transform(nodes.CallFunc,
+ transform_callfunc,
+ should_inline)
+
+ module = self.parse_transform('''
+ def inlineme_1():
+ return 24
+ def dont_inline_me():
+ return 42
+ def inlineme_2():
+ return 2
+ inlineme_1()
+ dont_inline_me()
+ inlineme_2()
+ ''')
+ values = module.body[-3:]
+ self.assertIsInstance(values[0], nodes.Discard)
+ self.assertIsInstance(values[0].value, nodes.Const)
+ self.assertEqual(values[0].value.value, 24)
+ self.assertIsInstance(values[1], nodes.Discard)
+ self.assertIsInstance(values[1].value, nodes.CallFunc)
+ self.assertIsInstance(values[2], nodes.Discard)
+ self.assertIsInstance(values[2].value, nodes.Const)
+ self.assertEqual(values[2].value.value, 2)
+
+ def test_transforms_are_separated(self):
+ # Test that the transforming is done at a separate
+ # step, which means that we are not doing inference
+ # on a partially constructred tree anymore, which was the
+ # source of crashes in the past when certain inference rules
+ # were used in a transform.
+ def transform_function(node):
+ if node.decorators:
+ for decorator in node.decorators.nodes:
+ inferred = next(decorator.infer())
+ if inferred.qname() == 'abc.abstractmethod':
+ return next(node.infer_call_result(node))
+
+ manager = builder.MANAGER
+ with add_transform(manager, nodes.Function, transform_function):
+ module = builder.parse('''
+ import abc
+ from abc import abstractmethod
+
+ class A(object):
+ @abc.abstractmethod
+ def ala(self):
+ return 24
+
+ @abstractmethod
+ def bala(self):
+ return 42
+ ''')
+
+ cls = module['A']
+ ala = cls.body[0]
+ bala = cls.body[1]
+ self.assertIsInstance(ala, nodes.Const)
+ self.assertEqual(ala.value, 24)
+ self.assertIsInstance(bala, nodes.Const)
+ self.assertEqual(bala.value, 42)
+
+ def test_transforms_are_called_for_builtin_modules(self):
+ # Test that transforms are called for builtin modules.
+ def transform_function(node):
+ name = nodes.AssName()
+ name.name = 'value'
+ node.args.args = [name]
+ return node
+
+ manager = builder.MANAGER
+ predicate = lambda node: node.root().name == 'time'
+ with add_transform(manager, nodes.Function,
+ transform_function, predicate):
+ builder_instance = builder.AstroidBuilder()
+ module = builder_instance.module_build(time)
+
+ asctime = module['asctime']
+ self.assertEqual(len(asctime.args.args), 1)
+ self.assertIsInstance(asctime.args.args[0], nodes.AssName)
+ self.assertEqual(asctime.args.args[0].name, 'value')
+
+ def test_builder_apply_transforms(self):
+ def transform_function(node):
+ return nodes.const_factory(42)
+
+ manager = builder.MANAGER
+ with add_transform(manager, nodes.Function, transform_function):
+ astroid_builder = builder.AstroidBuilder(apply_transforms=False)
+ module = astroid_builder.string_build('''def test(): pass''')
+
+ # The transform wasn't applied.
+ self.assertIsInstance(module.body[0], nodes.Function)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/astroid/transforms.py b/astroid/transforms.py
new file mode 100644
index 00000000..5d8fc91b
--- /dev/null
+++ b/astroid/transforms.py
@@ -0,0 +1,96 @@
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of astroid.
+#
+# astroid is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 2.1 of the License, or (at your
+# option) any later version.
+#
+# astroid is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+
+import collections
+import warnings
+
+
+class TransformVisitor(object):
+ """A visitor for handling transforms.
+
+ The standard approach of using it is to call
+ :meth:`~visit` with an *astroid* module and the class
+ will take care of the rest, walking the tree and running the
+ transforms for each encountered node.
+ """
+
+ def __init__(self):
+ self.transforms = collections.defaultdict(list)
+
+ def _transform(self, node):
+ """Call matching transforms for the given node if any and return the
+ transformed node.
+ """
+ cls = node.__class__
+ if cls not in self.transforms:
+ # no transform registered for this class of node
+ return node
+
+ transforms = self.transforms[cls]
+ orig_node = node # copy the reference
+ for transform_func, predicate in transforms:
+ if predicate is None or predicate(node):
+ ret = transform_func(node)
+ # if the transformation function returns something, it's
+ # expected to be a replacement for the node
+ if ret is not None:
+ if node is not orig_node:
+ # node has already be modified by some previous
+ # transformation, warn about it
+ warnings.warn('node %s substituted multiple times' % node)
+ node = ret
+ return node
+
+ def _visit(self, node):
+ if hasattr(node, '_astroid_fields'):
+ for field in node._astroid_fields:
+ value = getattr(node, field)
+ visited = self._visit_generic(value)
+ setattr(node, field, visited)
+ return self._transform(node)
+
+ def _visit_generic(self, node):
+ if isinstance(node, list):
+ return [self._visit_generic(child) for child in node]
+ elif isinstance(node, tuple):
+ return tuple(self._visit_generic(child) for child in node)
+ else:
+ return self._visit(node)
+
+ def register_transform(self, node_class, transform, predicate=None):
+ """Register `transform(node)` function to be applied on the given
+ astroid's `node_class` if `predicate` is None or returns true
+ when called with the node as argument.
+
+ The transform function may return a value which is then used to
+ substitute the original node in the tree.
+ """
+ self.transforms[node_class].append((transform, predicate))
+
+ def unregister_transform(self, node_class, transform, predicate=None):
+ """Unregister the given transform."""
+ self.transforms[node_class].remove((transform, predicate))
+
+ def visit(self, module):
+ """Walk the given astroid *tree* and transform each encountered node
+
+ Only the nodes which have transforms registered will actually
+ be replaced or changed.
+ """
+ module.body = [self._visit(child) for child in module.body]
+ return self._transform(module)
diff --git a/astroid/util.py b/astroid/util.py
new file mode 100644
index 00000000..8dde9b97
--- /dev/null
+++ b/astroid/util.py
@@ -0,0 +1,38 @@
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of astroid.
+#
+# astroid is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 2.1 of the License, or (at your
+# option) any later version.
+#
+# astroid is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+#
+# The code in this file was originally part of logilab-common, licensed under
+# the same license.
+
+class _Yes(object):
+ """Special inference object, which is returned when inference fails."""
+ def __repr__(self):
+ return 'YES'
+
+ def __getattribute__(self, name):
+ if name == 'next':
+ raise AttributeError('next method should not be called')
+ if name.startswith('__') and name.endswith('__'):
+ return super(_Yes, self).__getattribute__(name)
+ return self
+
+ def __call__(self, *args, **kwargs):
+ return self
+
+
+YES = _Yes()
diff --git a/tox.ini b/tox.ini
index ebb4c224..466a6fa3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,22 +1,20 @@
-[tox]
-# official list is
-# envlist = py27, py33, py34, pypy, jython
-# envlist = py27, py34, pylint
-envlist = py27, py33
-
-[testenv:pylint]
-deps =
- lazy-object-proxy
- logilab-common
- six
- hg+https://bitbucket.org/logilab/astroid
- hg+https://bitbucket.org/logilab/pylint
-commands = pylint -rn --rcfile={toxinidir}/pylintrc {envsitepackagesdir}/astroid
-
-[testenv]
-deps =
- lazy-object-proxy
- logilab-common
- six
-
-commands = python -m unittest discover -s {envsitepackagesdir}/astroid/tests -p "unittest*.py"
+[tox]
+# official list is
+# envlist = py27, py33, py34, pypy, jython
+# envlist = py27, py34, pylint
+envlist = py27, py33
+
+[testenv:pylint]
+deps =
+ lazy-object-proxy
+ six
+ hg+https://bitbucket.org/logilab/astroid
+ hg+https://bitbucket.org/logilab/pylint
+commands = pylint -rn --rcfile={toxinidir}/pylintrc {envsitepackagesdir}/astroid
+
+[testenv]
+deps =
+ lazy-object-proxy
+ six
+
+commands = python -m unittest discover -s {envsitepackagesdir}/astroid/tests -p "unittest*.py"