summaryrefslogtreecommitdiff
path: root/astroid/tree/scoped_nodes.py
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2015-12-29 18:32:49 +0200
committerClaudiu Popa <pcmanticore@gmail.com>2015-12-29 18:32:49 +0200
commit512fc01cd75b64b7e801f7e821a866b06086a31f (patch)
tree60be7c29f239731d0131e865dbc6b415826e1b43 /astroid/tree/scoped_nodes.py
parent736e08ced56ed3bdd8b84337e36fad192ccce0ca (diff)
downloadastroid-git-512fc01cd75b64b7e801f7e821a866b06086a31f.tar.gz
Move lookup specific bits into interpreter.lookup
This patch moves code which was targeted for implementing lookup support from scoped_nodes into interpreter.lookup, resulting in a more cohesive scoped_nodes module. The lookup functions were capable of using virtual base classes instead on relying on concrete classes offered by node_classes and scoped_nodes. The only minor complaint is that builtin_lookup still needs to depend on the manager, which is lazily loaded inside the function itself, at least until we'll get rid of this circular dependency.
Diffstat (limited to 'astroid/tree/scoped_nodes.py')
-rw-r--r--astroid/tree/scoped_nodes.py291
1 files changed, 11 insertions, 280 deletions
diff --git a/astroid/tree/scoped_nodes.py b/astroid/tree/scoped_nodes.py
index 43c5cc06..8226c281 100644
--- a/astroid/tree/scoped_nodes.py
+++ b/astroid/tree/scoped_nodes.py
@@ -38,6 +38,7 @@ from astroid.tree import base
from astroid import context as contextmod
from astroid import exceptions
from astroid import decorators as decorators_mod
+from astroid.interpreter import lookup
from astroid.interpreter import objects
from astroid.interpreter import runtimeabc
from astroid.interpreter.util import infer_stmts
@@ -129,138 +130,8 @@ def std_special_attributes(self, name, add_locals=True):
raise exceptions.AttributeInferenceError(target=self, attribute=name)
-def builtin_lookup(name):
- """lookup a name into the builtin module
- return the list of matching statements and the astroid for the builtin
- module
- """
- builtin_astroid = MANAGER.ast_from_module(six.moves.builtins)
- if name == '__dict__':
- return builtin_astroid, ()
- stmts = builtin_astroid.locals.get(name, ())
- # Use inference to find what AssignName nodes point to in builtins.
- stmts = [next(s.infer()) if isinstance(s, node_classes.AssignName) else s
- for s in stmts]
- return builtin_astroid, stmts
-
-
-# TODO move this Mixin to mixins.py; problem: 'FunctionDef' in _scope_lookup
-class LocalsDictNodeNG(mixins.LookupMixIn,
- treebase.NodeNG):
- """ this class provides locals handling common to Module, FunctionDef
- and ClassDef nodes, including a dict like interface for direct access
- to locals information
- """
-
- # attributes below are set by the builder module or by raw factories
-
- # dictionary of locals with name as key and node defining the local as
- # value
-
- @property
- def locals(self):
- return MappingProxyType(get_locals(self))
-
- def qname(self):
- """return the 'qualified' name of the node, eg module.name,
- module.class.name ...
- """
- if self.parent is None:
- return self.name
- return '%s.%s' % (self.parent.frame().qname(), self.name)
-
- def frame(self):
- """return the first parent frame node (i.e. Module, FunctionDef or
- ClassDef)
-
- """
- return self
-
- def _scope_lookup(self, node, name, offset=0):
- """XXX method for interfacing the scope lookup"""
- try:
- stmts = node._filter_stmts(self.locals[name], self, offset)
- except KeyError:
- stmts = ()
- if stmts:
- return self, stmts
- if self.parent: # i.e. not Module
- # nested scope: if parent scope is a function, that's fine
- # else jump to the module
- pscope = self.parent.scope()
- if not pscope.is_function:
- pscope = pscope.root()
- return pscope.scope_lookup(node, name)
- return builtin_lookup(name) # Module
-
- def set_local(self, name, stmt):
- raise Exception('Attempted locals mutation.')
-
- # def set_local(self, name, stmt):
- # """define <name> in locals (<stmt> is the node defining the name)
- # if the node is a Module node (i.e. has globals), add the name to
- # globals
-
- # if the name is already defined, ignore it
- # """
- # #assert not stmt in self.locals.get(name, ()), (self, stmt)
- # self.locals.setdefault(name, []).append(stmt)
-
- __setitem__ = set_local
-
- # def _append_node(self, child):
- # """append a child, linking it in the tree"""
- # self.body.append(child)
- # child.parent = self
-
- # def add_local_node(self, child_node, name=None):
- # """append a child which should alter locals to the given node"""
- # if name != '__class__':
- # # add __class__ node as a child will cause infinite recursion later!
- # self._append_node(child_node)
- # self.set_local(name or child_node.name, child_node)
-
- def __getitem__(self, item):
- """method from the `dict` interface returning the first node
- associated with the given name in the locals dictionary
-
- :type item: str
- :param item: the name of the locally defined object
- :raises KeyError: if the name is not defined
- """
- return self.locals[item][0]
-
- def __iter__(self):
- """method from the `dict` interface returning an iterator on
- `self.keys()`
- """
- return iter(self.locals)
-
- def keys(self):
- """method from the `dict` interface returning a tuple containing
- locally defined names
- """
- return self.locals.keys()
-
- def values(self):
- """method from the `dict` interface returning a tuple containing
- locally defined nodes which are instance of `FunctionDef` or `ClassDef`
- """
- return tuple(v[0] for v in self.locals.values())
-
- def items(self):
- """method from the `dict` interface returning a list of tuple
- containing each locally defined name with its associated node,
- which is an instance of `FunctionDef` or `ClassDef`
- """
- return tuple((k, v[0]) for k, v in self.locals.items())
-
- def __contains__(self, name):
- return name in self.locals
-
-
@util.register_implementation(treeabc.Module)
-class Module(LocalsDictNodeNG):
+class Module(lookup.LocalsDictNode):
_astroid_fields = ('body',)
fromlineno = 0
@@ -368,7 +239,7 @@ class Module(LocalsDictNodeNG):
@property
def globals(self):
- return MappingProxyType(get_locals(self))
+ return MappingProxyType(lookup.get_locals(self))
@property
def future_imports(self):
@@ -612,11 +483,11 @@ class Module(LocalsDictNodeNG):
return True
-class ComprehensionScope(LocalsDictNodeNG):
+class ComprehensionScope(lookup.LocalsDictNode):
def frame(self):
return self.parent.frame()
- scope_lookup = LocalsDictNodeNG._scope_lookup
+ scope_lookup = lookup.LocalsDictNode._scope_lookup
@util.register_implementation(treeabc.GeneratorExp)
@@ -971,7 +842,7 @@ class CallSite(object):
@util.register_implementation(treeabc.Lambda)
-class Lambda(mixins.FilterStmtsMixin, LocalsDictNodeNG):
+class Lambda(mixins.FilterStmtsMixin, lookup.LocalsDictNode):
_astroid_fields = ('args', 'body',)
_other_other_fields = ('locals',)
name = '<lambda>'
@@ -1431,7 +1302,7 @@ def get_wrapping_class(node):
@util.register_implementation(treeabc.ClassDef)
-class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
+class ClassDef(mixins.FilterStmtsMixin, lookup.LocalsDictNode,
node_classes.Statement):
# some of the attributes below are set by the builder module or
@@ -1476,7 +1347,7 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
@property
def locals(self):
# return get_locals(self)
- return MappingProxyType(get_locals(self))
+ return MappingProxyType(lookup.get_locals(self))
# @property
# def instance_attrs(self):
@@ -1630,7 +1501,7 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
context = contextmod.InferenceContext()
if six.PY3:
if not self.bases and self.qname() != 'builtins.object':
- yield builtin_lookup("object")[1][0]
+ yield lookup.builtin_lookup("object")[1][0]
return
for stmt in self.bases:
@@ -1900,7 +1771,7 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
"""
if self.newstyle:
- return builtin_lookup('type')[1][0]
+ return lookup.builtin_lookup('type')[1][0]
_metaclass = None
def declared_metaclass(self):
@@ -2098,7 +1969,7 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
context = contextmod.InferenceContext()
if six.PY3:
if not self.bases and self.qname() != 'builtins.object':
- yield builtin_lookup("object")[1][0]
+ yield lookup.builtin_lookup("object")[1][0]
return
for stmt in self.bases:
@@ -2150,146 +2021,6 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
return True
-@_singledispatch
-def get_locals(node):
- '''Return the local variables for an appropriate node.
-
- For function nodes, this will be the local variables defined in
- their scope, what would be returned by a locals() call in the
- function body. For Modules, this will be all the global names
- defined in the module, what would be returned by a locals() or
- globals() call at the module level. For classes, this will be
- class attributes defined in the class body, also what a locals()
- call in the body would return.
-
- This function starts by recursing over its argument's children to
- avoid incorrectly adding a class's, function's, or module's name
- to its own local variables.
-
- Args:
- node (LocalsDictNodeNG): A node defining a scope to return locals for.
-
- Returns:
- A defaultdict(list) mapping names (strings) to lists of nodes.
-
- Raises:
- TypeError: When called on a node that doesn't represent a scope or a
- non-node object.
- '''
- raise TypeError("This isn't an astroid node: %s" % type(node))
-
-# pylint: disable=unused-variable; doesn't understand singledispatch
-@get_locals.register(base.NodeNG)
-def not_scoped_node(node):
- raise TypeError("This node doesn't have local variables: %s" % type(node))
-
-# pylint: disable=unused-variable; doesn't understand singledispatch
-@get_locals.register(LocalsDictNodeNG)
-def scoped_node(node):
- locals_ = collections.defaultdict(list)
- for n in node.get_children():
- _get_locals(n, locals_)
- return locals_
-
-
-@_singledispatch
-def _get_locals(node, locals_):
- '''Return the local variables for a node.
-
- This is the internal recursive generic function for gathering
- nodes into a local variables mapping. The locals mapping is
- passed down and mutated by each function.
-
- Args:
- node (NodeNG): The node to inspect for assignments to locals.
- locals_ (defaultdict(list)): A mapping of (strings) to lists of nodes.
-
- Raises:
- TypeError: When called on a non-node object.
-
- '''
-
- raise TypeError('Non-astroid object in an astroid AST: %s' % type(node))
-
-# pylint: disable=unused-variable; doesn't understand singledispatch
-@_get_locals.register(base.NodeNG)
-def locals_generic(node, locals_):
- '''Generic nodes don't create name bindings or scopes.'''
- for n in node.get_children():
- _get_locals(n, locals_)
-
-# # pylint: disable=unused-variable; doesn't understand singledispatch
-@_get_locals.register(LocalsDictNodeNG)
-def locals_new_scope(node, locals_):
- '''These nodes start a new scope, so terminate recursion here.'''
-
-# pylint: disable=unused-variable; doesn't understand singledispatch
-@_get_locals.register(node_classes.AssignName)
-@_get_locals.register(node_classes.DelName)
-@_get_locals.register(FunctionDef)
-@_get_locals.register(ClassDef)
-def locals_name(node, locals_):
- '''These nodes add a name to the local variables. AssignName and
- DelName have no children while FunctionDef and ClassDef start a
- new scope so shouldn't be recursed into.'''
- locals_[node.name].append(node)
-
-@_get_locals.register(node_classes.InterpreterObject)
-def locals_interpreter_object(node, locals_):
- '''InterpreterObjects add an object to the local variables under a specified
- name.'''
- if node.name:
- locals_[node.name].append(node)
-
-@_get_locals.register(node_classes.ReservedName)
-def locals_reserved_name(node, locals_):
- '''InterpreterObjects add an object to the local variables under a specified
- name.'''
- locals_[node.name].append(node.value)
-
-# pylint: disable=unused-variable; doesn't understand singledispatch
-@_get_locals.register(node_classes.Arguments)
-def locals_arguments(node, locals_):
- '''Other names assigned by functions have AssignName nodes that are
- children of an Arguments node.'''
- if node.vararg:
- locals_[node.vararg].append(node)
- if node.kwarg:
- locals_[node.kwarg].append(node)
- for n in node.get_children():
- _get_locals(n, locals_)
-
-# pylint: disable=unused-variable; doesn't understand singledispatch
-@_get_locals.register(node_classes.Import)
-def locals_import(node, locals_):
- for name, asname in node.names:
- name = asname or name
- locals_[name.split('.')[0]].append(node)
-
-# pylint: disable=unused-variable; doesn't understand singledispatch
-@_get_locals.register(node_classes.ImportFrom)
-def locals_import_from(node, locals_):
- # Don't add future imports to locals.
- if node.modname == '__future__':
- return
- # Inherited code, I don't know why this function sorts this list.
- def sort_locals(my_list):
- my_list.sort(key=lambda node: node.fromlineno)
-
- for name, asname in node.names:
- if name == '*':
- try:
- imported = node.do_import_module()
- except exceptions.AstroidBuildingError:
- continue
- for name in imported.wildcard_import_names():
- locals_[name].append(node)
- sort_locals(locals_[name])
- else:
- locals_[asname or name].append(node)
- sort_locals(locals_[asname or name])
-
-
# Backwards-compatibility aliases
Class = util.proxy_alias('Class', ClassDef)
Function = util.proxy_alias('Function', FunctionDef)