summaryrefslogtreecommitdiff
path: root/utils.py
diff options
context:
space:
mode:
authorsylvain thenault <sylvain.thenault@logilab.fr>2009-03-19 12:46:06 +0100
committersylvain thenault <sylvain.thenault@logilab.fr>2009-03-19 12:46:06 +0100
commit0f8763549b85bf7d872ed71797c783f6cc6968be (patch)
treefea57de5f8ee1335c5766801c098231f70e87d30 /utils.py
parent72054c730aab597b7708acffd588a735db500e90 (diff)
parent0b6c831c11dd2d19137743f76742487da3880477 (diff)
downloadastroid-0f8763549b85bf7d872ed71797c783f6cc6968be.tar.gz
(painful) merge
Diffstat (limited to 'utils.py')
-rw-r--r--utils.py326
1 files changed, 254 insertions, 72 deletions
diff --git a/utils.py b/utils.py
index 5d230a0..452ac82 100644
--- a/utils.py
+++ b/utils.py
@@ -14,26 +14,250 @@
extract information from it
:author: Sylvain Thenault
-:copyright: 2003-2007 LOGILAB S.A. (Paris, FRANCE)
+:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE)
:contact: http://www.logilab.fr/ -- mailto:python-projects@logilab.org
-:copyright: 2003-2007 Sylvain Thenault
+:copyright: 2003-2009 Sylvain Thenault
:contact: mailto:thenault@gmail.com
"""
__docformat__ = "restructuredtext en"
-from logilab.common.compat import enumerate
-from logilab.astng import Instance, IgnoreChild
+from logilab.astng._exceptions import IgnoreChild
def extend_class(original, addons):
"""add methods and attribute defined in the addons class to the original
class
"""
brain = addons.__dict__.copy()
- for special_key in ('__doc__', '__module__'):
+ for special_key in ('__doc__', '__module__', '__dict__'):
if special_key in addons.__dict__:
del brain[special_key]
- original.__dict__.update(brain)
+ try:
+ original.__dict__.update(brain)
+ except AttributeError:
+ # dictproxy object
+ for k, v in brain.iteritems():
+ setattr(original, k, v)
+
+
+class ASTVisitor(object):
+ """Abstract Base Class for Python AST Visitors.
+
+ Visitors inheritating from ASTVisitors could visit
+ compiler.ast, _ast or astng trees.
+
+ Not all methods will have to be implemented;
+ so some methods are just empty interfaces for catching
+ cases where we don't want to do anything on the
+ concerned node.
+ """
+
+ def visit_arguments(self, node):
+ """dummy method for visiting an Arguments node"""
+
+ def visit_assattr(self, node):
+ """dummy method for visiting an AssAttr node"""
+
+ def visit_assert(self, node):
+ """dummy method for visiting an Assert node"""
+
+ def visit_assign(self, node):
+ """dummy method for visiting an Assign node"""
+
+ def visit_assname(self, node):
+ """dummy method for visiting an AssName node"""
+
+ def visit_augassign(self, node):
+ """dummy method for visiting an AugAssign node"""
+
+ def visit_backquote(self, node):
+ """dummy method for visiting an Backquote node"""
+
+ def visit_binop(self, node):
+ """dummy method for visiting an BinOp node"""
+
+ def visit_boolop(self, node):
+ """dummy method for visiting an BoolOp node"""
+
+ def visit_break(self, node):
+ """dummy method for visiting an Break node"""
+
+ def visit_callfunc(self, node):
+ """dummy method for visiting an CallFunc node"""
+
+ def visit_class(self, node):
+ """dummy method for visiting an Class node"""
+
+ def visit_compare(self, node):
+ """dummy method for visiting an Compare node"""
+
+ def visit_comprehension(self, node):
+ """dummy method for visiting an Comprehension node"""
+
+ def visit_const(self, node):
+ """dummy method for visiting an Const node"""
+
+ def visit_continue(self, node):
+ """dummy method for visiting an Continue node"""
+
+ def visit_decorators(self, node):
+ """dummy method for visiting an Decorators node"""
+
+ def visit_delattr(self, node):
+ """dummy method for visiting an DelAttr node"""
+
+ def visit_delete(self, node):
+ """dummy method for visiting an Delete node"""
+
+ def visit_delname(self, node):
+ """dummy method for visiting an DelName node"""
+
+ def visit_dict(self, node):
+ """dummy method for visiting an Dict node"""
+
+ def visit_discard(self, node):
+ """dummy method for visiting an Discard node"""
+
+ def visit_emptynode(self, node):
+ """dummy method for visiting an EmptyNode node"""
+
+ def visit_excepthandler(self, node):
+ """dummy method for visiting an ExceptHandler node"""
+
+ def visit_ellipsis(self, node):
+ """dummy method for visiting an Ellipsis node"""
+
+ def visit_empty(self, node):
+ """dummy method for visiting an Empty node"""
+
+ def visit_exec(self, node):
+ """dummy method for visiting an Exec node"""
+
+ def visit_extslice(self, node):
+ """dummy method for visiting an ExtSlice node"""
+
+ def visit_for(self, node):
+ """dummy method for visiting an For node"""
+
+ def visit_from(self, node):
+ """dummy method for visiting an From node"""
+
+ def visit_function(self, node):
+ """dummy method for visiting an Function node"""
+
+ def visit_genexpr(self, node):
+ """dummy method for visiting an ListComp node"""
+
+ def visit_getattr(self, node):
+ """dummy method for visiting an Getattr node"""
+
+ def visit_global(self, node):
+ """dummy method for visiting an Global node"""
+
+ def visit_if(self, node):
+ """dummy method for visiting an If node"""
+
+ def visit_ifexp(self, node):
+ """dummy method for visiting an IfExp node"""
+
+ def visit_import(self, node):
+ """dummy method for visiting an Import node"""
+
+ def visit_index(self, node):
+ """dummy method for visiting an Index node"""
+
+ def visit_keyword(self, node):
+ """dummy method for visiting an Keyword node"""
+
+ def visit_lambda(self, node):
+ """dummy method for visiting an Lambda node"""
+
+ def visit_list(self, node):
+ """dummy method for visiting an List node"""
+
+ def visit_listcomp(self, node):
+ """dummy method for visiting an ListComp node"""
+
+ def visit_module(self, node):
+ """dummy method for visiting an Module node"""
+
+ def visit_name(self, node):
+ """dummy method for visiting an Name node"""
+
+ def visit_pass(self, node):
+ """dummy method for visiting an Pass node"""
+
+ def visit_print(self, node):
+ """dummy method for visiting an Print node"""
+
+ def visit_raise(self, node):
+ """dummy method for visiting an Raise node"""
+
+ def visit_return(self, node):
+ """dummy method for visiting an Return node"""
+
+ def visit_slice(self, node):
+ """dummy method for visiting an Slice node"""
+
+ def visit_subscript(self, node):
+ """dummy method for visiting an Subscript node"""
+
+ def visit_tryexcept(self, node):
+ """dummy method for visiting an TryExcept node"""
+
+ def visit_tryfinally(self, node):
+ """dummy method for visiting an TryFinally node"""
+
+ def visit_tuple(self, node):
+ """dummy method for visiting an Tuple node"""
+
+ def visit_unaryop(self, node):
+ """dummy method for visiting an UnaryOp node"""
+
+ def visit_while(self, node):
+ """dummy method for visiting an While node"""
+
+ def visit_with(self, node):
+ """dummy method for visiting an With node"""
+
+ def visit_yield(self, node):
+ """dummy method for visiting an Yield node"""
+
+
+REDIRECT = {'Attribute': 'Getattr',
+ 'Call': 'CallFunc',
+ 'ClassDef': 'Class',
+ "ListCompFor": 'Comprehension',
+ "GenExprFor": 'Comprehension',
+ 'excepthandler': 'ExceptHandler',
+ 'Expr': 'Discard',
+ 'FunctionDef': 'Function',
+ 'GeneratorExp': 'GenExpr',
+ 'ImportFrom': 'From',
+ 'keyword': 'Keyword',
+ 'Repr': 'Backquote',
+
+ 'Add': 'BinOp',
+ 'Bitand': 'BinOp',
+ 'Bitor': 'BinOp',
+ 'Bitxor': 'BinOp',
+ 'Div': 'BinOp',
+ 'FloorDiv': 'BinOp',
+ 'LeftShift': 'BinOp',
+ 'Mod': 'BinOp',
+ 'Mul': 'BinOp',
+ 'Power': 'BinOp',
+ 'RightShift': 'BinOp',
+ 'Sub': 'BinOp',
+
+ 'And': 'BoolOp',
+ 'Or': 'BoolOp',
+
+ 'UnaryAdd': 'UnaryOp',
+ 'UnarySub': 'UnaryOp',
+ 'Not': 'UnaryOp',
+ 'Invert': 'UnaryOp'
+ }
class ASTWalker:
"""a walker visiting a tree in preorder, calling on the handler:
@@ -44,31 +268,42 @@ class ASTWalker:
* leave_<class name> on leaving a node, where class name is the class of
the node in lower case
"""
+ REDIRECTION = REDIRECT
+
def __init__(self, handler):
self.handler = handler
self._cache = {}
-
- def walk(self, node):
- """walk on the tree from <node>, getting callbacks from handler
- """
+
+ def walk(self, node, _done=None):
+ """walk on the tree from <node>, getting callbacks from handler"""
+ if _done is None:
+ _done = set()
+ if node in _done:
+ raise AssertionError((id(node), node, node.parent))
+ _done.add(node)
try:
self.visit(node)
except IgnoreChild:
pass
else:
- for child_node in node.getChildNodes():
- self.walk(child_node)
+ try:
+ for child_node in node.get_children():
+ self.handler.set_context(node, child_node)
+ assert child_node is not node
+ self.walk(child_node, _done)
+ except AttributeError:
+ print node.__class__, id(node.__class__)
+ raise
self.leave(node)
+ assert node.parent is not node
def get_callbacks(self, node):
- """get callbacks from handler for the visited node
- """
+ """get callbacks from handler for the visited node"""
klass = node.__class__
methods = self._cache.get(klass)
if methods is None:
handler = self.handler
- kid = klass.__name__.lower()
- # enter, leave
+ kid = self.REDIRECTION.get(klass.__name__, klass.__name__).lower()
e_method = getattr(handler, 'visit_%s' % kid,
getattr(handler, 'visit_default', None))
l_method = getattr(handler, 'leave_%s' % kid,
@@ -110,65 +345,12 @@ class LocalsVisitor(ASTWalker):
except IgnoreChild:
recurse = 0
if recurse:
- if hasattr(node, 'locals') and not isinstance(node, Instance):
+ if 'locals' in node.__dict__: # skip Instance and other proxy
for name, local_node in node.items():
self.visit(local_node)
if methods[1] is not None:
return methods[1](node)
-def are_exclusive(stmt1, stmt2):
- """return true if the two given statement are mutually exclusive
+__all__ = ('REDIRECT', 'LocalsVisitor', 'ASTWalker', 'ASTVisitor',
+ 'extend_class')
- 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 branchs
- """
- from logilab.astng.nodes import If, TryExcept
- # 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 branchs
- if isinstance(node, If):
- if previous != children[node]:
- return True
- elif isinstance(node, TryExcept):
- stmt1_previous = children[node]
- if not previous is stmt1_previous:
- stmt1_branch, stmt1_num = _try_except_from_branch(node, stmt1_previous)
- stmt2_branch, stmt2_num = _try_except_from_branch(node, previous)
- if stmt1_branch != stmt1_branch:
- if not ((stmt2_branch == 'body' and stmt1_branch == 'else') or
- (stmt1_branch == 'body' and stmt2_branch == 'else') or
- (stmt2_branch == 'body' and stmt1_branch == 'except') or
- (stmt1_branch == 'body' and stmt2_branch == 'except')):
- return True
- elif stmt1_num != stmt2_num:
- return True
- return False
- previous = node
- node = node.parent
- return False
-
-def _try_except_from_branch(node, stmt):
- if stmt is node.body:
- return 'body', 1
- if stmt is node.else_:
- return 'else', 1
- for i, block_nodes in enumerate(node.handlers):
- if stmt in block_nodes:
- return 'except', i