root / logilab.pylintinstaller / logilab / astng / utils.py

Revision 202:d67e86292521, 6.3 kB (checked in by tziade@…, 9 months ago)

added logilab.pylintinstaller

Line 
1# This program is free software; you can redistribute it and/or modify
2# it under the terms of the GNU General Public License as published by
3# the Free Software Foundation; either version 2 of the License, or
4# (at your option) any later version.
5#
6# This program is distributed in the hope that it will be useful, but WITHOUT
7# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
8# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
9#
10# You should have received a copy of the GNU General Public License along with
11# this program; if not, write to the Free Software Foundation, Inc.,
12# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
13"""this module contains some utilities to navigate in the tree or to
14extract information from it
15
16:author:    Sylvain Thenault
17:copyright: 2003-2007 LOGILAB S.A. (Paris, FRANCE)
18:contact:   http://www.logilab.fr/ -- mailto:python-projects@logilab.org
19:copyright: 2003-2007 Sylvain Thenault
20:contact:   mailto:thenault@gmail.com
21"""
22
23__docformat__ = "restructuredtext en"
24
25from logilab.common.compat import enumerate
26from logilab.astng._exceptions import IgnoreChild
27
28def extend_class(original, addons):
29    """add methods and attribute defined in the addons class to the original
30    class
31    """
32    brain = addons.__dict__.copy()
33    for special_key in ('__doc__', '__module__'):
34        if special_key in addons.__dict__:
35            del brain[special_key]
36    original.__dict__.update(brain)
37       
38class ASTWalker:
39    """a walker visiting a tree in preorder, calling on the handler:
40   
41    * visit_<class name> on entering a node, where class name is the class of
42    the node in lower case
43   
44    * leave_<class name> on leaving a node, where class name is the class of
45    the node in lower case
46    """
47    def __init__(self, handler):
48        self.handler = handler
49        self._cache = {}
50       
51    def walk(self, node):
52        """walk on the tree from <node>, getting callbacks from handler
53        """
54        try:           
55            self.visit(node)
56        except IgnoreChild:
57            pass
58        else:
59            for child_node in node.getChildNodes():
60                self.walk(child_node)
61        self.leave(node)
62
63    def get_callbacks(self, node):
64        """get callbacks from handler for the visited node
65        """
66        klass = node.__class__
67        methods = self._cache.get(klass)
68        if methods is None:
69            handler = self.handler
70            kid = klass.__name__.lower()
71            e_method = getattr(handler, 'visit_%s' % kid,
72                               getattr(handler, 'visit_default', None))
73            l_method = getattr(handler, 'leave_%s' % kid, 
74                               getattr(handler, 'leave_default', None))
75            self._cache[klass] = (e_method, l_method)
76        else:
77            e_method, l_method = methods
78        return e_method, l_method
79   
80    def visit(self, node):
81        """walk on the tree from <node>, getting callbacks from handler"""
82        method = self.get_callbacks(node)[0]
83        if method is not None:
84            method(node)
85           
86    def leave(self, node):
87        """walk on the tree from <node>, getting callbacks from handler"""
88        method = self.get_callbacks(node)[1]
89        if method is not None:
90            method(node)
91
92
93class LocalsVisitor(ASTWalker):
94    """visit a project by traversing the locals dictionnary"""
95    def __init__(self):
96        ASTWalker.__init__(self, self)
97        self._visited = {}
98       
99    def visit(self, node):
100        """launch the visit starting from the given node"""
101        if self._visited.has_key(node):
102            return
103        self._visited[node] = 1
104        methods = self.get_callbacks(node)
105        recurse = 1
106        if methods[0] is not None:
107            try:
108                methods[0](node)
109            except IgnoreChild:
110                recurse = 0
111        if recurse:
112            if hasattr(node, 'locals'):
113                for local_node in node.values():
114                    self.visit(local_node)
115        if methods[1] is not None:
116            return methods[1](node)
117
118def are_exclusive(stmt1, stmt2):
119    """return true if the two given statement are mutually exclusive
120
121    algorithm :
122     1) index stmt1's parents
123     2) climb among stmt2's parents until we find a common parent
124     3) if the common parent is a If or TryExcept statement, look if nodes are
125        in exclusive branchs
126    """
127    from logilab.astng.nodes import If, TryExcept
128    # index stmt1's parents
129    stmt1_parents = {}
130    children = {}
131    node = stmt1.parent
132    previous = stmt1
133    while node:
134        stmt1_parents[node] = 1
135        children[node] = previous
136        previous = node
137        node = node.parent
138    # climb among stmt2's parents until we find a common parent
139    node = stmt2.parent
140    previous = stmt2
141    while node:
142        if stmt1_parents.has_key(node):
143            # if the common parent is a If or TryExcept statement, look if
144            # nodes are in exclusive branchs
145            if isinstance(node, If):
146                if previous != children[node]:
147                    return True
148            elif isinstance(node, TryExcept):
149                stmt1_previous = children[node]
150                if not previous is stmt1_previous:
151                    stmt1_branch, stmt1_num = _try_except_from_branch(node, stmt1_previous)
152                    stmt2_branch, stmt2_num = _try_except_from_branch(node, previous)
153                    if stmt1_branch != stmt1_branch:
154                        if not ((stmt2_branch == 'body' and stmt1_branch == 'else') or
155                                (stmt1_branch == 'body' and stmt2_branch == 'else') or
156                                (stmt2_branch == 'body' and stmt1_branch == 'except') or
157                                (stmt1_branch == 'body' and stmt2_branch == 'except')):
158                            return True
159                    elif stmt1_num != stmt2_num:
160                        return True
161            return False
162        previous = node
163        node = node.parent
164    return False
165
166def _try_except_from_branch(node, stmt):
167    if stmt is node.body:
168        return 'body', 1
169    if stmt is node.else_:
170        return 'else', 1
171    for i, block_nodes in enumerate(node.handlers):
172        if stmt in block_nodes:
173            return 'except', i
Note: See TracBrowser for help on using the browser.