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