diff options
-rw-r--r-- | inference.py | 1 | ||||
-rw-r--r-- | infutils.py | 66 | ||||
-rw-r--r-- | lookup.py | 139 | ||||
-rw-r--r-- | node_classes.py | 108 | ||||
-rw-r--r-- | nodes.py | 39 | ||||
-rw-r--r-- | patchcomptransformer.py | 3 | ||||
-rw-r--r-- | protocols.py | 3 | ||||
-rw-r--r-- | rebuilder.py | 15 | ||||
-rw-r--r-- | scoped_nodes.py | 304 |
9 files changed, 416 insertions, 262 deletions
diff --git a/inference.py b/inference.py index 9e60662f..1bd424b7 100644 --- a/inference.py +++ b/inference.py @@ -404,3 +404,4 @@ def infer_empty_node(self, context=None): except ASTNGError: yield YES nodes.EmptyNode.infer = path_wrapper(infer_empty_node) + diff --git a/infutils.py b/infutils.py index b4e5650c..a796eed9 100644 --- a/infutils.py +++ b/infutils.py @@ -26,7 +26,6 @@ from logilab.common.compat import chain, imap from logilab.astng._exceptions import InferenceError, NotFoundError, UnresolvableName from logilab.astng._nodes import BaseClass -from logilab.astng.node_classes import List, Tuple, If, TryExcept class Proxy(BaseClass): @@ -77,71 +76,6 @@ class InferenceContext(object): clone.boundnode = self.boundnode return clone - -def are_exclusive(stmt1, stmt2, exceptions=None): - """return true if the two given statements are mutually exclusive - - `exceptions` may be a list of exception names. If specified, discard If - branches and check one of the statement is in an exception handler catching - one of the given exceptions. - - algorithm : - 1) index stmt1's parents - 2) climb among stmt2's parents until we find a common parent - 3) if the common parent is a If or TryExcept statement, look if nodes are - in exclusive branches - """ - # index stmt1's parents - stmt1_parents = {} - children = {} - node = stmt1.parent - previous = stmt1 - while node: - stmt1_parents[node] = 1 - children[node] = previous - previous = node - node = node.parent - # climb among stmt2's parents until we find a common parent - node = stmt2.parent - previous = stmt2 - while node: - if stmt1_parents.has_key(node): - # if the common parent is a If or TryExcept statement, look if - # nodes are in exclusive branches - if isinstance(node, If) and exceptions is None: - if (node.locate_child(previous)[1] - is not node.locate_child(children[node])[1]): - return True - elif isinstance(node, TryExcept): - c2attr, c2node = node.locate_child(previous) - c1attr, c1node = node.locate_child(children[node]) - if c1node is not c2node: - if ((c2attr == 'body' and c1attr == 'handlers' and children[node].catch(exceptions)) or - (c2attr == 'handlers' and c1attr == 'body' and previous.catch(exceptions)) or - (c2attr == 'handlers' and c1attr == 'orelse') or - (c2attr == 'orelse' and c1attr == 'handlers')): - return True - elif c2attr == 'handlers' and c1attr == 'handlers': - return previous is not children[node] - return False - previous = node - node = node.parent - return False - - -def unpack_infer(stmt, context=None): - """return an iterator on nodes inferred by the given statement if the inferred - value is a list or a tuple, recurse on it to get values inferred by its - content - """ - if isinstance(stmt, (List, Tuple)): - # XXX loosing context - return chain(*imap(unpack_infer, stmt.elts)) - infered = stmt.infer(context).next() - if infered is stmt: - return iter( (stmt,) ) - return chain(*imap(unpack_infer, stmt.infer(context))) - def copy_context(context): if context is not None: return context.clone() @@ -33,8 +33,8 @@ import __builtin__ from logilab.astng import MANAGER, NotFoundError from logilab.astng import nodes # XXX this will not work ?? circular import from logilab.astng._nodes import BaseClass, NodeNG -from logilab.astng.infutils import are_exclusive, copy_context, _infer_stmts - +from logilab.astng.infutils import copy_context, _infer_stmts +from logilab.astng.node_classes import are_exclusive class LookupMixIn(BaseClass): """Mixin looking up a name in the right scope @@ -175,141 +175,6 @@ class LookupMixIn(BaseClass): _stmt_parents.append(stmt.parent) return _stmts - -def builtin_lookup(name): - """lookup a name into the builtin module - return the list of matching statements and the astng for the builtin - module - """ - builtinastng = MANAGER.astng_from_module(__builtin__) - if name == '__dict__': - return builtinastng, () - try: - stmts = builtinastng.locals[name] - except KeyError: - stmts = () - return builtinastng, stmts - -class LocalsDictNodeNG(LookupMixIn, NodeNG): - """ this class provides locals handling common to Module, Function - and Class 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 - locals = None - - 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, Function or Class) - """ - return self - - def scope(self): - """return the first node defining a new scope (i.e. Module, - Function, Class, Lambda but also GenExpr) - """ - 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 isinstance(pscope, nodes.Function): - pscope = pscope.root() - return pscope.scope_lookup(node, name) - return builtin_lookup(name) # Module - - - - 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 self.locals is not None, (self, id(self)) - #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.keys()) - - 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 `Function` or `Class` - """ - return [self[key] for key in self.keys()] - - 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 `Function` or `Class` - """ - return zip(self.keys(), self.values()) - - def has_key(self, name): - """method from the `dict` interface returning True if the given - name is defined in the locals dictionary - """ - return self.locals.has_key(name) - - __contains__ = has_key - # maybe import at the end ? from logilab.astng import nodes diff --git a/node_classes.py b/node_classes.py index 8598311c..d7deb76b 100644 --- a/node_classes.py +++ b/node_classes.py @@ -1,14 +1,79 @@ # from logilab.astng import (ASTNGBuildingException, InferenceError, NotFoundError, NoDefault) -from logilab.astng._nodes import NodeNG, StmtMixIn, BlockRangeMixIn -from logilab.astng.lookup import LookupMixIn +from logilab.astng._nodes import NodeNG, StmtMixIn, BlockRangeMixIn, BaseClass, _const_factory from logilab.astng.infutils import Instance """ Module for all nodes (except scoped nodes). """ +def unpack_infer(stmt, context=None): + """return an iterator on nodes inferred by the given statement if the inferred + value is a list or a tuple, recurse on it to get values inferred by its + content + """ + if isinstance(stmt, (List, Tuple)): + # XXX loosing context + return chain(*imap(unpack_infer, stmt.elts)) + infered = stmt.infer(context).next() + if infered is stmt: + return iter( (stmt,) ) + return chain(*imap(unpack_infer, stmt.infer(context))) + + + +def are_exclusive(stmt1, stmt2, exceptions=None): + """return true if the two given statements are mutually exclusive + + `exceptions` may be a list of exception names. If specified, discard If + branches and check one of the statement is in an exception handler catching + one of the given exceptions. + + algorithm : + 1) index stmt1's parents + 2) climb among stmt2's parents until we find a common parent + 3) if the common parent is a If or TryExcept statement, look if nodes are + in exclusive branches + """ + # index stmt1's parents + stmt1_parents = {} + children = {} + node = stmt1.parent + previous = stmt1 + while node: + stmt1_parents[node] = 1 + children[node] = previous + previous = node + node = node.parent + # climb among stmt2's parents until we find a common parent + node = stmt2.parent + previous = stmt2 + while node: + if stmt1_parents.has_key(node): + # if the common parent is a If or TryExcept statement, look if + # nodes are in exclusive branches + if isinstance(node, If) and exceptions is None: + if (node.locate_child(previous)[1] + is not node.locate_child(children[node])[1]): + return True + elif isinstance(node, TryExcept): + c2attr, c2node = node.locate_child(previous) + c1attr, c1node = node.locate_child(children[node]) + if c1node is not c2node: + if ((c2attr == 'body' and c1attr == 'handlers' and children[node].catch(exceptions)) or + (c2attr == 'handlers' and c1attr == 'body' and previous.catch(exceptions)) or + (c2attr == 'handlers' and c1attr == 'orelse') or + (c2attr == 'orelse' and c1attr == 'handlers')): + return True + elif c2attr == 'handlers' and c1attr == 'handlers': + return previous is not children[node] + return False + previous = node + node = node.parent + return False + + class Arguments(NodeNG): """class representing an Arguments node""" @@ -86,10 +151,6 @@ class AssAttr(NodeNG): """class representing an AssAttr node""" -class AssName(LookupMixIn, NodeNG): - """class representing an AssName node""" - - class Assert(StmtMixIn, NodeNG): """class representing an Assert node""" @@ -166,10 +227,6 @@ class DelAttr(NodeNG): """class representing a DelAttr node""" -class DelName(LookupMixIn, NodeNG): - """class representing a DelName node""" - - class Delete(StmtMixIn, NodeNG): """class representing a Delete node""" @@ -351,10 +408,6 @@ class ListComp(NodeNG): """class representing a ListComp node""" -class Name(LookupMixIn, NodeNG): - """class representing a Name node""" - - class Pass(StmtMixIn, NodeNG): """class representing a Pass node""" @@ -456,3 +509,30 @@ class With(BlockRangeMixIn, StmtMixIn, NodeNG): class Yield(NodeNG): """class representing a Yield node""" +# constants ############################################################## + +CONST_CLS = { + list: List, + tuple: Tuple, + dict: Dict, + } + +def const_factory(value): + """return an astng node for a python value""" + try: + # if value is of class list, tuple, dict use specific class, not Const + cls = CONST_CLS[value.__class__] + node = cls() + if isinstance(node, Dict): + node.items = () + else: + node.elts = () + except KeyError: + try: + node = Const(value) + except KeyError: + node = _const_factory(value) + return node + + + @@ -44,16 +44,16 @@ from itertools import imap from logilab.astng._exceptions import UnresolvableName, NotFoundError, \ InferenceError, ASTNGError from logilab.astng.utils import REDIRECT -from logilab.astng._nodes import _const_factory +from logilab.astng._nodes import class_factory, module_factory -from logilab.astng.node_classes import (Arguments, AssAttr, AssName, Assert, +from logilab.astng.node_classes import (Arguments, AssAttr, Assert, Assign, AugAssign, Backquote, BinOp, BoolOp, Break, CallFunc, Compare, - Comprehension, Const, Continue, Decorators, DelAttr, DelName, Delete, + Comprehension, Const, Continue, Decorators, DelAttr, Delete, Dict, Discard, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, For, From, Getattr, Global, If, IfExp, Import, Index, Keyword, - List, ListComp, Name, Pass, Print, Raise, Return, Slice, Subscript, - TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield) -from logilab.astng.scoped_nodes import Module, GenExpr, Lambda, Function, Class + List, ListComp, Pass, Print, Raise, Return, Slice, Subscript, + TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, const_factory ) +from logilab.astng.scoped_nodes import AssName, DelName, Name, Module, GenExpr, Lambda, Function, Class # astng fields definition #################################################### @@ -116,32 +116,5 @@ With._astng_fields = ('expr', 'vars', 'body') While._astng_fields = ('test', 'body', 'orelse',) Yield._astng_fields = ('value',) -# constants ... ############################################################## - -CONST_CLS = { - list: List, - tuple: Tuple, - dict: Dict, - } - -def const_factory(value): - """return an astng node for a python value""" - try: - # if value is of class list, tuple, dict use specific class, not Const - cls = CONST_CLS[value.__class__] - node = cls() - if isinstance(node, Dict): - node.items = () - else: - node.elts = () - except KeyError: - try: - node = Const(value) - except KeyError: - node = _const_factory(value) - return node - -LOCALS_NODES = (Class, Function, GenExpr, Lambda, Module) - diff --git a/patchcomptransformer.py b/patchcomptransformer.py index 586effe8..8744a781 100644 --- a/patchcomptransformer.py +++ b/patchcomptransformer.py @@ -18,12 +18,13 @@ :copyright: 2003-2009 Sylvain Thenault :contact: mailto:thenault@gmail.com """ +# TODO : move this module to _nodes_compiler from types import TupleType from token import DEDENT from compiler import transformer -from logilab.astng import nodes +import compiler.ast as nodes def fromto_lineno(asttuple): """return the minimum and maximum line number of the given ast tuple""" diff --git a/protocols.py b/protocols.py index b7962fe2..57039929 100644 --- a/protocols.py +++ b/protocols.py @@ -25,7 +25,8 @@ from __future__ import generators __doctype__ = "restructuredtext en" from logilab.astng import InferenceError, NoDefault, _nodes as nodes -from logilab.astng.infutils import copy_context, unpack_infer, \ +from logilab.astng.node_classes import unpack_infer +from logilab.astng.infutils import copy_context, \ raise_if_nothing_infered, yes_if_nothing_infered, Instance, Generator, YES from logilab.astng.nodes import Const, Class, Function, Tuple, List, \ const_factory diff --git a/rebuilder.py b/rebuilder.py index 90047215..b25f13c6 100644 --- a/rebuilder.py +++ b/rebuilder.py @@ -21,8 +21,9 @@ order to get a single ASTNG representation """ from logilab.astng import ASTNGBuildingException, InferenceError, NodeRemoved +from logilab.astng import _nodes from logilab.astng import nodes -from logilab.astng.utils import ASTVisitor +from logilab.astng.utils import ASTVisitor, REDIRECT from logilab.astng.infutils import YES, Instance @@ -38,8 +39,8 @@ class RebuildVisitor(ASTVisitor): self._metaclass = None self._global_names = None self._delayed = [] - self.rebuilder = nodes.TreeRebuilder(self) - self.set_line_info = nodes.AST_MODE == '_ast' + self.rebuilder = _nodes.TreeRebuilder(self) + self.set_line_info = _nodes.AST_MODE == '_ast' def _push(self, node): """update the stack and init some parts of the Function or Class node @@ -86,15 +87,19 @@ class RebuildVisitor(ASTVisitor): self.__asscontext = None def walk(self, node): - self._walk(node) + node = self._walk(node) delayed = self._delayed while delayed: dnode = delayed.pop(0) node_name = dnode.__class__.__name__.lower() self.delayed_visit_assattr(dnode) + return node def _walk(self, node, parent=None): """default visit method, handle the parent attribute""" + cls_name = node.__class__.__name__ + NGNode = getattr(nodes, REDIRECT.get(cls_name, cls_name)) + node = NGNode() node.parent = parent try: node.accept(self.rebuilder) @@ -114,7 +119,7 @@ class RebuildVisitor(ASTVisitor): if handle_leave: leave = getattr(self, "leave_" + node.__class__.__name__.lower()) leave(node) - + return node # general visit_<node> methods ############################################ diff --git a/scoped_nodes.py b/scoped_nodes.py index 8702f976..f0795758 100644 --- a/scoped_nodes.py +++ b/scoped_nodes.py @@ -36,13 +36,15 @@ from logilab.common.decorators import cached from logilab.astng import MANAGER, NotFoundError, NoDefault, \ ASTNGBuildingException, InferenceError -from logilab.astng.node_clases import (Const, Dict, From, List, Name, Pass, - Raise, Return, Tuple, Yield, AssName, DelAttr, DelName,) -from logilab.astng._nodes import const_factory as cf, NodeNG, StmtMixIn +from logilab.astng.node_classes import (Const, Dict, From, List, Pass, + Raise, Return, Tuple, Yield, DelAttr, are_exclusive, const_factory as cf, + unpack_infer) +from logilab.astng._nodes import NodeNG, StmtMixIn, BaseClass from logilab.astng.infutils import YES, InferenceContext, Instance, Generator, \ - UnboundMethod, copy_context, unpack_infer, _infer_stmts + UnboundMethod, _infer_stmts, copy_context + from logilab.astng.nodes_as_string import as_string -from logilab.astng.lookup import LocalsDictNodeNG + def remove_nodes(func, cls): def wrapper(*args, **kwargs): @@ -73,6 +75,298 @@ def std_special_attributes(self, name, add_locals=True): raise NotFoundError(name) +# MixIns ----------------------------------------------------- + + + +class LookupMixIn(BaseClass): + """Mixin looking up a name in the right scope + """ + + def lookup(self, name): + """lookup a variable name + + return the scope node and the list of assignments associated to the given + name according to the scope where it has been found (locals, globals or + builtin) + + The lookup is starting from self's scope. If self is not a frame itself and + the name is found in the inner frame locals, statements will be filtered + to remove ignorable statements according to self's location + """ + return self.scope().scope_lookup(self, name) + + def ilookup(self, name, context=None): + """infered lookup + + return an iterator on infered values of the statements returned by + the lookup method + """ + frame, stmts = self.lookup(name) + context = copy_context(context) + context.lookupname = name + return _infer_stmts(stmts, context, frame) + + def _filter_stmts(self, stmts, frame, offset): + """filter statements to remove ignorable statements. + + If self is not a frame itself and the name is found in the inner + frame locals, statements will be filtered to remove ignorable + statements according to self's location + """ + # if offset == -1, my actual frame is not the inner frame but its parent + # + # class A(B): pass + # + # we need this to resolve B correctly + if offset == -1: + myframe = self.frame().parent.frame() + else: + myframe = self.frame() + if not myframe is frame or self is frame: + return stmts + mystmt = self.statement() + # line filtering if we are in the same frame + # + # take care node may be missing lineno information (this is the case for + # nodes inserted for living objects) + if myframe is frame and mystmt.fromlineno is not None: + assert mystmt.fromlineno is not None, mystmt + mylineno = mystmt.fromlineno + offset + else: + # disabling lineno filtering + mylineno = 0 + _stmts = [] + _stmt_parents = [] + for node in stmts: + stmt = node.statement() + # line filtering is on and we have reached our location, break + if mylineno > 0 and stmt.fromlineno > mylineno: + break + if isinstance(node, nodes.Class) and self in node.bases: + break + assert hasattr(node, 'ass_type'), (node, node.scope(), + node.scope().locals) + ass_type = node.ass_type() + if ass_type is mystmt and not isinstance(ass_type, (nodes.Class, + nodes.Function, nodes.Import, nodes.From, nodes.Lambda)): + if not isinstance(ass_type, nodes.Comprehension): + break + if isinstance(self, (nodes.Const, nodes.Name)): + _stmts = [self] + break + elif ass_type.statement() is mystmt: + # original node's statement is the assignment, only keeps + # current node (gen exp, list comp) + _stmts = [node] + break + optional_assign = isinstance(ass_type, (nodes.For, nodes.Comprehension)) + if optional_assign and ass_type.parent_of(self): + # we are inside a loop, loop var assigment is hidding previous + # assigment + _stmts = [node] + _stmt_parents = [stmt.parent] + continue + # XXX comment various branches below!!! + try: + pindex = _stmt_parents.index(stmt.parent) + except ValueError: + pass + else: + # we got a parent index, this means the currently visited node + # is at the same block level as a previously visited node + if _stmts[pindex].ass_type().parent_of(ass_type): + # both statements are not at the same block level + continue + # if currently visited node is following previously considered + # assignement and both are not exclusive, we can drop the + # previous one. For instance in the following code :: + # + # if a: + # x = 1 + # else: + # x = 2 + # print x + # + # we can't remove neither x = 1 nor x = 2 when looking for 'x' + # of 'print x'; while in the following :: + # + # x = 1 + # x = 2 + # print x + # + # we can remove x = 1 when we see x = 2 + # + # moreover, on loop assignment types, assignment won't + # necessarily be done if the loop has no iteration, so we don't + # want to clear previous assigments if any (hence the test on + # optional_assign) + if not (optional_assign or are_exclusive(_stmts[pindex], node)): + del _stmt_parents[pindex] + del _stmts[pindex] + if isinstance(node, nodes.AssName): + if not optional_assign and stmt.parent is mystmt.parent: + _stmts = [] + _stmt_parents = [] + elif isinstance(node, nodes.DelName): + _stmts = [] + _stmt_parents = [] + continue + if not are_exclusive(self, node): + _stmts.append(node) + _stmt_parents.append(stmt.parent) + return _stmts + + +def builtin_lookup(name): + """lookup a name into the builtin module + return the list of matching statements and the astng for the builtin + module + """ + builtinastng = MANAGER.astng_from_module(__builtin__) + if name == '__dict__': + return builtinastng, () + try: + stmts = builtinastng.locals[name] + except KeyError: + stmts = () + return builtinastng, stmts + + +class LocalsDictNodeNG(LookupMixIn, NodeNG): + """ this class provides locals handling common to Module, Function + and Class 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 + locals = None + + 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, Function or Class) + """ + return self + + def scope(self): + """return the first node defining a new scope (i.e. Module, + Function, Class, Lambda but also GenExpr) + """ + 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 isinstance(pscope, nodes.Function): + pscope = pscope.root() + return pscope.scope_lookup(node, name) + return builtin_lookup(name) # Module + + + + 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 self.locals is not None, (self, id(self)) + #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.keys()) + + 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 `Function` or `Class` + """ + return [self[key] for key in self.keys()] + + 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 `Function` or `Class` + """ + return zip(self.keys(), self.values()) + + def has_key(self, name): + """method from the `dict` interface returning True if the given + name is defined in the locals dictionary + """ + return self.locals.has_key(name) + + __contains__ = has_key + +# Name classses + +class AssName(LookupMixIn, NodeNG): + """class representing an AssName node""" + + +class DelName(LookupMixIn, NodeNG): + """class representing a DelName node""" + + +class Name(LookupMixIn, NodeNG): + """class representing a Name node""" + + # Module ##################################################################### class Module(LocalsDictNodeNG): |