root / logilab.pylintinstaller / logilab / astng / nodes.py

Revision 202:d67e86292521, 27.9 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 it under
2# the terms of the GNU General Public License as published by the Free Software
3# Foundation; either version 2 of the License, or (at your option) any later
4# 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"""
14on all nodes :
15 .is_statement(), returning true if the node should be considered as a
16  statement node
17 .root(), returning the root node of the tree (i.e. a Module)
18 .previous_sibling(), returning previous sibling statement node
19 .next_sibling(), returning next sibling statement node
20 .statement(), returning the first parent node marked as statement node
21 .frame(), returning the first node defining a new local scope (i.e.
22  Module, Function or Class)
23 .set_local(name, node), define an identifier <name> on the first parent frame,
24  with the node defining it. This is used by the astng builder and should not
25  be used from out there.
26 .as_string(), returning a string representation of the code (should be
27  executable).
28
29on From and Import :
30 .real_name(name),
31
32 [1] http://docs.python.org/lib/module-compiler.ast.html
33
34:author:    Sylvain Thenault
35:copyright: 2003-2007 LOGILAB S.A. (Paris, FRANCE)
36:contact:   http://www.logilab.fr/ -- mailto:python-projects@logilab.org
37:copyright: 2003-2007 Sylvain Thenault
38:contact:   mailto:thenault@gmail.com
39"""
40
41from __future__ import generators
42
43__docformat__ = "restructuredtext en"
44
45from compiler.ast import Assign, Add, And, AssAttr, AssList, AssName, \
46     AssTuple, Assert, Assign, AugAssign, \
47     Backquote, Bitand, Bitor, Bitxor, Break, CallFunc, Class, \
48     Compare, Const, Continue, Dict, Discard, Div, FloorDiv, \
49     Ellipsis, EmptyNode, Exec, \
50     For, From, Function, Getattr, Global, \
51     If, Import, Invert, Keyword, Lambda, LeftShift, \
52     List, ListComp, ListCompFor, ListCompIf, Mod, Module, Mul, Name, Node, \
53     Not, Or, Pass, Power, Print, Printnl, Raise, Return, RightShift, Slice, \
54     Sliceobj, Stmt, Sub, Subscript, TryExcept, TryFinally, Tuple, UnaryAdd, \
55     UnarySub, While, Yield
56try:
57    # introduced in python 2.4
58    from compiler.ast import GenExpr, GenExprFor, GenExprIf, GenExprInner
59except:
60    class GenExpr:
61        """dummy GenExpr node, shouldn't be used since py < 2.4"""
62    class GenExprFor: 
63        """dummy GenExprFor node, shouldn't be used since py < 2.4"""
64    class GenExprIf: 
65        """dummy GenExprIf node, shouldn't be used since py < 2.4"""
66    class GenExprInner: 
67        """dummy GenExprInner node, shouldn't be used since py < 2.4"""
68
69try:
70    # introduced in python 2.4
71    from compiler.ast import Decorators
72except:
73    class Decorators:
74        """dummy Decorators node, shouldn't be used since py < 2.4"""
75
76try:
77    # introduced in python 2.5
78    from compiler.ast import With
79except:
80    class With:
81        """dummy With node, shouldn't be used since py < 2.5"""
82
83from logilab.astng._exceptions import NotFoundError, InferenceError
84from logilab.astng.utils import extend_class
85from logilab.astng import InferenceContext
86
87import re
88ID_RGX = re.compile('^[a-zA-Z_][a-zA-Z_0-9]*$')
89del re
90
91INFER_NEED_NAME_STMTS = (From, Import, Global, TryExcept)
92
93# Node  ######################################################################
94
95class NodeNG:
96    """/!\ this class should not be used directly /!\ it's
97    only used as a methods and attribute container, and update the
98    original class from the compiler.ast module using its dictionnary
99    (see below the class definition)
100    """
101   
102    # attributes below are set by the builder module or by raw factories
103    fromlineno = None
104    tolineno = None
105    # parent node in the tree
106    parent = None
107
108    def __str__(self):
109        return '%s(%s)' % (self.__class__.__name__, getattr(self, 'name', ''))
110   
111    def parent_of(self, node):
112        """return true if i'm a parent of the given node"""
113        parent = node.parent
114        while parent is not None:
115            if self is parent:
116                return True
117            parent = parent.parent
118        return False
119
120    def is_statement(self):
121        """return true if the node should be considered as statement node
122        """
123        if isinstance(self.parent, Stmt):
124            return self
125        return None
126
127    def statement(self):
128        """return the first parent node marked as statement node
129        """
130        if self.is_statement():
131            return self
132        return self.parent.statement()
133
134    def frame(self):
135        """return the first parent frame node (i.e. Module, Function or Class)
136        """
137        return self.parent.frame()
138
139    def scope(self):
140        """return the first node defining a new scope (i.e. Module,
141        Function, Class, Lambda but also GenExpr)
142        """
143        return self.parent.scope()
144
145    def root(self):
146        """return the root node of the tree, (i.e. a Module)
147        """
148        if self.parent:
149            return self.parent.root()
150        return self
151
152    def next_sibling(self):
153        """return the previous sibling statement
154        """
155        while not self.is_statement(): 
156            self = self.parent
157        index = self.parent.nodes.index(self)
158        try:
159            return self.parent.nodes[index+1]
160        except IndexError:
161            return
162
163    def previous_sibling(self):
164        """return the next sibling statement
165        """
166        while not self.is_statement(): 
167            self = self.parent
168        index = self.parent.nodes.index(self)
169        if index > 0:
170            return self.parent.nodes[index-1]
171        return
172
173    def nearest(self, nodes):
174        """return the node which is the nearest before this one in the
175        given list of nodes
176        """
177        myroot = self.root()
178        mylineno = self.source_line()
179        nearest = None, 0
180        for node in nodes:
181            assert node.root() is myroot, \
182                   'not from the same module %s' % (self, node)
183            lineno = node.source_line()
184            if node.source_line() > mylineno:
185                break
186            if lineno > nearest[1]:
187                nearest = node, lineno
188        # FIXME: raise an exception if nearest is None ?
189        return nearest[0]
190   
191    def source_line(self):
192        """return the line number where the given node appears
193
194        we need this method since not all nodes as the lineno attribute
195        correctly set...
196        """
197        line = self.lineno
198        if line is None:
199            _node = self
200            try:
201                while line is None:
202                    _node = _node.getChildNodes()[0]
203                    line = _node.lineno
204            except IndexError:
205                _node = self.parent
206                while _node and line is None:
207                    line = _node.lineno
208                    _node = _node.parent
209            self.lineno = line
210        return line
211   
212    def last_source_line(self):
213        """return the last block line number for this node (i.e. including
214        children)
215        """
216        try:
217            return self.__dict__['_cached_last_source_line']
218        except KeyError:
219            line = self.source_line()
220            for node in self.getChildNodes():
221                line = max(line, node.last_source_line())
222            self._cached_last_source_line = line
223            return line
224
225    def block_range(self, lineno):
226        """handle block line numbers range for non block opening statements
227        """
228        return lineno, self.last_source_line()
229
230
231    def set_local(self, name, stmt):
232        """delegate to a scoped parent handling a locals dictionary
233        """
234        self.parent.set_local(name, stmt)
235
236    def nodes_of_class(self, klass, skip_klass=None):
237        """return an iterator on nodes which are instance of the given class(es)
238
239        klass may be a class object or a tuple of class objects
240        """
241        if isinstance(self, klass):
242            yield self
243        for child_node in self.getChildNodes():
244            if skip_klass is not None and isinstance(child_node, skip_klass):
245                continue
246            for matching in child_node.nodes_of_class(klass, skip_klass):
247                yield matching
248
249    def _infer_name(self, frame, name):
250        if isinstance(self, INFER_NEED_NAME_STMTS) or (
251                 isinstance(self, (Function, Lambda)) and self is frame):
252            return name
253        return None
254
255    def eq(self, value):
256        return False
257   
258extend_class(Node, NodeNG)
259
260Const.eq = lambda self, value: self.value == value
261
262def decorators_scope(self):
263    # skip the function node to go directly to the upper level scope
264    return self.parent.parent.scope()
265Decorators.scope = decorators_scope
266
267# block range overrides #######################################################
268
269def object_block_range(node, lineno):
270    """handle block line numbers range for function/class statements:
271
272    start from the "def" or "class" position whatever the given lineno
273    """
274    return node.source_line(), node.last_source_line()
275
276Function.block_range = object_block_range
277Class.block_range = object_block_range
278Module.block_range = object_block_range
279
280def if_block_range(node, lineno):
281    """handle block line numbers range for if/elif statements
282    """
283    last = None
284    for test, testbody in node.tests[1:]:
285        if lineno == testbody.source_line():
286            return lineno, lineno
287        if lineno <= testbody.last_source_line():
288            return lineno, testbody.last_source_line()
289        if last is None:
290            last = testbody.source_line() - 1
291    return elsed_block_range(node, lineno, last)
292
293If.block_range = if_block_range
294
295def try_except_block_range(node, lineno):
296    """handle block line numbers range for try/except statements
297    """
298    last = None
299    for excls, exinst, exbody in node.handlers:
300        if excls and lineno == excls.source_line():
301            return lineno, lineno
302        if exbody.source_line() <= lineno <= exbody.last_source_line():
303            return lineno, exbody.last_source_line()
304        if last is None:
305            last = exbody.source_line() - 1
306    return elsed_block_range(node, lineno, last)
307
308TryExcept.block_range = try_except_block_range
309
310def elsed_block_range(node, lineno, last=None):
311    """handle block line numbers range for try/finally, for and while
312    statements
313    """
314    if lineno == node.source_line():
315        return lineno, lineno
316    if node.else_:
317        if lineno >= node.else_.source_line():
318            return lineno, node.else_.last_source_line()
319        return lineno, node.else_.source_line() - 1
320    return lineno, last or node.last_source_line()
321
322TryFinally.block_range = elsed_block_range
323While.block_range = elsed_block_range
324For.block_range = elsed_block_range
325
326# From and Import #############################################################
327
328def real_name(node, asname):
329    """get name from 'as' name
330    """
331    for index in range(len(node.names)):
332        name, _asname = node.names[index]
333        if name == '*':
334            return asname
335        if not _asname:
336            name = name.split('.', 1)[0]
337            _asname = name
338        if asname == _asname:
339            return name
340    raise NotFoundError(asname)
341   
342From.real_name = real_name
343Import.real_name = real_name
344
345def infer_name_module(node, name):
346    context = InferenceContext(node)
347    context.lookupname = name
348    return node.infer(context, asname=False)
349Import.infer_name_module = infer_name_module
350
351# as_string ###################################################################
352
353def add_as_string(node):
354    """return an ast.Add node as string"""
355    return '(%s) + (%s)' % (node.left.as_string(), node.right.as_string())
356Add.as_string = add_as_string
357
358def and_as_string(node):
359    """return an ast.And node as string"""
360    return ' and '.join(['(%s)' % n.as_string() for n in node.nodes])
361And.as_string = and_as_string
362   
363def assattr_as_string(node):
364    """return an ast.AssAttr node as string"""
365    if node.flags == 'OP_DELETE':
366        return 'del %s.%s' % (node.expr.as_string(), node.attrname)
367    return '%s.%s' % (node.expr.as_string(), node.attrname)
368AssAttr.as_string = assattr_as_string
369
370def asslist_as_string(node):
371    """return an ast.AssList node as string"""
372    string = ', '.join([n.as_string() for n in node.nodes])
373    return '[%s]' % string
374AssList.as_string = asslist_as_string
375
376def assname_as_string(node):
377    """return an ast.AssName node as string"""
378    if node.flags == 'OP_DELETE':
379        return 'del %s' % node.name
380    return node.name
381AssName.as_string = assname_as_string
382   
383def asstuple_as_string(node):
384    """return an ast.AssTuple node as string"""
385    string = ', '.join([n.as_string() for n in node.nodes])
386    # fix for del statement
387    return string.replace(', del ', ', ')
388AssTuple.as_string = asstuple_as_string
389
390def assert_as_string(node):
391    """return an ast.Assert node as string"""
392    if node.fail:
393        return 'assert %s, %s' % (node.test.as_string(), node.fail.as_string())
394    return 'assert %s' % node.test.as_string()
395Assert.as_string = assert_as_string
396
397def assign_as_string(node):
398    """return an ast.Assign node as string"""
399    lhs = ' = '.join([n.as_string() for n in node.nodes])
400    return '%s = %s' % (lhs, node.expr.as_string())
401Assign.as_string = assign_as_string
402
403def augassign_as_string(node):
404    """return an ast.AugAssign node as string"""
405    return '%s %s %s' % (node.node.as_string(), node.op, node.expr.as_string())
406AugAssign.as_string = augassign_as_string
407
408def backquote_as_string(node):
409    """return an ast.Backquote node as string"""
410    return '`%s`' % node.expr.as_string()
411Backquote.as_string = backquote_as_string
412
413def bitand_as_string(node):
414    """return an ast.Bitand node as string"""
415    return ' & '.join(['(%s)' % n.as_string() for n in node.nodes])
416Bitand.as_string = bitand_as_string
417
418def bitor_as_string(node):
419    """return an ast.Bitor node as string"""
420    return ' | '.join(['(%s)' % n.as_string() for n in node.nodes])
421Bitor.as_string = bitor_as_string
422
423def bitxor_as_string(node):
424    """return an ast.Bitxor node as string"""
425    return ' ^ '.join(['(%s)' % n.as_string() for n in node.nodes])
426Bitxor.as_string = bitxor_as_string
427
428def break_as_string(node):
429    """return an ast.Break node as string"""
430    return 'break'
431Break.as_string = break_as_string
432
433def callfunc_as_string(node):
434    """return an ast.CallFunc node as string"""
435    expr_str = node.node.as_string()
436    args = ', '.join([arg.as_string() for arg in node.args])
437    if node.star_args: