summaryrefslogtreecommitdiff
path: root/astroid/scoped_nodes.py
diff options
context:
space:
mode:
Diffstat (limited to 'astroid/scoped_nodes.py')
-rw-r--r--astroid/scoped_nodes.py126
1 files changed, 67 insertions, 59 deletions
diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py
index 3af9267..475cb38 100644
--- a/astroid/scoped_nodes.py
+++ b/astroid/scoped_nodes.py
@@ -27,7 +27,6 @@ import itertools
import warnings
import six
-import wrapt
from astroid import bases
from astroid import context as contextmod
@@ -43,7 +42,7 @@ BUILTINS = six.moves.builtins.__name__
ITER_METHODS = ('__iter__', '__getitem__')
-def _c3_merge(sequences):
+def _c3_merge(sequences, cls, context):
"""Merges MROs in *sequences* to a single MRO using the C3 algorithm.
Adapted from http://www.python.org/download/releases/2.3/mro/.
@@ -65,12 +64,10 @@ def _c3_merge(sequences):
if not candidate:
# Show all the remaining bases, which were considered as
# candidates for the next mro sequence.
- bases = ["({})".format(", ".join(base.name
- for base in subsequence))
- for subsequence in sequences]
raise exceptions.InconsistentMroError(
- "Cannot create a consistent method resolution "
- "order for bases %s" % ", ".join(bases))
+ message="Cannot create a consistent method resolution order "
+ "for MROs {mros} of class {cls!r}.",
+ mros=sequences, cls=cls, context=context)
result.append(candidate)
# remove the chosen candidate
@@ -79,21 +76,13 @@ def _c3_merge(sequences):
del seq[0]
-def _verify_duplicates_mro(sequences):
+def _verify_duplicates_mro(sequences, cls, context):
for sequence in sequences:
names = [node.qname() for node in sequence]
if len(names) != len(set(names)):
- raise exceptions.DuplicateBasesError('Duplicates found in the mro.')
-
-
-def remove_nodes(cls):
- @wrapt.decorator
- def decorator(func, instance, args, kwargs):
- nodes = [n for n in func(*args, **kwargs) if not isinstance(n, cls)]
- if not nodes:
- raise exceptions.NotFoundError()
- return nodes
- return decorator
+ raise exceptions.DuplicateBasesError(
+ message='Duplicates found in MROs {mros} for {cls!r}.',
+ mros=sequences, cls=cls, context=context)
def function_to_method(n, klass):
@@ -116,7 +105,8 @@ def std_special_attributes(self, name, add_locals=True):
return [node_classes.const_factory(self.doc)] + locals.get(name, [])
if name == '__dict__':
return [node_classes.Dict()] + locals.get(name, [])
- raise exceptions.NotFoundError(name)
+ # TODO: missing context
+ raise exceptions.AttributeInferenceError(target=self, attribute=name)
MANAGER = manager.AstroidManager()
@@ -344,7 +334,7 @@ class Module(LocalsDictNodeNG):
if name in self.scope_attrs and name not in self.locals:
try:
return self, self.getattr(name)
- except exceptions.NotFoundError:
+ except exceptions.AttributeInferenceError:
return self, ()
return self._scope_lookup(node, name, offset)
@@ -354,22 +344,30 @@ class Module(LocalsDictNodeNG):
def display_type(self):
return 'Module'
- @remove_nodes(node_classes.DelName)
def getattr(self, name, context=None, ignore_locals=False):
+ result = []
if name in self.special_attributes:
if name == '__file__':
- return [node_classes.const_factory(self.file)] + self.locals.get(name, [])
- if name == '__path__' and self.package:
- return [node_classes.List()] + self.locals.get(name, [])
- return std_special_attributes(self, name)
- if not ignore_locals and name in self.locals:
- return self.locals[name]
- if self.package:
+ result = ([node_classes.const_factory(self.file)] +
+ self.locals.get(name, []))
+ elif name == '__path__' and self.package:
+ result = [node_classes.List()] + self.locals.get(name, [])
+ else:
+ result = std_special_attributes(self, name)
+ elif not ignore_locals and name in self.locals:
+ result = self.locals[name]
+ elif self.package:
try:
- return [self.import_module(name, relative_only=True)]
+ result = [self.import_module(name, relative_only=True)]
except (exceptions.AstroidBuildingException, SyntaxError):
- util.reraise(exceptions.NotFoundError(name))
- raise exceptions.NotFoundError(name)
+ util.reraise(exceptions.AttributeInferenceError(target=self,
+ attribute=name,
+ context=context))
+ result = [n for n in result if not isinstance(n, node_classes.DelName)]
+ if result:
+ return result
+ raise exceptions.AttributeInferenceError(target=self, attribute=name,
+ context=context)
def igetattr(self, name, context=None):
"""inferred getattr"""
@@ -380,8 +378,9 @@ class Module(LocalsDictNodeNG):
try:
return bases._infer_stmts(self.getattr(name, context),
context, frame=self)
- except exceptions.NotFoundError:
- util.reraise(exceptions.InferenceError(name))
+ except exceptions.AttributeInferenceError as error:
+ util.reraise(exceptions.InferenceError(
+ error.message, target=self, attribute=name, context=context))
def fully_defined(self):
"""return True if this module has been built from a .py file
@@ -855,8 +854,9 @@ class FunctionDef(node_classes.Statement, Lambda):
try:
return bases._infer_stmts(self.getattr(name, context),
context, frame=self)
- except exceptions.NotFoundError:
- util.reraise(exceptions.InferenceError(name))
+ except exceptions.AttributeInferenceError as error:
+ util.reraise(exceptions.InferenceError(
+ error.message, target=self, attribute=name, context=context))
def is_method(self):
"""return true if the function node should be considered as a method"""
@@ -1303,28 +1303,32 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
def has_base(self, node):
return node in self.bases
- @remove_nodes(node_classes.DelAttr)
def local_attr(self, name, context=None):
"""return the list of assign node associated to name in this class
locals or in its parents
- :raises `NotFoundError`:
+ :raises `AttributeInferenceError`:
if no attribute with this name has been find in this class or
its parent classes
"""
- try:
- return self.locals[name]
- except KeyError:
- for class_node in self.local_attr_ancestors(name, context):
- return class_node.locals[name]
- raise exceptions.NotFoundError(name)
+ result = []
+ if name in self.locals:
+ result = self.locals[name]
+ else:
+ class_node = next(self.local_attr_ancestors(name, context), ())
+ if class_node:
+ result = class_node.locals[name]
+ result = [n for n in result if not isinstance(n, node_classes.DelAttr)]
+ if result:
+ return result
+ raise exceptions.AttributeInferenceError(target=self, attribute=name,
+ context=context)
- @remove_nodes(node_classes.DelAttr)
def instance_attr(self, name, context=None):
"""return the astroid nodes associated to name in this class instance
attributes dictionary and in its parents
- :raises `NotFoundError`:
+ :raises `AttributeInferenceError`:
if no attribute with this name has been find in this class or
its parent classes
"""
@@ -1334,9 +1338,11 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
# get all values from parents
for class_node in self.instance_attr_ancestors(name, context):
values += class_node.instance_attrs[name]
- if not values:
- raise exceptions.NotFoundError(name)
- return values
+ values = [n for n in values if not isinstance(n, node_classes.DelAttr)]
+ if values:
+ return values
+ raise exceptions.AttributeInferenceError(target=self, attribute=name,
+ context=context)
def instantiate_class(self):
"""return Instance of ClassDef node, else return self"""
@@ -1387,7 +1393,8 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
if class_context:
values += self._metaclass_lookup_attribute(name, context)
if not values:
- raise exceptions.NotFoundError(name)
+ raise exceptions.AttributeInferenceError(target=self, attribute=name,
+ context=context)
return values
def _metaclass_lookup_attribute(self, name, context):
@@ -1406,7 +1413,7 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
try:
attrs = cls.getattr(name, context=context,
class_context=True)
- except exceptions.NotFoundError:
+ except exceptions.AttributeInferenceError:
return
for attr in bases._infer_stmts(attrs, context, frame=cls):
@@ -1448,18 +1455,19 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
and isinstance(inferred, bases.Instance)):
try:
inferred._proxied.getattr('__get__', context)
- except exceptions.NotFoundError:
+ except exceptions.AttributeInferenceError:
yield inferred
else:
yield util.Uninferable
else:
yield function_to_method(inferred, self)
- except exceptions.NotFoundError:
+ except exceptions.AttributeInferenceError as error:
if not name.startswith('__') and self.has_dynamic_getattr(context):
# class handle some dynamic attributes, return a Uninferable object
yield util.Uninferable
else:
- util.reraise(exceptions.InferenceError(name))
+ util.reraise(exceptions.InferenceError(
+ error.message, target=self, attribute=name, context=context))
def has_dynamic_getattr(self, context=None):
"""
@@ -1476,12 +1484,12 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
try:
return _valid_getattr(self.getattr('__getattr__', context)[0])
- except exceptions.NotFoundError:
+ except exceptions.AttributeInferenceError:
#if self.newstyle: XXX cause an infinite recursion error
try:
getattribute = self.getattr('__getattribute__', context)[0]
return _valid_getattr(getattribute)
- except exceptions.NotFoundError:
+ except exceptions.AttributeInferenceError:
pass
return False
@@ -1601,7 +1609,7 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
try:
slots.getattr(meth)
break
- except exceptions.NotFoundError:
+ except exceptions.AttributeInferenceError:
continue
else:
continue
@@ -1731,8 +1739,8 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
bases_mro.append(ancestors)
unmerged_mro = ([[self]] + bases_mro + [bases])
- _verify_duplicates_mro(unmerged_mro)
- return _c3_merge(unmerged_mro)
+ _verify_duplicates_mro(unmerged_mro, self, context)
+ return _c3_merge(unmerged_mro, self, context)
def bool_value(self):
return True