OLD | NEW |
1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. | 1 # -*- coding: utf-8 -*- |
| 2 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr | 3 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
| 4 # copyright 2003-2010 Sylvain Thenault, all rights reserved. |
| 5 # contact mailto:thenault@gmail.com |
3 # | 6 # |
4 # This file is part of astroid. | 7 # This file is part of logilab-astng. |
5 # | 8 # |
6 # astroid is free software: you can redistribute it and/or modify it | 9 # logilab-astng is free software: you can redistribute it and/or modify it |
7 # under the terms of the GNU Lesser General Public License as published by the | 10 # under the terms of the GNU Lesser General Public License as published by the |
8 # Free Software Foundation, either version 2.1 of the License, or (at your | 11 # Free Software Foundation, either version 2.1 of the License, or (at your |
9 # option) any later version. | 12 # option) any later version. |
10 # | 13 # |
11 # astroid is distributed in the hope that it will be useful, but | 14 # logilab-astng is distributed in the hope that it will be useful, but |
12 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 15 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License | 16 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License |
14 # for more details. | 17 # for more details. |
15 # | 18 # |
16 # You should have received a copy of the GNU Lesser General Public License along | 19 # You should have received a copy of the GNU Lesser General Public License along |
17 # with astroid. If not, see <http://www.gnu.org/licenses/>. | 20 # with logilab-astng. If not, see <http://www.gnu.org/licenses/>. |
18 """This module contains base classes and functions for the nodes and some | 21 """This module contains base classes and functions for the nodes and some |
19 inference utils. | 22 inference utils. |
20 """ | 23 """ |
21 | 24 |
22 __docformat__ = "restructuredtext en" | 25 __docformat__ = "restructuredtext en" |
23 | 26 |
24 import sys | |
25 from contextlib import contextmanager | 27 from contextlib import contextmanager |
26 | 28 |
27 from astroid.exceptions import (InferenceError, AstroidError, NotFoundError, | 29 from logilab.common.compat import builtins |
28 UnresolvableName, UseInferenceDefault) | |
29 | 30 |
| 31 from logilab.astng import BUILTINS_MODULE |
| 32 from logilab.astng.exceptions import InferenceError, ASTNGError, \ |
| 33 NotFoundError, UnresolvableName |
| 34 from logilab.astng.as_string import as_string |
30 | 35 |
31 if sys.version_info >= (3, 0): | 36 BUILTINS_NAME = builtins.__name__ |
32 BUILTINS = 'builtins' | |
33 else: | |
34 BUILTINS = '__builtin__' | |
35 | |
36 | 37 |
37 class Proxy(object): | 38 class Proxy(object): |
38 """a simple proxy object""" | 39 """a simple proxy object""" |
39 | 40 _proxied = None |
40 _proxied = None # proxied object may be set by class or by instance | |
41 | 41 |
42 def __init__(self, proxied=None): | 42 def __init__(self, proxied=None): |
43 if proxied is not None: | 43 if proxied is not None: |
44 self._proxied = proxied | 44 self._proxied = proxied |
45 | 45 |
46 def __getattr__(self, name): | 46 def __getattr__(self, name): |
47 if name == '_proxied': | 47 if name == '_proxied': |
48 return getattr(self.__class__, '_proxied') | 48 return getattr(self.__class__, '_proxied') |
49 if name in self.__dict__: | 49 if name in self.__dict__: |
50 return self.__dict__[name] | 50 return self.__dict__[name] |
(...skipping 14 matching lines...) Expand all Loading... |
65 else: | 65 else: |
66 self.path = path | 66 self.path = path |
67 self.lookupname = None | 67 self.lookupname = None |
68 self.callcontext = None | 68 self.callcontext = None |
69 self.boundnode = None | 69 self.boundnode = None |
70 | 70 |
71 def push(self, node): | 71 def push(self, node): |
72 name = self.lookupname | 72 name = self.lookupname |
73 if (node, name) in self.path: | 73 if (node, name) in self.path: |
74 raise StopIteration() | 74 raise StopIteration() |
75 self.path.add((node, name)) | 75 self.path.add( (node, name) ) |
76 | 76 |
77 def clone(self): | 77 def clone(self): |
78 # XXX copy lookupname/callcontext ? | 78 # XXX copy lookupname/callcontext ? |
79 clone = InferenceContext(self.path) | 79 clone = InferenceContext(self.path) |
80 clone.callcontext = self.callcontext | 80 clone.callcontext = self.callcontext |
81 clone.boundnode = self.boundnode | 81 clone.boundnode = self.boundnode |
82 return clone | 82 return clone |
83 | 83 |
84 @contextmanager | 84 @contextmanager |
85 def restore_path(self): | 85 def restore_path(self): |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 raise InferenceError(str(stmt)) | 124 raise InferenceError(str(stmt)) |
125 | 125 |
126 | 126 |
127 # special inference objects (e.g. may be returned as nodes by .infer()) ####### | 127 # special inference objects (e.g. may be returned as nodes by .infer()) ####### |
128 | 128 |
129 class _Yes(object): | 129 class _Yes(object): |
130 """a yes object""" | 130 """a yes object""" |
131 def __repr__(self): | 131 def __repr__(self): |
132 return 'YES' | 132 return 'YES' |
133 def __getattribute__(self, name): | 133 def __getattribute__(self, name): |
134 if name == 'next': | |
135 raise AttributeError('next method should not be called') | |
136 if name.startswith('__') and name.endswith('__'): | 134 if name.startswith('__') and name.endswith('__'): |
137 # to avoid inspection pb | 135 # to avoid inspection pb |
138 return super(_Yes, self).__getattribute__(name) | 136 return super(_Yes, self).__getattribute__(name) |
139 return self | 137 return self |
140 def __call__(self, *args, **kwargs): | 138 def __call__(self, *args, **kwargs): |
141 return self | 139 return self |
142 | 140 |
143 | 141 |
144 YES = _Yes() | 142 YES = _Yes() |
145 | 143 |
(...skipping 18 matching lines...) Expand all Loading... |
164 if lookupclass: | 162 if lookupclass: |
165 try: | 163 try: |
166 return values + self._proxied.getattr(name, context) | 164 return values + self._proxied.getattr(name, context) |
167 except NotFoundError: | 165 except NotFoundError: |
168 pass | 166 pass |
169 return values | 167 return values |
170 | 168 |
171 def igetattr(self, name, context=None): | 169 def igetattr(self, name, context=None): |
172 """inferred getattr""" | 170 """inferred getattr""" |
173 try: | 171 try: |
174 # avoid recursively inferring the same attr on the same class | |
175 if context: | |
176 context.push((self._proxied, name)) | |
177 # XXX frame should be self._proxied, or not ? | 172 # XXX frame should be self._proxied, or not ? |
178 get_attr = self.getattr(name, context, lookupclass=False) | 173 get_attr = self.getattr(name, context, lookupclass=False) |
179 return _infer_stmts(self._wrap_attr(get_attr, context), context, | 174 return _infer_stmts(self._wrap_attr(get_attr, context), context, |
180 frame=self) | 175 frame=self) |
181 except NotFoundError: | 176 except NotFoundError: |
182 try: | 177 try: |
183 # fallback to class'igetattr since it has some logic to handle | 178 # fallback to class'igetattr since it has some logic to handle |
184 # descriptors | 179 # descriptors |
185 return self._wrap_attr(self._proxied.igetattr(name, context), | 180 return self._wrap_attr(self._proxied.igetattr(name, context), |
186 context) | 181 context) |
187 except NotFoundError: | 182 except NotFoundError: |
188 raise InferenceError(name) | 183 raise InferenceError(name) |
189 | 184 |
190 def _wrap_attr(self, attrs, context=None): | 185 def _wrap_attr(self, attrs, context=None): |
191 """wrap bound methods of attrs in a InstanceMethod proxies""" | 186 """wrap bound methods of attrs in a InstanceMethod proxies""" |
192 for attr in attrs: | 187 for attr in attrs: |
193 if isinstance(attr, UnboundMethod): | 188 if isinstance(attr, UnboundMethod): |
194 if BUILTINS + '.property' in attr.decoratornames(): | 189 if BUILTINS_NAME + '.property' in attr.decoratornames(): |
195 for infered in attr.infer_call_result(self, context): | 190 for infered in attr.infer_call_result(self, context): |
196 yield infered | 191 yield infered |
197 else: | 192 else: |
198 yield BoundMethod(attr, self) | 193 yield BoundMethod(attr, self) |
199 else: | 194 else: |
200 yield attr | 195 yield attr |
201 | 196 |
202 def infer_call_result(self, caller, context=None): | 197 def infer_call_result(self, caller, context=None): |
203 """infer what a class instance is returning when called""" | 198 """infer what a class instance is returning when called""" |
204 infered = False | 199 infered = False |
205 for node in self._proxied.igetattr('__call__', context): | 200 for node in self._proxied.igetattr('__call__', context): |
206 if node is YES: | |
207 continue | |
208 for res in node.infer_call_result(caller, context): | 201 for res in node.infer_call_result(caller, context): |
209 infered = True | 202 infered = True |
210 yield res | 203 yield res |
211 if not infered: | 204 if not infered: |
212 raise InferenceError() | 205 raise InferenceError() |
213 | 206 |
214 def __repr__(self): | 207 def __repr__(self): |
215 return '<Instance of %s.%s at 0x%s>' % (self._proxied.root().name, | 208 return '<Instance of %s.%s at 0x%s>' % (self._proxied.root().name, |
216 self._proxied.name, | 209 self._proxied.name, |
217 id(self)) | 210 id(self)) |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
251 | 244 |
252 def igetattr(self, name, context=None): | 245 def igetattr(self, name, context=None): |
253 if name == 'im_func': | 246 if name == 'im_func': |
254 return iter((self._proxied,)) | 247 return iter((self._proxied,)) |
255 return super(UnboundMethod, self).igetattr(name, context) | 248 return super(UnboundMethod, self).igetattr(name, context) |
256 | 249 |
257 def infer_call_result(self, caller, context): | 250 def infer_call_result(self, caller, context): |
258 # If we're unbound method __new__ of builtin object, the result is an | 251 # If we're unbound method __new__ of builtin object, the result is an |
259 # instance of the class given as first argument. | 252 # instance of the class given as first argument. |
260 if (self._proxied.name == '__new__' and | 253 if (self._proxied.name == '__new__' and |
261 self._proxied.parent.frame().qname() == '%s.object' % BUILTINS): | 254 self._proxied.parent.frame().qname() == '%s.object' % BUILTINS_M
ODULE): |
262 infer = caller.args[0].infer() if caller.args else [] | 255 return (x is YES and x or Instance(x) for x in caller.args[0].infer(
)) |
263 return ((x is YES and x or Instance(x)) for x in infer) | |
264 return self._proxied.infer_call_result(caller, context) | 256 return self._proxied.infer_call_result(caller, context) |
265 | 257 |
266 | 258 |
267 class BoundMethod(UnboundMethod): | 259 class BoundMethod(UnboundMethod): |
268 """a special node representing a method bound to an instance""" | 260 """a special node representing a method bound to an instance""" |
269 def __init__(self, proxy, bound): | 261 def __init__(self, proxy, bound): |
270 UnboundMethod.__init__(self, proxy) | 262 UnboundMethod.__init__(self, proxy) |
271 self.bound = bound | 263 self.bound = bound |
272 | 264 |
273 def is_bound(self): | 265 def is_bound(self): |
274 return True | 266 return True |
275 | 267 |
276 def infer_call_result(self, caller, context): | 268 def infer_call_result(self, caller, context): |
277 context = context.clone() | 269 context = context.clone() |
278 context.boundnode = self.bound | 270 context.boundnode = self.bound |
279 return self._proxied.infer_call_result(caller, context) | 271 return self._proxied.infer_call_result(caller, context) |
280 | 272 |
281 | 273 |
282 class Generator(Instance): | 274 class Generator(Instance): |
283 """a special node representing a generator. | 275 """a special node representing a generator""" |
284 | |
285 Proxied class is set once for all in raw_building. | |
286 """ | |
287 def callable(self): | 276 def callable(self): |
288 return False | 277 return True |
289 | 278 |
290 def pytype(self): | 279 def pytype(self): |
291 return '%s.generator' % BUILTINS | 280 return '%s.generator' % BUILTINS_MODULE |
292 | 281 |
293 def display_type(self): | 282 def display_type(self): |
294 return 'Generator' | 283 return 'Generator' |
295 | 284 |
296 def __repr__(self): | 285 def __repr__(self): |
297 return '<Generator(%s) l.%s at 0x%s>' % (self._proxied.name, self.lineno
, id(self)) | 286 return '<Generator(%s) l.%s at 0x%s>' % (self._proxied.name, self.lineno
, id(self)) |
298 | 287 |
299 def __str__(self): | 288 def __str__(self): |
300 return 'Generator(%s)' % (self._proxied.name) | 289 return 'Generator(%s)' % (self._proxied.name) |
301 | 290 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
338 infered = True | 327 infered = True |
339 yield node | 328 yield node |
340 if not infered: | 329 if not infered: |
341 raise InferenceError() | 330 raise InferenceError() |
342 return wrapper | 331 return wrapper |
343 | 332 |
344 | 333 |
345 # Node ###################################################################### | 334 # Node ###################################################################### |
346 | 335 |
347 class NodeNG(object): | 336 class NodeNG(object): |
348 """Base Class for all Astroid node classes. | 337 """Base Class for all ASTNG node classes. |
349 | 338 |
350 It represents a node of the new abstract syntax tree. | 339 It represents a node of the new abstract syntax tree. |
351 """ | 340 """ |
352 is_statement = False | 341 is_statement = False |
353 optional_assign = False # True for For (and for Comprehension if py <3.0) | 342 optional_assign = False # True for For (and for Comprehension if py <3.0) |
354 is_function = False # True for Function nodes | 343 is_function = False # True for Function nodes |
355 # attributes below are set by the builder module or by raw factories | 344 # attributes below are set by the builder module or by raw factories |
356 lineno = None | 345 lineno = None |
357 fromlineno = None | 346 fromlineno = None |
358 tolineno = None | 347 tolineno = None |
359 col_offset = None | 348 col_offset = None |
360 # parent node in the tree | 349 # parent node in the tree |
361 parent = None | 350 parent = None |
362 # attributes containing child node(s) redefined in most concrete classes: | 351 # attributes containing child node(s) redefined in most concrete classes: |
363 _astroid_fields = () | 352 _astng_fields = () |
364 # instance specific inference function infer(node, context) | |
365 _explicit_inference = None | |
366 | |
367 def infer(self, context=None, **kwargs): | |
368 """main interface to the interface system, return a generator on infered | |
369 values. | |
370 | |
371 If the instance has some explicit inference function set, it will be | |
372 called instead of the default interface. | |
373 """ | |
374 if self._explicit_inference is not None: | |
375 # explicit_inference is not bound, give it self explicitly | |
376 try: | |
377 return self._explicit_inference(self, context, **kwargs) | |
378 except UseInferenceDefault: | |
379 pass | |
380 return self._infer(context, **kwargs) | |
381 | 353 |
382 def _repr_name(self): | 354 def _repr_name(self): |
383 """return self.name or self.attrname or '' for nice representation""" | 355 """return self.name or self.attrname or '' for nice representation""" |
384 return getattr(self, 'name', getattr(self, 'attrname', '')) | 356 return getattr(self, 'name', getattr(self, 'attrname', '')) |
385 | 357 |
386 def __str__(self): | 358 def __str__(self): |
387 return '%s(%s)' % (self.__class__.__name__, self._repr_name()) | 359 return '%s(%s)' % (self.__class__.__name__, self._repr_name()) |
388 | 360 |
389 def __repr__(self): | 361 def __repr__(self): |
390 return '<%s(%s) l.%s [%s] at 0x%x>' % (self.__class__.__name__, | 362 return '<%s(%s) l.%s [%s] at Ox%x>' % (self.__class__.__name__, |
391 self._repr_name(), | 363 self._repr_name(), |
392 self.fromlineno, | 364 self.fromlineno, |
393 self.root().name, | 365 self.root().name, |
394 id(self)) | 366 id(self)) |
395 | 367 |
396 | 368 |
397 def accept(self, visitor): | 369 def accept(self, visitor): |
| 370 klass = self.__class__.__name__ |
398 func = getattr(visitor, "visit_" + self.__class__.__name__.lower()) | 371 func = getattr(visitor, "visit_" + self.__class__.__name__.lower()) |
399 return func(self) | 372 return func(self) |
400 | 373 |
401 def get_children(self): | 374 def get_children(self): |
402 for field in self._astroid_fields: | 375 for field in self._astng_fields: |
403 attr = getattr(self, field) | 376 attr = getattr(self, field) |
404 if attr is None: | 377 if attr is None: |
405 continue | 378 continue |
406 if isinstance(attr, (list, tuple)): | 379 if isinstance(attr, (list, tuple)): |
407 for elt in attr: | 380 for elt in attr: |
408 yield elt | 381 yield elt |
409 else: | 382 else: |
410 yield attr | 383 yield attr |
411 | 384 |
412 def last_child(self): | 385 def last_child(self): |
413 """an optimized version of list(get_children())[-1]""" | 386 """an optimized version of list(get_children())[-1]""" |
414 for field in self._astroid_fields[::-1]: | 387 for field in self._astng_fields[::-1]: |
415 attr = getattr(self, field) | 388 attr = getattr(self, field) |
416 if not attr: # None or empty listy / tuple | 389 if not attr: # None or empty listy / tuple |
417 continue | 390 continue |
418 if isinstance(attr, (list, tuple)): | 391 if isinstance(attr, (list, tuple)): |
419 return attr[-1] | 392 return attr[-1] |
420 else: | 393 else: |
421 return attr | 394 return attr |
422 return None | 395 return None |
423 | 396 |
424 def parent_of(self, node): | 397 def parent_of(self, node): |
(...skipping 23 matching lines...) Expand all Loading... |
448 return self.parent.scope() | 421 return self.parent.scope() |
449 | 422 |
450 def root(self): | 423 def root(self): |
451 """return the root node of the tree, (i.e. a Module)""" | 424 """return the root node of the tree, (i.e. a Module)""" |
452 if self.parent: | 425 if self.parent: |
453 return self.parent.root() | 426 return self.parent.root() |
454 return self | 427 return self |
455 | 428 |
456 def child_sequence(self, child): | 429 def child_sequence(self, child): |
457 """search for the right sequence where the child lies in""" | 430 """search for the right sequence where the child lies in""" |
458 for field in self._astroid_fields: | 431 for field in self._astng_fields: |
459 node_or_sequence = getattr(self, field) | 432 node_or_sequence = getattr(self, field) |
460 if node_or_sequence is child: | 433 if node_or_sequence is child: |
461 return [node_or_sequence] | 434 return [node_or_sequence] |
462 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes | 435 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes |
463 if isinstance(node_or_sequence, (tuple, list)) and child in node_or_
sequence: | 436 if isinstance(node_or_sequence, (tuple, list)) and child in node_or_
sequence: |
464 return node_or_sequence | 437 return node_or_sequence |
465 else: | 438 else: |
466 msg = 'Could not find %s in %s\'s children' | 439 msg = 'Could not found %s in %s\'s children' |
467 raise AstroidError(msg % (repr(child), repr(self))) | 440 raise ASTNGError(msg % (repr(child), repr(self))) |
468 | 441 |
469 def locate_child(self, child): | 442 def locate_child(self, child): |
470 """return a 2-uple (child attribute name, sequence or node)""" | 443 """return a 2-uple (child attribute name, sequence or node)""" |
471 for field in self._astroid_fields: | 444 for field in self._astng_fields: |
472 node_or_sequence = getattr(self, field) | 445 node_or_sequence = getattr(self, field) |
473 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes | 446 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes |
474 if child is node_or_sequence: | 447 if child is node_or_sequence: |
475 return field, child | 448 return field, child |
476 if isinstance(node_or_sequence, (tuple, list)) and child in node_or_
sequence: | 449 if isinstance(node_or_sequence, (tuple, list)) and child in node_or_
sequence: |
477 return field, node_or_sequence | 450 return field, node_or_sequence |
478 msg = 'Could not find %s in %s\'s children' | 451 msg = 'Could not found %s in %s\'s children' |
479 raise AstroidError(msg % (repr(child), repr(self))) | 452 raise ASTNGError(msg % (repr(child), repr(self))) |
480 # FIXME : should we merge child_sequence and locate_child ? locate_child | 453 # FIXME : should we merge child_sequence and locate_child ? locate_child |
481 # is only used in are_exclusive, child_sequence one time in pylint. | 454 # is only used in are_exclusive, child_sequence one time in pylint. |
482 | 455 |
483 def next_sibling(self): | 456 def next_sibling(self): |
484 """return the next sibling statement""" | 457 """return the next sibling statement""" |
485 return self.parent.next_sibling() | 458 return self.parent.next_sibling() |
486 | 459 |
487 def previous_sibling(self): | 460 def previous_sibling(self): |
488 """return the previous sibling statement""" | 461 """return the previous sibling statement""" |
489 return self.parent.previous_sibling() | 462 return self.parent.previous_sibling() |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
558 for child_node in self.get_children(): | 531 for child_node in self.get_children(): |
559 if skip_klass is not None and isinstance(child_node, skip_klass): | 532 if skip_klass is not None and isinstance(child_node, skip_klass): |
560 continue | 533 continue |
561 for matching in child_node.nodes_of_class(klass, skip_klass): | 534 for matching in child_node.nodes_of_class(klass, skip_klass): |
562 yield matching | 535 yield matching |
563 | 536 |
564 def _infer_name(self, frame, name): | 537 def _infer_name(self, frame, name): |
565 # overridden for From, Import, Global, TryExcept and Arguments | 538 # overridden for From, Import, Global, TryExcept and Arguments |
566 return None | 539 return None |
567 | 540 |
568 def _infer(self, context=None): | 541 def infer(self, context=None): |
569 """we don't know how to resolve a statement by default""" | 542 """we don't know how to resolve a statement by default""" |
570 # this method is overridden by most concrete classes | 543 # this method is overridden by most concrete classes |
571 raise InferenceError(self.__class__.__name__) | 544 raise InferenceError(self.__class__.__name__) |
572 | 545 |
573 def infered(self): | 546 def infered(self): |
574 '''return list of infered values for a more simple inference usage''' | 547 '''return list of infered values for a more simple inference usage''' |
575 return list(self.infer()) | 548 return list(self.infer()) |
576 | 549 |
577 def instanciate_class(self): | 550 def instanciate_class(self): |
578 """instanciate a node if it is a Class node, else return self""" | 551 """instanciate a node if it is a Class node, else return self""" |
579 return self | 552 return self |
580 | 553 |
581 def has_base(self, node): | 554 def has_base(self, node): |
582 return False | 555 return False |
583 | 556 |
584 def callable(self): | 557 def callable(self): |
585 return False | 558 return False |
586 | 559 |
587 def eq(self, value): | 560 def eq(self, value): |
588 return False | 561 return False |
589 | 562 |
590 def as_string(self): | 563 def as_string(self): |
591 from astroid.as_string import to_code | 564 return as_string(self) |
592 return to_code(self) | |
593 | 565 |
594 def repr_tree(self, ids=False): | 566 def repr_tree(self, ids=False): |
595 from astroid.as_string import dump | 567 """print a nice astng tree representation. |
596 return dump(self) | 568 |
| 569 :param ids: if true, we also print the ids (usefull for debugging)""" |
| 570 result = [] |
| 571 _repr_tree(self, result, ids=ids) |
| 572 return "\n".join(result) |
597 | 573 |
598 | 574 |
599 class Statement(NodeNG): | 575 class Statement(NodeNG): |
600 """Statement node adding a few attributes""" | 576 """Statement node adding a few attributes""" |
601 is_statement = True | 577 is_statement = True |
602 | 578 |
603 def next_sibling(self): | 579 def next_sibling(self): |
604 """return the next sibling statement""" | 580 """return the next sibling statement""" |
605 stmts = self.parent.child_sequence(self) | 581 stmts = self.parent.child_sequence(self) |
606 index = stmts.index(self) | 582 index = stmts.index(self) |
607 try: | 583 try: |
608 return stmts[index +1] | 584 return stmts[index +1] |
609 except IndexError: | 585 except IndexError: |
610 pass | 586 pass |
611 | 587 |
612 def previous_sibling(self): | 588 def previous_sibling(self): |
613 """return the previous sibling statement""" | 589 """return the previous sibling statement""" |
614 stmts = self.parent.child_sequence(self) | 590 stmts = self.parent.child_sequence(self) |
615 index = stmts.index(self) | 591 index = stmts.index(self) |
616 if index >= 1: | 592 if index >= 1: |
617 return stmts[index -1] | 593 return stmts[index -1] |
| 594 |
| 595 INDENT = " " |
| 596 |
| 597 def _repr_tree(node, result, indent='', _done=None, ids=False): |
| 598 """built a tree representation of a node as a list of lines""" |
| 599 if _done is None: |
| 600 _done = set() |
| 601 if not hasattr(node, '_astng_fields'): # not a astng node |
| 602 return |
| 603 if node in _done: |
| 604 result.append( indent + 'loop in tree: %s' % node ) |
| 605 return |
| 606 _done.add(node) |
| 607 node_str = str(node) |
| 608 if ids: |
| 609 node_str += ' . \t%x' % id(node) |
| 610 result.append( indent + node_str ) |
| 611 indent += INDENT |
| 612 for field in node._astng_fields: |
| 613 value = getattr(node, field) |
| 614 if isinstance(value, (list, tuple) ): |
| 615 result.append( indent + field + " = [" ) |
| 616 for child in value: |
| 617 if isinstance(child, (list, tuple) ): |
| 618 # special case for Dict # FIXME |
| 619 _repr_tree(child[0], result, indent, _done, ids) |
| 620 _repr_tree(child[1], result, indent, _done, ids) |
| 621 result.append(indent + ',') |
| 622 else: |
| 623 _repr_tree(child, result, indent, _done, ids) |
| 624 result.append( indent + "]" ) |
| 625 else: |
| 626 result.append( indent + field + " = " ) |
| 627 _repr_tree(value, result, indent, _done, ids) |
| 628 |
| 629 |
OLD | NEW |