OLD | NEW |
1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. | 1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr | 2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
3 # | 3 # |
4 # This file is part of astroid. | 4 # This file is part of astroid. |
5 # | 5 # |
6 # astroid is free software: you can redistribute it and/or modify it | 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 | 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 | 8 # Free Software Foundation, either version 2.1 of the License, or (at your |
9 # option) any later version. | 9 # option) any later version. |
10 # | 10 # |
11 # astroid is distributed in the hope that it will be useful, but | 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 | 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 | 13 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License |
14 # for more details. | 14 # for more details. |
15 # | 15 # |
16 # You should have received a copy of the GNU Lesser General Public License along | 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/>. | 17 # with astroid. If not, see <http://www.gnu.org/licenses/>. |
18 """Module for some node classes. More nodes in scoped_nodes.py | 18 """Module for some node classes. More nodes in scoped_nodes.py |
19 """ | 19 """ |
20 | 20 |
21 import sys | 21 import sys |
22 | 22 |
| 23 import six |
| 24 from logilab.common.decorators import cachedproperty |
| 25 |
23 from astroid.exceptions import NoDefault | 26 from astroid.exceptions import NoDefault |
24 from astroid.bases import (NodeNG, Statement, Instance, InferenceContext, | 27 from astroid.bases import (NodeNG, Statement, Instance, InferenceContext, |
25 _infer_stmts, YES, BUILTINS) | 28 _infer_stmts, YES, BUILTINS) |
26 from astroid.mixins import (BlockRangeMixIn, AssignTypeMixin, | 29 from astroid.mixins import (BlockRangeMixIn, AssignTypeMixin, |
27 ParentAssignTypeMixin, FromImportMixIn) | 30 ParentAssignTypeMixin, FromImportMixIn) |
28 | 31 |
29 PY3K = sys.version_info >= (3, 0) | 32 PY3K = sys.version_info >= (3, 0) |
30 | 33 |
31 | 34 |
32 def unpack_infer(stmt, context=None): | 35 def unpack_infer(stmt, context=None): |
33 """recursively generate nodes inferred by the given statement. | 36 """recursively generate nodes inferred by the given statement. |
34 If the inferred value is a list or a tuple, recurse on the elements | 37 If the inferred value is a list or a tuple, recurse on the elements |
35 """ | 38 """ |
36 if isinstance(stmt, (List, Tuple)): | 39 if isinstance(stmt, (List, Tuple)): |
37 for elt in stmt.elts: | 40 for elt in stmt.elts: |
38 for infered_elt in unpack_infer(elt, context): | 41 for infered_elt in unpack_infer(elt, context): |
39 yield infered_elt | 42 yield infered_elt |
40 return | 43 return |
41 # if infered is a final node, return it and stop | 44 # if infered is a final node, return it and stop |
42 infered = stmt.infer(context).next() | 45 infered = next(stmt.infer(context)) |
43 if infered is stmt: | 46 if infered is stmt: |
44 yield infered | 47 yield infered |
45 return | 48 return |
46 # else, infer recursivly, except YES object that should be returned as is | 49 # else, infer recursivly, except YES object that should be returned as is |
47 for infered in stmt.infer(context): | 50 for infered in stmt.infer(context): |
48 if infered is YES: | 51 if infered is YES: |
49 yield infered | 52 yield infered |
50 else: | 53 else: |
51 for inf_inf in unpack_infer(infered, context): | 54 for inf_inf in unpack_infer(infered, context): |
52 yield inf_inf | 55 yield inf_inf |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
120 """ | 123 """ |
121 return self.scope().scope_lookup(self, name) | 124 return self.scope().scope_lookup(self, name) |
122 | 125 |
123 def ilookup(self, name): | 126 def ilookup(self, name): |
124 """infered lookup | 127 """infered lookup |
125 | 128 |
126 return an iterator on infered values of the statements returned by | 129 return an iterator on infered values of the statements returned by |
127 the lookup method | 130 the lookup method |
128 """ | 131 """ |
129 frame, stmts = self.lookup(name) | 132 frame, stmts = self.lookup(name) |
130 context = InferenceContext() | 133 return _infer_stmts(stmts, None, frame) |
131 return _infer_stmts(stmts, context, frame) | |
132 | 134 |
133 def _filter_stmts(self, stmts, frame, offset): | 135 def _filter_stmts(self, stmts, frame, offset): |
134 """filter statements to remove ignorable statements. | 136 """filter statements to remove ignorable statements. |
135 | 137 |
136 If self is not a frame itself and the name is found in the inner | 138 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 | 139 frame locals, statements will be filtered to remove ignorable |
138 statements according to self's location | 140 statements according to self's location |
139 """ | 141 """ |
140 # if offset == -1, my actual frame is not the inner frame but its parent | 142 # if offset == -1, my actual frame is not the inner frame but its parent |
141 # | 143 # |
142 # class A(B): pass | 144 # class A(B): pass |
143 # | 145 # |
144 # we need this to resolve B correctly | 146 # we need this to resolve B correctly |
145 if offset == -1: | 147 if offset == -1: |
146 myframe = self.frame().parent.frame() | 148 myframe = self.frame().parent.frame() |
147 else: | 149 else: |
148 myframe = self.frame() | 150 myframe = self.frame() |
| 151 # If the frame of this node is the same as the statement |
| 152 # of this node, then the node is part of a class or |
| 153 # a function definition and the frame of this node should be the |
| 154 # the upper frame, not the frame of the definition. |
| 155 # For more information why this is important, |
| 156 # see Pylint issue #295. |
| 157 # For example, for 'b', the statement is the same |
| 158 # as the frame / scope: |
| 159 # |
| 160 # def test(b=1): |
| 161 # ... |
| 162 |
| 163 if self.statement() is myframe and myframe.parent: |
| 164 myframe = myframe.parent.frame() |
149 if not myframe is frame or self is frame: | 165 if not myframe is frame or self is frame: |
150 return stmts | 166 return stmts |
151 mystmt = self.statement() | 167 mystmt = self.statement() |
152 # line filtering if we are in the same frame | 168 # line filtering if we are in the same frame |
153 # | 169 # |
154 # take care node may be missing lineno information (this is the case for | 170 # take care node may be missing lineno information (this is the case for |
155 # nodes inserted for living objects) | 171 # nodes inserted for living objects) |
156 if myframe is frame and mystmt.fromlineno is not None: | 172 if myframe is frame and mystmt.fromlineno is not None: |
157 assert mystmt.fromlineno is not None, mystmt | 173 assert mystmt.fromlineno is not None, mystmt |
158 mylineno = mystmt.fromlineno + offset | 174 mylineno = mystmt.fromlineno + offset |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
282 | 298 |
283 def __init__(self, vararg=None, kwarg=None): | 299 def __init__(self, vararg=None, kwarg=None): |
284 self.vararg = vararg | 300 self.vararg = vararg |
285 self.kwarg = kwarg | 301 self.kwarg = kwarg |
286 | 302 |
287 def _infer_name(self, frame, name): | 303 def _infer_name(self, frame, name): |
288 if self.parent is frame: | 304 if self.parent is frame: |
289 return name | 305 return name |
290 return None | 306 return None |
291 | 307 |
| 308 @cachedproperty |
| 309 def fromlineno(self): |
| 310 lineno = super(Arguments, self).fromlineno |
| 311 return max(lineno, self.parent.fromlineno) |
| 312 |
292 def format_args(self): | 313 def format_args(self): |
293 """return arguments formatted as string""" | 314 """return arguments formatted as string""" |
294 result = [] | 315 result = [] |
295 if self.args: | 316 if self.args: |
296 result.append(_format_args(self.args, self.defaults)) | 317 result.append(_format_args(self.args, self.defaults)) |
297 if self.vararg: | 318 if self.vararg: |
298 result.append('*%s' % self.vararg) | 319 result.append('*%s' % self.vararg) |
299 if self.kwarg: | 320 if self.kwarg: |
300 result.append('**%s' % self.kwarg) | 321 result.append('**%s' % self.kwarg) |
301 if self.kwonlyargs: | 322 if self.kwonlyargs: |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
468 return stmts, False | 489 return stmts, False |
469 | 490 |
470 | 491 |
471 class Const(NodeNG, Instance): | 492 class Const(NodeNG, Instance): |
472 """represent a constant node like num, str, bool, None, bytes""" | 493 """represent a constant node like num, str, bool, None, bytes""" |
473 | 494 |
474 def __init__(self, value=None): | 495 def __init__(self, value=None): |
475 self.value = value | 496 self.value = value |
476 | 497 |
477 def getitem(self, index, context=None): | 498 def getitem(self, index, context=None): |
478 if isinstance(self.value, basestring): | 499 if isinstance(self.value, six.string_types): |
479 return Const(self.value[index]) | 500 return Const(self.value[index]) |
480 raise TypeError('%r (value=%s)' % (self, self.value)) | 501 raise TypeError('%r (value=%s)' % (self, self.value)) |
481 | 502 |
482 def has_dynamic_getattr(self): | 503 def has_dynamic_getattr(self): |
483 return False | 504 return False |
484 | 505 |
485 def itered(self): | 506 def itered(self): |
486 if isinstance(self.value, basestring): | 507 if isinstance(self.value, six.string_types): |
487 return self.value | 508 return self.value |
488 raise TypeError() | 509 raise TypeError() |
489 | 510 |
490 def pytype(self): | 511 def pytype(self): |
491 return self._proxied.qname() | 512 return self._proxied.qname() |
492 | 513 |
493 | 514 |
494 class Continue(Statement): | 515 class Continue(Statement): |
495 """class representing a Continue node""" | 516 """class representing a Continue node""" |
496 | 517 |
(...skipping 24 matching lines...) Expand all Loading... |
521 | 542 |
522 class Dict(NodeNG, Instance): | 543 class Dict(NodeNG, Instance): |
523 """class representing a Dict node""" | 544 """class representing a Dict node""" |
524 _astroid_fields = ('items',) | 545 _astroid_fields = ('items',) |
525 | 546 |
526 def __init__(self, items=None): | 547 def __init__(self, items=None): |
527 if items is None: | 548 if items is None: |
528 self.items = [] | 549 self.items = [] |
529 else: | 550 else: |
530 self.items = [(const_factory(k), const_factory(v)) | 551 self.items = [(const_factory(k), const_factory(v)) |
531 for k, v in items.iteritems()] | 552 for k, v in items.items()] |
532 | 553 |
533 def pytype(self): | 554 def pytype(self): |
534 return '%s.dict' % BUILTINS | 555 return '%s.dict' % BUILTINS |
535 | 556 |
536 def get_children(self): | 557 def get_children(self): |
537 """get children of a Dict node""" | 558 """get children of a Dict node""" |
538 # overrides get_children | 559 # overrides get_children |
539 for key, value in self.items: | 560 for key, value in self.items: |
540 yield key | 561 yield key |
541 yield value | 562 yield value |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
576 """class representing an EmptyNode node""" | 597 """class representing an EmptyNode node""" |
577 | 598 |
578 | 599 |
579 class ExceptHandler(Statement, AssignTypeMixin): | 600 class ExceptHandler(Statement, AssignTypeMixin): |
580 """class representing an ExceptHandler node""" | 601 """class representing an ExceptHandler node""" |
581 _astroid_fields = ('type', 'name', 'body',) | 602 _astroid_fields = ('type', 'name', 'body',) |
582 type = None | 603 type = None |
583 name = None | 604 name = None |
584 body = None | 605 body = None |
585 | 606 |
586 def _blockstart_toline(self): | 607 @cachedproperty |
| 608 def blockstart_tolineno(self): |
587 if self.name: | 609 if self.name: |
588 return self.name.tolineno | 610 return self.name.tolineno |
589 elif self.type: | 611 elif self.type: |
590 return self.type.tolineno | 612 return self.type.tolineno |
591 else: | 613 else: |
592 return self.lineno | 614 return self.lineno |
593 | 615 |
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): | 616 def catch(self, exceptions): |
600 if self.type is None or exceptions is None: | 617 if self.type is None or exceptions is None: |
601 return True | 618 return True |
602 for node in self.type.nodes_of_class(Name): | 619 for node in self.type.nodes_of_class(Name): |
603 if node.name in exceptions: | 620 if node.name in exceptions: |
604 return True | 621 return True |
605 | 622 |
606 | 623 |
607 class Exec(Statement): | 624 class Exec(Statement): |
608 """class representing an Exec node""" | 625 """class representing an Exec node""" |
(...skipping 10 matching lines...) Expand all Loading... |
619 | 636 |
620 class For(BlockRangeMixIn, AssignTypeMixin, Statement): | 637 class For(BlockRangeMixIn, AssignTypeMixin, Statement): |
621 """class representing a For node""" | 638 """class representing a For node""" |
622 _astroid_fields = ('target', 'iter', 'body', 'orelse',) | 639 _astroid_fields = ('target', 'iter', 'body', 'orelse',) |
623 target = None | 640 target = None |
624 iter = None | 641 iter = None |
625 body = None | 642 body = None |
626 orelse = None | 643 orelse = None |
627 | 644 |
628 optional_assign = True | 645 optional_assign = True |
629 def _blockstart_toline(self): | 646 @cachedproperty |
| 647 def blockstart_tolineno(self): |
630 return self.iter.tolineno | 648 return self.iter.tolineno |
631 | 649 |
632 | 650 |
633 class From(FromImportMixIn, Statement): | 651 class From(FromImportMixIn, Statement): |
634 """class representing a From node""" | 652 """class representing a From node""" |
635 | 653 |
636 def __init__(self, fromname, names, level=0): | 654 def __init__(self, fromname, names, level=0): |
637 self.modname = fromname | 655 self.modname = fromname |
638 self.names = names | 656 self.names = names |
639 self.level = level | 657 self.level = level |
(...skipping 14 matching lines...) Expand all Loading... |
654 return name | 672 return name |
655 | 673 |
656 | 674 |
657 class If(BlockRangeMixIn, Statement): | 675 class If(BlockRangeMixIn, Statement): |
658 """class representing an If node""" | 676 """class representing an If node""" |
659 _astroid_fields = ('test', 'body', 'orelse') | 677 _astroid_fields = ('test', 'body', 'orelse') |
660 test = None | 678 test = None |
661 body = None | 679 body = None |
662 orelse = None | 680 orelse = None |
663 | 681 |
664 def _blockstart_toline(self): | 682 @cachedproperty |
| 683 def blockstart_tolineno(self): |
665 return self.test.tolineno | 684 return self.test.tolineno |
666 | 685 |
667 def block_range(self, lineno): | 686 def block_range(self, lineno): |
668 """handle block line numbers range for if statements""" | 687 """handle block line numbers range for if statements""" |
669 if lineno == self.body[0].fromlineno: | 688 if lineno == self.body[0].fromlineno: |
670 return lineno, lineno | 689 return lineno, lineno |
671 if lineno <= self.body[-1].tolineno: | 690 if lineno <= self.body[-1].tolineno: |
672 return lineno, self.body[-1].tolineno | 691 return lineno, self.body[-1].tolineno |
673 return self._elsed_block_range(lineno, self.orelse, | 692 return self._elsed_block_range(lineno, self.orelse, |
674 self.body[0].fromlineno - 1) | 693 self.body[0].fromlineno - 1) |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
805 class TryExcept(BlockRangeMixIn, Statement): | 824 class TryExcept(BlockRangeMixIn, Statement): |
806 """class representing a TryExcept node""" | 825 """class representing a TryExcept node""" |
807 _astroid_fields = ('body', 'handlers', 'orelse',) | 826 _astroid_fields = ('body', 'handlers', 'orelse',) |
808 body = None | 827 body = None |
809 handlers = None | 828 handlers = None |
810 orelse = None | 829 orelse = None |
811 | 830 |
812 def _infer_name(self, frame, name): | 831 def _infer_name(self, frame, name): |
813 return name | 832 return name |
814 | 833 |
815 def _blockstart_toline(self): | |
816 return self.lineno | |
817 | |
818 def block_range(self, lineno): | 834 def block_range(self, lineno): |
819 """handle block line numbers range for try/except statements""" | 835 """handle block line numbers range for try/except statements""" |
820 last = None | 836 last = None |
821 for exhandler in self.handlers: | 837 for exhandler in self.handlers: |
822 if exhandler.type and lineno == exhandler.type.fromlineno: | 838 if exhandler.type and lineno == exhandler.type.fromlineno: |
823 return lineno, lineno | 839 return lineno, lineno |
824 if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].toli
neno: | 840 if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].toli
neno: |
825 return lineno, exhandler.body[-1].tolineno | 841 return lineno, exhandler.body[-1].tolineno |
826 if last is None: | 842 if last is None: |
827 last = exhandler.body[0].fromlineno - 1 | 843 last = exhandler.body[0].fromlineno - 1 |
828 return self._elsed_block_range(lineno, self.orelse, last) | 844 return self._elsed_block_range(lineno, self.orelse, last) |
829 | 845 |
830 | 846 |
831 class TryFinally(BlockRangeMixIn, Statement): | 847 class TryFinally(BlockRangeMixIn, Statement): |
832 """class representing a TryFinally node""" | 848 """class representing a TryFinally node""" |
833 _astroid_fields = ('body', 'finalbody',) | 849 _astroid_fields = ('body', 'finalbody',) |
834 body = None | 850 body = None |
835 finalbody = None | 851 finalbody = None |
836 | 852 |
837 def _blockstart_toline(self): | |
838 return self.lineno | |
839 | |
840 def block_range(self, lineno): | 853 def block_range(self, lineno): |
841 """handle block line numbers range for try/finally statements""" | 854 """handle block line numbers range for try/finally statements""" |
842 child = self.body[0] | 855 child = self.body[0] |
843 # py2.5 try: except: finally: | 856 # py2.5 try: except: finally: |
844 if (isinstance(child, TryExcept) and child.fromlineno == self.fromlineno | 857 if (isinstance(child, TryExcept) and child.fromlineno == self.fromlineno |
845 and lineno > self.fromlineno and lineno <= child.tolineno): | 858 and lineno > self.fromlineno and lineno <= child.tolineno): |
846 return child.block_range(lineno) | 859 return child.block_range(lineno) |
847 return self._elsed_block_range(lineno, self.finalbody) | 860 return self._elsed_block_range(lineno, self.finalbody) |
848 | 861 |
849 | 862 |
(...skipping 23 matching lines...) Expand all Loading... |
873 operand = None | 886 operand = None |
874 | 887 |
875 | 888 |
876 class While(BlockRangeMixIn, Statement): | 889 class While(BlockRangeMixIn, Statement): |
877 """class representing a While node""" | 890 """class representing a While node""" |
878 _astroid_fields = ('test', 'body', 'orelse',) | 891 _astroid_fields = ('test', 'body', 'orelse',) |
879 test = None | 892 test = None |
880 body = None | 893 body = None |
881 orelse = None | 894 orelse = None |
882 | 895 |
883 def _blockstart_toline(self): | 896 @cachedproperty |
| 897 def blockstart_tolineno(self): |
884 return self.test.tolineno | 898 return self.test.tolineno |
885 | 899 |
886 def block_range(self, lineno): | 900 def block_range(self, lineno): |
887 """handle block line numbers range for for and while statements""" | 901 """handle block line numbers range for for and while statements""" |
888 return self. _elsed_block_range(lineno, self.orelse) | 902 return self. _elsed_block_range(lineno, self.orelse) |
889 | 903 |
890 | 904 |
891 class With(BlockRangeMixIn, AssignTypeMixin, Statement): | 905 class With(BlockRangeMixIn, AssignTypeMixin, Statement): |
892 """class representing a With node""" | 906 """class representing a With node""" |
893 _astroid_fields = ('items', 'body') | 907 _astroid_fields = ('items', 'body') |
894 items = None | 908 items = None |
895 body = None | 909 body = None |
896 | 910 |
897 def _blockstart_toline(self): | 911 @cachedproperty |
| 912 def blockstart_tolineno(self): |
898 return self.items[-1][0].tolineno | 913 return self.items[-1][0].tolineno |
899 | 914 |
900 def get_children(self): | 915 def get_children(self): |
901 for expr, var in self.items: | 916 for expr, var in self.items: |
902 yield expr | 917 yield expr |
903 if var: | 918 if var: |
904 yield var | 919 yield var |
905 for elt in self.body: | 920 for elt in self.body: |
906 yield elt | 921 yield elt |
907 | 922 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
941 # we should rather recall the builder on this value than returning an empty | 956 # 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 | 957 # node (another option being that const_factory shouldn't be called with som
ething |
943 # not in CONST_CLS) | 958 # not in CONST_CLS) |
944 assert not isinstance(value, NodeNG) | 959 assert not isinstance(value, NodeNG) |
945 try: | 960 try: |
946 return CONST_CLS[value.__class__](value) | 961 return CONST_CLS[value.__class__](value) |
947 except (KeyError, AttributeError): | 962 except (KeyError, AttributeError): |
948 node = EmptyNode() | 963 node = EmptyNode() |
949 node.object = value | 964 node.object = value |
950 return node | 965 return node |
OLD | NEW |