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