| 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 |