OLD | NEW |
| (Empty) |
1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. | |
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr | |
3 # copyright 2003-2010 Sylvain Thenault, all rights reserved. | |
4 # contact mailto:thenault@gmail.com | |
5 # | |
6 # This file is part of logilab-astng. | |
7 # | |
8 # logilab-astng is free software: you can redistribute it and/or modify it | |
9 # under the terms of the GNU Lesser General Public License as published by the | |
10 # Free Software Foundation, either version 2.1 of the License, or (at your | |
11 # option) any later version. | |
12 # | |
13 # logilab-astng is distributed in the hope that it will be useful, but | |
14 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License | |
16 # for more details. | |
17 # | |
18 # You should have received a copy of the GNU Lesser General Public License along | |
19 # with logilab-astng. If not, see <http://www.gnu.org/licenses/>. | |
20 """Module for some node classes. More nodes in scoped_nodes.py | |
21 """ | |
22 | |
23 import sys | |
24 | |
25 from logilab.astng import BUILTINS_MODULE | |
26 from logilab.astng.exceptions import NoDefault | |
27 from logilab.astng.bases import (NodeNG, Statement, Instance, InferenceContext, | |
28 _infer_stmts, YES) | |
29 from logilab.astng.mixins import BlockRangeMixIn, AssignTypeMixin, \ | |
30 ParentAssignTypeMixin, FromImportMixIn | |
31 | |
32 | |
33 def unpack_infer(stmt, context=None): | |
34 """recursively generate nodes inferred by the given statement. | |
35 If the inferred value is a list or a tuple, recurse on the elements | |
36 """ | |
37 if isinstance(stmt, (List, Tuple)): | |
38 for elt in stmt.elts: | |
39 for infered_elt in unpack_infer(elt, context): | |
40 yield infered_elt | |
41 return | |
42 infered = stmt.infer(context).next() | |
43 if infered is stmt or infered is YES: | |
44 yield infered | |
45 return | |
46 for infered in stmt.infer(context): | |
47 for inf_inf in unpack_infer(infered, context): | |
48 yield inf_inf | |
49 | |
50 | |
51 def are_exclusive(stmt1, stmt2, exceptions=None): | |
52 """return true if the two given statements are mutually exclusive | |
53 | |
54 `exceptions` may be a list of exception names. If specified, discard If | |
55 branches and check one of the statement is in an exception handler catching | |
56 one of the given exceptions. | |
57 | |
58 algorithm : | |
59 1) index stmt1's parents | |
60 2) climb among stmt2's parents until we find a common parent | |
61 3) if the common parent is a If or TryExcept statement, look if nodes are | |
62 in exclusive branches | |
63 """ | |
64 # index stmt1's parents | |
65 stmt1_parents = {} | |
66 children = {} | |
67 node = stmt1.parent | |
68 previous = stmt1 | |
69 while node: | |
70 stmt1_parents[node] = 1 | |
71 children[node] = previous | |
72 previous = node | |
73 node = node.parent | |
74 # climb among stmt2's parents until we find a common parent | |
75 node = stmt2.parent | |
76 previous = stmt2 | |
77 while node: | |
78 if node in stmt1_parents: | |
79 # if the common parent is a If or TryExcept statement, look if | |
80 # nodes are in exclusive branches | |
81 if isinstance(node, If) and exceptions is None: | |
82 if (node.locate_child(previous)[1] | |
83 is not node.locate_child(children[node])[1]): | |
84 return True | |
85 elif isinstance(node, TryExcept): | |
86 c2attr, c2node = node.locate_child(previous) | |
87 c1attr, c1node = node.locate_child(children[node]) | |
88 if c1node is not c2node: | |
89 if ((c2attr == 'body' and c1attr == 'handlers' and children[
node].catch(exceptions)) or | |
90 (c2attr == 'handlers' and c1attr == 'body' and previous.
catch(exceptions)) or | |
91 (c2attr == 'handlers' and c1attr == 'orelse') or | |
92 (c2attr == 'orelse' and c1attr == 'handlers')): | |
93 return True | |
94 elif c2attr == 'handlers' and c1attr == 'handlers': | |
95 return previous is not children[node] | |
96 return False | |
97 previous = node | |
98 node = node.parent | |
99 return False | |
100 | |
101 | |
102 class LookupMixIn(object): | |
103 """Mixin looking up a name in the right scope | |
104 """ | |
105 | |
106 def lookup(self, name): | |
107 """lookup a variable name | |
108 | |
109 return the scope node and the list of assignments associated to the give
n | |
110 name according to the scope where it has been found (locals, globals or | |
111 builtin) | |
112 | |
113 The lookup is starting from self's scope. If self is not a frame itself
and | |
114 the name is found in the inner frame locals, statements will be filtered | |
115 to remove ignorable statements according to self's location | |
116 """ | |
117 return self.scope().scope_lookup(self, name) | |
118 | |
119 def ilookup(self, name): | |
120 """infered lookup | |
121 | |
122 return an iterator on infered values of the statements returned by | |
123 the lookup method | |
124 """ | |
125 frame, stmts = self.lookup(name) | |
126 context = InferenceContext() | |
127 return _infer_stmts(stmts, context, frame) | |
128 | |
129 def _filter_stmts(self, stmts, frame, offset): | |
130 """filter statements to remove ignorable statements. | |
131 | |
132 If self is not a frame itself and the name is found in the inner | |
133 frame locals, statements will be filtered to remove ignorable | |
134 statements according to self's location | |
135 """ | |
136 # if offset == -1, my actual frame is not the inner frame but its parent | |
137 # | |
138 # class A(B): pass | |
139 # | |
140 # we need this to resolve B correctly | |
141 if offset == -1: | |
142 myframe = self.frame().parent.frame() | |
143 else: | |
144 myframe = self.frame() | |
145 if not myframe is frame or self is frame: | |
146 return stmts | |
147 mystmt = self.statement() | |
148 # line filtering if we are in the same frame | |
149 # | |
150 # take care node may be missing lineno information (this is the case for | |
151 # nodes inserted for living objects) | |
152 if myframe is frame and mystmt.fromlineno is not None: | |
153 assert mystmt.fromlineno is not None, mystmt | |
154 mylineno = mystmt.fromlineno + offset | |
155 else: | |
156 # disabling lineno filtering | |
157 mylineno = 0 | |
158 _stmts = [] | |
159 _stmt_parents = [] | |
160 for node in stmts: | |
161 stmt = node.statement() | |
162 # line filtering is on and we have reached our location, break | |
163 if mylineno > 0 and stmt.fromlineno > mylineno: | |
164 break | |
165 assert hasattr(node, 'ass_type'), (node, node.scope(), | |
166 node.scope().locals) | |
167 ass_type = node.ass_type() | |
168 | |
169 if node.has_base(self): | |
170 break | |
171 | |
172 _stmts, done = ass_type._get_filtered_stmts(self, node, _stmts, myst
mt) | |
173 if done: | |
174 break | |
175 | |
176 optional_assign = ass_type.optional_assign | |
177 if optional_assign and ass_type.parent_of(self): | |
178 # we are inside a loop, loop var assigment is hidding previous | |
179 # assigment | |
180 _stmts = [node] | |
181 _stmt_parents = [stmt.parent] | |
182 continue | |
183 | |
184 # XXX comment various branches below!!! | |
185 try: | |
186 pindex = _stmt_parents.index(stmt.parent) | |
187 except ValueError: | |
188 pass | |
189 else: | |
190 # we got a parent index, this means the currently visited node | |
191 # is at the same block level as a previously visited node | |
192 if _stmts[pindex].ass_type().parent_of(ass_type): | |
193 # both statements are not at the same block level | |
194 continue | |
195 # if currently visited node is following previously considered | |
196 # assignement and both are not exclusive, we can drop the | |
197 # previous one. For instance in the following code :: | |
198 # | |
199 # if a: | |
200 # x = 1 | |
201 # else: | |
202 # x = 2 | |
203 # print x | |
204 # | |
205 # we can't remove neither x = 1 nor x = 2 when looking for 'x' | |
206 # of 'print x'; while in the following :: | |
207 # | |
208 # x = 1 | |
209 # x = 2 | |
210 # print x | |
211 # | |
212 # we can remove x = 1 when we see x = 2 | |
213 # | |
214 # moreover, on loop assignment types, assignment won't | |
215 # necessarily be done if the loop has no iteration, so we don't | |
216 # want to clear previous assigments if any (hence the test on | |
217 # optional_assign) | |
218 if not (optional_assign or are_exclusive(_stmts[pindex], node)): | |
219 del _stmt_parents[pindex] | |
220 del _stmts[pindex] | |
221 if isinstance(node, AssName): | |
222 if not optional_assign and stmt.parent is mystmt.parent: | |
223 _stmts = [] | |
224 _stmt_parents = [] | |
225 elif isinstance(node, DelName): | |
226 _stmts = [] | |
227 _stmt_parents = [] | |
228 continue | |
229 if not are_exclusive(self, node): | |
230 _stmts.append(node) | |
231 _stmt_parents.append(stmt.parent) | |
232 return _stmts | |
233 | |
234 # Name classes | |
235 | |
236 class AssName(LookupMixIn, ParentAssignTypeMixin, NodeNG): | |
237 """class representing an AssName node""" | |
238 | |
239 | |
240 class DelName(LookupMixIn, ParentAssignTypeMixin, NodeNG): | |
241 """class representing a DelName node""" | |
242 | |
243 | |
244 class Name(LookupMixIn, NodeNG): | |
245 """class representing a Name node""" | |
246 | |
247 | |
248 | |
249 | |
250 ##################### node classes ######################################## | |
251 | |
252 class Arguments(NodeNG, AssignTypeMixin): | |
253 """class representing an Arguments node""" | |
254 _astng_fields = ('args', 'defaults') | |
255 args = None | |
256 defaults = None | |
257 | |
258 def __init__(self, vararg=None, kwarg=None): | |
259 self.vararg = vararg | |
260 self.kwarg = kwarg | |
261 | |
262 def _infer_name(self, frame, name): | |
263 if self.parent is frame: | |
264 return name | |
265 return None | |
266 | |
267 def format_args(self): | |
268 """return arguments formatted as string""" | |
269 result = [_format_args(self.args, self.defaults)] | |
270 if self.vararg: | |
271 result.append('*%s' % self.vararg) | |
272 if self.kwarg: | |
273 result.append('**%s' % self.kwarg) | |
274 return ', '.join(result) | |
275 | |
276 def default_value(self, argname): | |
277 """return the default value for an argument | |
278 | |
279 :raise `NoDefault`: if there is no default value defined | |
280 """ | |
281 i = _find_arg(argname, self.args)[0] | |
282 if i is not None: | |
283 idx = i - (len(self.args) - len(self.defaults)) | |
284 if idx >= 0: | |
285 return self.defaults[idx] | |
286 raise NoDefault() | |
287 | |
288 def is_argument(self, name): | |
289 """return True if the name is defined in arguments""" | |
290 if name == self.vararg: | |
291 return True | |
292 if name == self.kwarg: | |
293 return True | |
294 return self.find_argname(name, True)[1] is not None | |
295 | |
296 def find_argname(self, argname, rec=False): | |
297 """return index and Name node with given name""" | |
298 if self.args: # self.args may be None in some cases (builtin function) | |
299 return _find_arg(argname, self.args, rec) | |
300 return None, None | |
301 | |
302 | |
303 def _find_arg(argname, args, rec=False): | |
304 for i, arg in enumerate(args): | |
305 if isinstance(arg, Tuple): | |
306 if rec: | |
307 found = _find_arg(argname, arg.elts) | |
308 if found[0] is not None: | |
309 return found | |
310 elif arg.name == argname: | |
311 return i, arg | |
312 return None, None | |
313 | |
314 | |
315 def _format_args(args, defaults=None): | |
316 values = [] | |
317 if args is None: | |
318 return '' | |
319 if defaults is not None: | |
320 default_offset = len(args) - len(defaults) | |
321 for i, arg in enumerate(args): | |
322 if isinstance(arg, Tuple): | |
323 values.append('(%s)' % _format_args(arg.elts)) | |
324 else: | |
325 values.append(arg.name) | |
326 if defaults is not None and i >= default_offset: | |
327 values[-1] += '=' + defaults[i-default_offset].as_string() | |
328 return ', '.join(values) | |
329 | |
330 | |
331 class AssAttr(NodeNG, ParentAssignTypeMixin): | |
332 """class representing an AssAttr node""" | |
333 _astng_fields = ('expr',) | |
334 expr = None | |
335 | |
336 class Assert(Statement): | |
337 """class representing an Assert node""" | |
338 _astng_fields = ('test', 'fail',) | |
339 test = None | |
340 fail = None | |
341 | |
342 class Assign(Statement, AssignTypeMixin): | |
343 """class representing an Assign node""" | |
344 _astng_fields = ('targets', 'value',) | |
345 targets = None | |
346 value = None | |
347 | |
348 class AugAssign(Statement, AssignTypeMixin): | |
349 """class representing an AugAssign node""" | |
350 _astng_fields = ('target', 'value',) | |
351 target = None | |
352 value = None | |
353 | |
354 class Backquote(NodeNG): | |
355 """class representing a Backquote node""" | |
356 _astng_fields = ('value',) | |
357 value = None | |
358 | |
359 class BinOp(NodeNG): | |
360 """class representing a BinOp node""" | |
361 _astng_fields = ('left', 'right',) | |
362 left = None | |
363 right = None | |
364 | |
365 class BoolOp(NodeNG): | |
366 """class representing a BoolOp node""" | |
367 _astng_fields = ('values',) | |
368 values = None | |
369 | |
370 class Break(Statement): | |
371 """class representing a Break node""" | |
372 | |
373 | |
374 class CallFunc(NodeNG): | |
375 """class representing a CallFunc node""" | |
376 _astng_fields = ('func', 'args', 'starargs', 'kwargs') | |
377 func = None | |
378 args = None | |
379 starargs = None | |
380 kwargs = None | |
381 | |
382 def __init__(self): | |
383 self.starargs = None | |
384 self.kwargs = None | |
385 | |
386 class Compare(NodeNG): | |
387 """class representing a Compare node""" | |
388 _astng_fields = ('left', 'ops',) | |
389 left = None | |
390 ops = None | |
391 | |
392 def get_children(self): | |
393 """override get_children for tuple fields""" | |
394 yield self.left | |
395 for _, comparator in self.ops: | |
396 yield comparator # we don't want the 'op' | |
397 | |
398 def last_child(self): | |
399 """override last_child""" | |
400 # XXX maybe if self.ops: | |
401 return self.ops[-1][1] | |
402 #return self.left | |
403 | |
404 class Comprehension(NodeNG): | |
405 """class representing a Comprehension node""" | |
406 _astng_fields = ('target', 'iter' ,'ifs') | |
407 target = None | |
408 iter = None | |
409 ifs = None | |
410 | |
411 optional_assign = True | |
412 def ass_type(self): | |
413 return self | |
414 | |
415 def _get_filtered_stmts(self, lookup_node, node, stmts, mystmt): | |
416 """method used in filter_stmts""" | |
417 if self is mystmt: | |
418 if isinstance(lookup_node, (Const, Name)): | |
419 return [lookup_node], True | |
420 | |
421 elif self.statement() is mystmt: | |
422 # original node's statement is the assignment, only keeps | |
423 # current node (gen exp, list comp) | |
424 | |
425 return [node], True | |
426 | |
427 return stmts, False | |
428 | |
429 | |
430 class Const(NodeNG, Instance): | |
431 """represent a constant node like num, str, bool, None, bytes""" | |
432 | |
433 def __init__(self, value=None): | |
434 self.value = value | |
435 | |
436 def getitem(self, index, context=None): | |
437 if isinstance(self.value, basestring): | |
438 return Const(self.value[index]) | |
439 raise TypeError('%r (value=%s)' % (self, self.value)) | |
440 | |
441 def has_dynamic_getattr(self): | |
442 return False | |
443 | |
444 def itered(self): | |
445 if isinstance(self.value, basestring): | |
446 return self.value | |
447 raise TypeError() | |
448 | |
449 def pytype(self): | |
450 return self._proxied.qname() | |
451 | |
452 | |
453 class Continue(Statement): | |
454 """class representing a Continue node""" | |
455 | |
456 | |
457 class Decorators(NodeNG): | |
458 """class representing a Decorators node""" | |
459 _astng_fields = ('nodes',) | |
460 nodes = None | |
461 | |
462 def __init__(self, nodes=None): | |
463 self.nodes = nodes | |
464 | |
465 def scope(self): | |
466 # skip the function node to go directly to the upper level scope | |
467 return self.parent.parent.scope() | |
468 | |
469 class DelAttr(NodeNG, ParentAssignTypeMixin): | |
470 """class representing a DelAttr node""" | |
471 _astng_fields = ('expr',) | |
472 expr = None | |
473 | |
474 | |
475 class Delete(Statement, AssignTypeMixin): | |
476 """class representing a Delete node""" | |
477 _astng_fields = ('targets',) | |
478 targets = None | |
479 | |
480 | |
481 class Dict(NodeNG, Instance): | |
482 """class representing a Dict node""" | |
483 _astng_fields = ('items',) | |
484 | |
485 def __init__(self, items=None): | |
486 if items is None: | |
487 self.items = [] | |
488 else: | |
489 self.items = [(const_factory(k), const_factory(v)) | |
490 for k,v in items.iteritems()] | |
491 | |
492 def pytype(self): | |
493 return '%s.dict' % BUILTINS_MODULE | |
494 | |
495 def get_children(self): | |
496 """get children of a Dict node""" | |
497 # overrides get_children | |
498 for key, value in self.items: | |
499 yield key | |
500 yield value | |
501 | |
502 def last_child(self): | |
503 """override last_child""" | |
504 if self.items: | |
505 return self.items[-1][1] | |
506 return None | |
507 | |
508 def itered(self): | |
509 return self.items[::2] | |
510 | |
511 def getitem(self, key, context=None): | |
512 for i in xrange(0, len(self.items), 2): | |
513 for inferedkey in self.items[i].infer(context): | |
514 if inferedkey is YES: | |
515 continue | |
516 if isinstance(inferedkey, Const) and inferedkey.value == key: | |
517 return self.items[i+1] | |
518 raise IndexError(key) | |
519 | |
520 | |
521 class Discard(Statement): | |
522 """class representing a Discard node""" | |
523 _astng_fields = ('value',) | |
524 value = None | |
525 | |
526 | |
527 class Ellipsis(NodeNG): | |
528 """class representing an Ellipsis node""" | |
529 | |
530 | |
531 class EmptyNode(NodeNG): | |
532 """class representing an EmptyNode node""" | |
533 | |
534 | |
535 class ExceptHandler(Statement, AssignTypeMixin): | |
536 """class representing an ExceptHandler node""" | |
537 _astng_fields = ('type', 'name', 'body',) | |
538 type = None | |
539 name = None | |
540 body = None | |
541 | |
542 def _blockstart_toline(self): | |
543 if self.name: | |
544 return self.name.tolineno | |
545 elif self.type: | |
546 return self.type.tolineno | |
547 else: | |
548 return self.lineno | |
549 | |
550 def set_line_info(self, lastchild): | |
551 self.fromlineno = self.lineno | |
552 self.tolineno = lastchild.tolineno | |
553 self.blockstart_tolineno = self._blockstart_toline() | |
554 | |
555 def catch(self, exceptions): | |
556 if self.type is None or exceptions is None: | |
557 return True | |
558 for node in self.type.nodes_of_class(Name): | |
559 if node.name in exceptions: | |
560 return True | |
561 | |
562 | |
563 class Exec(Statement): | |
564 """class representing an Exec node""" | |
565 _astng_fields = ('expr', 'globals', 'locals',) | |
566 expr = None | |
567 globals = None | |
568 locals = None | |
569 | |
570 | |
571 class ExtSlice(NodeNG): | |
572 """class representing an ExtSlice node""" | |
573 _astng_fields = ('dims',) | |
574 dims = None | |
575 | |
576 class For(BlockRangeMixIn, AssignTypeMixin, Statement): | |
577 """class representing a For node""" | |
578 _astng_fields = ('target', 'iter', 'body', 'orelse',) | |
579 target = None | |
580 iter = None | |
581 body = None | |
582 orelse = None | |
583 | |
584 optional_assign = True | |
585 def _blockstart_toline(self): | |
586 return self.iter.tolineno | |
587 | |
588 | |
589 class From(FromImportMixIn, Statement): | |
590 """class representing a From node""" | |
591 | |
592 def __init__(self, fromname, names, level=0): | |
593 self.modname = fromname | |
594 self.names = names | |
595 self.level = level | |
596 | |
597 class Getattr(NodeNG): | |
598 """class representing a Getattr node""" | |
599 _astng_fields = ('expr',) | |
600 expr = None | |
601 | |
602 | |
603 class Global(Statement): | |
604 """class representing a Global node""" | |
605 | |
606 def __init__(self, names): | |
607 self.names = names | |
608 | |
609 def _infer_name(self, frame, name): | |
610 return name | |
611 | |
612 | |
613 class If(BlockRangeMixIn, Statement): | |
614 """class representing an If node""" | |
615 _astng_fields = ('test', 'body', 'orelse') | |
616 test = None | |
617 body = None | |
618 orelse = None | |
619 | |
620 def _blockstart_toline(self): | |
621 return self.test.tolineno | |
622 | |
623 def block_range(self, lineno): | |
624 """handle block line numbers range for if statements""" | |
625 if lineno == self.body[0].fromlineno: | |
626 return lineno, lineno | |
627 if lineno <= self.body[-1].tolineno: | |
628 return lineno, self.body[-1].tolineno | |
629 return self._elsed_block_range(lineno, self.orelse, | |
630 self.body[0].fromlineno - 1) | |
631 | |
632 | |
633 class IfExp(NodeNG): | |
634 """class representing an IfExp node""" | |
635 _astng_fields = ('test', 'body', 'orelse') | |
636 test = None | |
637 body = None | |
638 orelse = None | |
639 | |
640 | |
641 class Import(FromImportMixIn, Statement): | |
642 """class representing an Import node""" | |
643 | |
644 | |
645 class Index(NodeNG): | |
646 """class representing an Index node""" | |
647 _astng_fields = ('value',) | |
648 value = None | |
649 | |
650 | |
651 class Keyword(NodeNG): | |
652 """class representing a Keyword node""" | |
653 _astng_fields = ('value',) | |
654 value = None | |
655 | |
656 | |
657 class List(NodeNG, Instance, ParentAssignTypeMixin): | |
658 """class representing a List node""" | |
659 _astng_fields = ('elts',) | |
660 | |
661 def __init__(self, elts=None): | |
662 if elts is None: | |
663 self.elts = [] | |
664 else: | |
665 self.elts = [const_factory(e) for e in elts] | |
666 | |
667 def pytype(self): | |
668 return '%s.list' % BUILTINS_MODULE | |
669 | |
670 def getitem(self, index, context=None): | |
671 return self.elts[index] | |
672 | |
673 def itered(self): | |
674 return self.elts | |
675 | |
676 | |
677 class Nonlocal(Statement): | |
678 """class representing a Nonlocal node""" | |
679 | |
680 def __init__(self, names): | |
681 self.names = names | |
682 | |
683 def _infer_name(self, frame, name): | |
684 return name | |
685 | |
686 | |
687 class Pass(Statement): | |
688 """class representing a Pass node""" | |
689 | |
690 | |
691 class Print(Statement): | |
692 """class representing a Print node""" | |
693 _astng_fields = ('dest', 'values',) | |
694 dest = None | |
695 values = None | |
696 | |
697 | |
698 class Raise(Statement): | |
699 """class representing a Raise node""" | |
700 exc = None | |
701 if sys.version_info < (3, 0): | |
702 _astng_fields = ('exc', 'inst', 'tback') | |
703 inst = None | |
704 tback = None | |
705 else: | |
706 _astng_fields = ('exc', 'cause') | |
707 exc = None | |
708 cause = None | |
709 | |
710 def raises_not_implemented(self): | |
711 if not self.exc: | |
712 return | |
713 for name in self.exc.nodes_of_class(Name): | |
714 if name.name == 'NotImplementedError': | |
715 return True | |
716 | |
717 | |
718 class Return(Statement): | |
719 """class representing a Return node""" | |
720 _astng_fields = ('value',) | |
721 value = None | |
722 | |
723 | |
724 class Set(NodeNG, Instance, ParentAssignTypeMixin): | |
725 """class representing a Set node""" | |
726 _astng_fields = ('elts',) | |
727 | |
728 def __init__(self, elts=None): | |
729 if elts is None: | |
730 self.elts = [] | |
731 else: | |
732 self.elts = [const_factory(e) for e in elts] | |
733 | |
734 def pytype(self): | |
735 return '%s.set' % BUILTINS_MODULE | |
736 | |
737 def itered(self): | |
738 return self.elts | |
739 | |
740 | |
741 class Slice(NodeNG): | |
742 """class representing a Slice node""" | |
743 _astng_fields = ('lower', 'upper', 'step') | |
744 lower = None | |
745 upper = None | |
746 step = None | |
747 | |
748 class Starred(NodeNG): | |
749 """class representing a Starred node""" | |
750 _astng_fields = ('value',) | |
751 value = None | |
752 | |
753 | |
754 class Subscript(NodeNG): | |
755 """class representing a Subscript node""" | |
756 _astng_fields = ('value', 'slice') | |
757 value = None | |
758 slice = None | |
759 | |
760 | |
761 class TryExcept(BlockRangeMixIn, Statement): | |
762 """class representing a TryExcept node""" | |
763 _astng_fields = ('body', 'handlers', 'orelse',) | |
764 body = None | |
765 handlers = None | |
766 orelse = None | |
767 | |
768 def _infer_name(self, frame, name): | |
769 return name | |
770 | |
771 def _blockstart_toline(self): | |
772 return self.lineno | |
773 | |
774 def block_range(self, lineno): | |
775 """handle block line numbers range for try/except statements""" | |
776 last = None | |
777 for exhandler in self.handlers: | |
778 if exhandler.type and lineno == exhandler.type.fromlineno: | |
779 return lineno, lineno | |
780 if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].toli
neno: | |
781 return lineno, exhandler.body[-1].tolineno | |
782 if last is None: | |
783 last = exhandler.body[0].fromlineno - 1 | |
784 return self._elsed_block_range(lineno, self.orelse, last) | |
785 | |
786 | |
787 class TryFinally(BlockRangeMixIn, Statement): | |
788 """class representing a TryFinally node""" | |
789 _astng_fields = ('body', 'finalbody',) | |
790 body = None | |
791 finalbody = None | |
792 | |
793 def _blockstart_toline(self): | |
794 return self.lineno | |
795 | |
796 def block_range(self, lineno): | |
797 """handle block line numbers range for try/finally statements""" | |
798 child = self.body[0] | |
799 # py2.5 try: except: finally: | |
800 if (isinstance(child, TryExcept) and child.fromlineno == self.fromlineno | |
801 and lineno > self.fromlineno and lineno <= child.tolineno): | |
802 return child.block_range(lineno) | |
803 return self._elsed_block_range(lineno, self.finalbody) | |
804 | |
805 | |
806 class Tuple(NodeNG, Instance, ParentAssignTypeMixin): | |
807 """class representing a Tuple node""" | |
808 _astng_fields = ('elts',) | |
809 | |
810 def __init__(self, elts=None): | |
811 if elts is None: | |
812 self.elts = [] | |
813 else: | |
814 self.elts = [const_factory(e) for e in elts] | |
815 | |
816 def pytype(self): | |
817 return '%s.tuple' % BUILTINS_MODULE | |
818 | |
819 def getitem(self, index, context=None): | |
820 return self.elts[index] | |
821 | |
822 def itered(self): | |
823 return self.elts | |
824 | |
825 | |
826 class UnaryOp(NodeNG): | |
827 """class representing an UnaryOp node""" | |
828 _astng_fields = ('operand',) | |
829 operand = None | |
830 | |
831 | |
832 class While(BlockRangeMixIn, Statement): | |
833 """class representing a While node""" | |
834 _astng_fields = ('test', 'body', 'orelse',) | |
835 test = None | |
836 body = None | |
837 orelse = None | |
838 | |
839 def _blockstart_toline(self): | |
840 return self.test.tolineno | |
841 | |
842 def block_range(self, lineno): | |
843 """handle block line numbers range for for and while statements""" | |
844 return self. _elsed_block_range(lineno, self.orelse) | |
845 | |
846 | |
847 class With(BlockRangeMixIn, AssignTypeMixin, Statement): | |
848 """class representing a With node""" | |
849 _astng_fields = ('expr', 'vars', 'body') | |
850 expr = None | |
851 vars = None | |
852 body = None | |
853 | |
854 def _blockstart_toline(self): | |
855 if self.vars: | |
856 return self.vars.tolineno | |
857 else: | |
858 return self.expr.tolineno | |
859 | |
860 | |
861 class Yield(NodeNG): | |
862 """class representing a Yield node""" | |
863 _astng_fields = ('value',) | |
864 value = None | |
865 | |
866 # constants ############################################################## | |
867 | |
868 CONST_CLS = { | |
869 list: List, | |
870 tuple: Tuple, | |
871 dict: Dict, | |
872 set: Set, | |
873 type(None): Const, | |
874 } | |
875 | |
876 def _update_const_classes(): | |
877 """update constant classes, so the keys of CONST_CLS can be reused""" | |
878 klasses = (bool, int, float, complex, str) | |
879 if sys.version_info < (3, 0): | |
880 klasses += (unicode, long) | |
881 if sys.version_info >= (2, 6): | |
882 klasses += (bytes,) | |
883 for kls in klasses: | |
884 CONST_CLS[kls] = Const | |
885 _update_const_classes() | |
886 | |
887 def const_factory(value): | |
888 """return an astng node for a python value""" | |
889 # since const_factory is called to evaluate content of container (eg list, | |
890 # tuple), it may be called with some node as argument that should be left | |
891 # untouched | |
892 if isinstance(value, NodeNG): | |
893 return value | |
894 try: | |
895 return CONST_CLS[value.__class__](value) | |
896 except (KeyError, AttributeError): | |
897 # some constants (like from gtk._gtk) don't have their class in | |
898 # CONST_CLS, though we can "assert isinstance(value, tuple(CONST_CLS))" | |
899 if isinstance(value, tuple(CONST_CLS)): | |
900 return Const(value) | |
901 node = EmptyNode() | |
902 node.object = value | |
903 return node | |
OLD | NEW |