diff options
Diffstat (limited to 'astroid/tree/scoped_nodes.py')
-rw-r--r-- | astroid/tree/scoped_nodes.py | 291 |
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) |