| 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 """This module contains base classes and functions for the nodes and some | |
| 19 inference utils. | |
| 20 """ | |
| 21 | |
| 22 __docformat__ = "restructuredtext en" | |
| 23 | |
| 24 import sys | |
| 25 from contextlib import contextmanager | |
| 26 | |
| 27 from astroid.exceptions import (InferenceError, AstroidError, NotFoundError, | |
| 28 UnresolvableName, UseInferenceDefault) | |
| 29 | |
| 30 | |
| 31 if sys.version_info >= (3, 0): | |
| 32 BUILTINS = 'builtins' | |
| 33 else: | |
| 34 BUILTINS = '__builtin__' | |
| 35 | |
| 36 | |
| 37 class Proxy(object): | |
| 38 """a simple proxy object""" | |
| 39 | |
| 40 _proxied = None # proxied object may be set by class or by instance | |
| 41 | |
| 42 def __init__(self, proxied=None): | |
| 43 if proxied is not None: | |
| 44 self._proxied = proxied | |
| 45 | |
| 46 def __getattr__(self, name): | |
| 47 if name == '_proxied': | |
| 48 return getattr(self.__class__, '_proxied') | |
| 49 if name in self.__dict__: | |
| 50 return self.__dict__[name] | |
| 51 return getattr(self._proxied, name) | |
| 52 | |
| 53 def infer(self, context=None): | |
| 54 yield self | |
| 55 | |
| 56 | |
| 57 # Inference ################################################################## | |
| 58 | |
| 59 class InferenceContext(object): | |
| 60 __slots__ = ('path', 'lookupname', 'callcontext', 'boundnode') | |
| 61 | |
| 62 def __init__(self, path=None): | |
| 63 if path is None: | |
| 64 self.path = set() | |
| 65 else: | |
| 66 self.path = path | |
| 67 self.lookupname = None | |
| 68 self.callcontext = None | |
| 69 self.boundnode = None | |
| 70 | |
| 71 def push(self, node): | |
| 72 name = self.lookupname | |
| 73 if (node, name) in self.path: | |
| 74 raise StopIteration() | |
| 75 self.path.add((node, name)) | |
| 76 | |
| 77 def clone(self): | |
| 78 # XXX copy lookupname/callcontext ? | |
| 79 clone = InferenceContext(self.path) | |
| 80 clone.callcontext = self.callcontext | |
| 81 clone.boundnode = self.boundnode | |
| 82 return clone | |
| 83 | |
| 84 @contextmanager | |
| 85 def restore_path(self): | |
| 86 path = set(self.path) | |
| 87 yield | |
| 88 self.path = path | |
| 89 | |
| 90 def copy_context(context): | |
| 91 if context is not None: | |
| 92 return context.clone() | |
| 93 else: | |
| 94 return InferenceContext() | |
| 95 | |
| 96 | |
| 97 def _infer_stmts(stmts, context, frame=None): | |
| 98 """return an iterator on statements inferred by each statement in <stmts> | |
| 99 """ | |
| 100 stmt = None | |
| 101 infered = False | |
| 102 if context is not None: | |
| 103 name = context.lookupname | |
| 104 context = context.clone() | |
| 105 else: | |
| 106 name = None | |
| 107 context = InferenceContext() | |
| 108 for stmt in stmts: | |
| 109 if stmt is YES: | |
| 110 yield stmt | |
| 111 infered = True | |
| 112 continue | |
| 113 context.lookupname = stmt._infer_name(frame, name) | |
| 114 try: | |
| 115 for infered in stmt.infer(context): | |
| 116 yield infered | |
| 117 infered = True | |
| 118 except UnresolvableName: | |
| 119 continue | |
| 120 except InferenceError: | |
| 121 yield YES | |
| 122 infered = True | |
| 123 if not infered: | |
| 124 raise InferenceError(str(stmt)) | |
| 125 | |
| 126 | |
| 127 # special inference objects (e.g. may be returned as nodes by .infer()) ####### | |
| 128 | |
| 129 class _Yes(object): | |
| 130 """a yes object""" | |
| 131 def __repr__(self): | |
| 132 return 'YES' | |
| 133 def __getattribute__(self, name): | |
| 134 if name == 'next': | |
| 135 raise AttributeError('next method should not be called') | |
| 136 if name.startswith('__') and name.endswith('__'): | |
| 137 # to avoid inspection pb | |
| 138 return super(_Yes, self).__getattribute__(name) | |
| 139 return self | |
| 140 def __call__(self, *args, **kwargs): | |
| 141 return self | |
| 142 | |
| 143 | |
| 144 YES = _Yes() | |
| 145 | |
| 146 | |
| 147 class Instance(Proxy): | |
| 148 """a special node representing a class instance""" | |
| 149 def getattr(self, name, context=None, lookupclass=True): | |
| 150 try: | |
| 151 values = self._proxied.instance_attr(name, context) | |
| 152 except NotFoundError: | |
| 153 if name == '__class__': | |
| 154 return [self._proxied] | |
| 155 if lookupclass: | |
| 156 # class attributes not available through the instance | |
| 157 # unless they are explicitly defined | |
| 158 if name in ('__name__', '__bases__', '__mro__', '__subclasses__'
): | |
| 159 return self._proxied.local_attr(name) | |
| 160 return self._proxied.getattr(name, context) | |
| 161 raise NotFoundError(name) | |
| 162 # since we've no context information, return matching class members as | |
| 163 # well | |
| 164 if lookupclass: | |
| 165 try: | |
| 166 return values + self._proxied.getattr(name, context) | |
| 167 except NotFoundError: | |
| 168 pass | |
| 169 return values | |
| 170 | |
| 171 def igetattr(self, name, context=None): | |
| 172 """inferred getattr""" | |
| 173 try: | |
| 174 # avoid recursively inferring the same attr on the same class | |
| 175 if context: | |
| 176 context.push((self._proxied, name)) | |
| 177 # XXX frame should be self._proxied, or not ? | |
| 178 get_attr = self.getattr(name, context, lookupclass=False) | |
| 179 return _infer_stmts(self._wrap_attr(get_attr, context), context, | |
| 180 frame=self) | |
| 181 except NotFoundError: | |
| 182 try: | |
| 183 # fallback to class'igetattr since it has some logic to handle | |
| 184 # descriptors | |
| 185 return self._wrap_attr(self._proxied.igetattr(name, context), | |
| 186 context) | |
| 187 except NotFoundError: | |
| 188 raise InferenceError(name) | |
| 189 | |
| 190 def _wrap_attr(self, attrs, context=None): | |
| 191 """wrap bound methods of attrs in a InstanceMethod proxies""" | |
| 192 for attr in attrs: | |
| 193 if isinstance(attr, UnboundMethod): | |
| 194 if BUILTINS + '.property' in attr.decoratornames(): | |
| 195 for infered in attr.infer_call_result(self, context): | |
| 196 yield infered | |
| 197 else: | |
| 198 yield BoundMethod(attr, self) | |
| 199 else: | |
| 200 yield attr | |
| 201 | |
| 202 def infer_call_result(self, caller, context=None): | |
| 203 """infer what a class instance is returning when called""" | |
| 204 infered = False | |
| 205 for node in self._proxied.igetattr('__call__', context): | |
| 206 if node is YES: | |
| 207 continue | |
| 208 for res in node.infer_call_result(caller, context): | |
| 209 infered = True | |
| 210 yield res | |
| 211 if not infered: | |
| 212 raise InferenceError() | |
| 213 | |
| 214 def __repr__(self): | |
| 215 return '<Instance of %s.%s at 0x%s>' % (self._proxied.root().name, | |
| 216 self._proxied.name, | |
| 217 id(self)) | |
| 218 def __str__(self): | |
| 219 return 'Instance of %s.%s' % (self._proxied.root().name, | |
| 220 self._proxied.name) | |
| 221 | |
| 222 def callable(self): | |
| 223 try: | |
| 224 self._proxied.getattr('__call__') | |
| 225 return True | |
| 226 except NotFoundError: | |
| 227 return False | |
| 228 | |
| 229 def pytype(self): | |
| 230 return self._proxied.qname() | |
| 231 | |
| 232 def display_type(self): | |
| 233 return 'Instance of' | |
| 234 | |
| 235 | |
| 236 class UnboundMethod(Proxy): | |
| 237 """a special node representing a method not bound to an instance""" | |
| 238 def __repr__(self): | |
| 239 frame = self._proxied.parent.frame() | |
| 240 return '<%s %s of %s at 0x%s' % (self.__class__.__name__, | |
| 241 self._proxied.name, | |
| 242 frame.qname(), id(self)) | |
| 243 | |
| 244 def is_bound(self): | |
| 245 return False | |
| 246 | |
| 247 def getattr(self, name, context=None): | |
| 248 if name == 'im_func': | |
| 249 return [self._proxied] | |
| 250 return super(UnboundMethod, self).getattr(name, context) | |
| 251 | |
| 252 def igetattr(self, name, context=None): | |
| 253 if name == 'im_func': | |
| 254 return iter((self._proxied,)) | |
| 255 return super(UnboundMethod, self).igetattr(name, context) | |
| 256 | |
| 257 def infer_call_result(self, caller, context): | |
| 258 # If we're unbound method __new__ of builtin object, the result is an | |
| 259 # instance of the class given as first argument. | |
| 260 if (self._proxied.name == '__new__' and | |
| 261 self._proxied.parent.frame().qname() == '%s.object' % BUILTINS): | |
| 262 infer = caller.args[0].infer() if caller.args else [] | |
| 263 return ((x is YES and x or Instance(x)) for x in infer) | |
| 264 return self._proxied.infer_call_result(caller, context) | |
| 265 | |
| 266 | |
| 267 class BoundMethod(UnboundMethod): | |
| 268 """a special node representing a method bound to an instance""" | |
| 269 def __init__(self, proxy, bound): | |
| 270 UnboundMethod.__init__(self, proxy) | |
| 271 self.bound = bound | |
| 272 | |
| 273 def is_bound(self): | |
| 274 return True | |
| 275 | |
| 276 def infer_call_result(self, caller, context): | |
| 277 context = context.clone() | |
| 278 context.boundnode = self.bound | |
| 279 return self._proxied.infer_call_result(caller, context) | |
| 280 | |
| 281 | |
| 282 class Generator(Instance): | |
| 283 """a special node representing a generator. | |
| 284 | |
| 285 Proxied class is set once for all in raw_building. | |
| 286 """ | |
| 287 def callable(self): | |
| 288 return False | |
| 289 | |
| 290 def pytype(self): | |
| 291 return '%s.generator' % BUILTINS | |
| 292 | |
| 293 def display_type(self): | |
| 294 return 'Generator' | |
| 295 | |
| 296 def __repr__(self): | |
| 297 return '<Generator(%s) l.%s at 0x%s>' % (self._proxied.name, self.lineno
, id(self)) | |
| 298 | |
| 299 def __str__(self): | |
| 300 return 'Generator(%s)' % (self._proxied.name) | |
| 301 | |
| 302 | |
| 303 # decorators ################################################################## | |
| 304 | |
| 305 def path_wrapper(func): | |
| 306 """return the given infer function wrapped to handle the path""" | |
| 307 def wrapped(node, context=None, _func=func, **kwargs): | |
| 308 """wrapper function handling context""" | |
| 309 if context is None: | |
| 310 context = InferenceContext() | |
| 311 context.push(node) | |
| 312 yielded = set() | |
| 313 for res in _func(node, context, **kwargs): | |
| 314 # unproxy only true instance, not const, tuple, dict... | |
| 315 if res.__class__ is Instance: | |
| 316 ares = res._proxied | |
| 317 else: | |
| 318 ares = res | |
| 319 if not ares in yielded: | |
| 320 yield res | |
| 321 yielded.add(ares) | |
| 322 return wrapped | |
| 323 | |
| 324 def yes_if_nothing_infered(func): | |
| 325 def wrapper(*args, **kwargs): | |
| 326 infered = False | |
| 327 for node in func(*args, **kwargs): | |
| 328 infered = True | |
| 329 yield node | |
| 330 if not infered: | |
| 331 yield YES | |
| 332 return wrapper | |
| 333 | |
| 334 def raise_if_nothing_infered(func): | |
| 335 def wrapper(*args, **kwargs): | |
| 336 infered = False | |
| 337 for node in func(*args, **kwargs): | |
| 338 infered = True | |
| 339 yield node | |
| 340 if not infered: | |
| 341 raise InferenceError() | |
| 342 return wrapper | |
| 343 | |
| 344 | |
| 345 # Node ###################################################################### | |
| 346 | |
| 347 class NodeNG(object): | |
| 348 """Base Class for all Astroid node classes. | |
| 349 | |
| 350 It represents a node of the new abstract syntax tree. | |
| 351 """ | |
| 352 is_statement = False | |
| 353 optional_assign = False # True for For (and for Comprehension if py <3.0) | |
| 354 is_function = False # True for Function nodes | |
| 355 # attributes below are set by the builder module or by raw factories | |
| 356 lineno = None | |
| 357 fromlineno = None | |
| 358 tolineno = None | |
| 359 col_offset = None | |
| 360 # parent node in the tree | |
| 361 parent = None | |
| 362 # attributes containing child node(s) redefined in most concrete classes: | |
| 363 _astroid_fields = () | |
| 364 # instance specific inference function infer(node, context) | |
| 365 _explicit_inference = None | |
| 366 | |
| 367 def infer(self, context=None, **kwargs): | |
| 368 """main interface to the interface system, return a generator on infered | |
| 369 values. | |
| 370 | |
| 371 If the instance has some explicit inference function set, it will be | |
| 372 called instead of the default interface. | |
| 373 """ | |
| 374 if self._explicit_inference is not None: | |
| 375 # explicit_inference is not bound, give it self explicitly | |
| 376 try: | |
| 377 return self._explicit_inference(self, context, **kwargs) | |
| 378 except UseInferenceDefault: | |
| 379 pass | |
| 380 return self._infer(context, **kwargs) | |
| 381 | |
| 382 def _repr_name(self): | |
| 383 """return self.name or self.attrname or '' for nice representation""" | |
| 384 return getattr(self, 'name', getattr(self, 'attrname', '')) | |
| 385 | |
| 386 def __str__(self): | |
| 387 return '%s(%s)' % (self.__class__.__name__, self._repr_name()) | |
| 388 | |
| 389 def __repr__(self): | |
| 390 return '<%s(%s) l.%s [%s] at 0x%x>' % (self.__class__.__name__, | |
| 391 self._repr_name(), | |
| 392 self.fromlineno, | |
| 393 self.root().name, | |
| 394 id(self)) | |
| 395 | |
| 396 | |
| 397 def accept(self, visitor): | |
| 398 func = getattr(visitor, "visit_" + self.__class__.__name__.lower()) | |
| 399 return func(self) | |
| 400 | |
| 401 def get_children(self): | |
| 402 for field in self._astroid_fields: | |
| 403 attr = getattr(self, field) | |
| 404 if attr is None: | |
| 405 continue | |
| 406 if isinstance(attr, (list, tuple)): | |
| 407 for elt in attr: | |
| 408 yield elt | |
| 409 else: | |
| 410 yield attr | |
| 411 | |
| 412 def last_child(self): | |
| 413 """an optimized version of list(get_children())[-1]""" | |
| 414 for field in self._astroid_fields[::-1]: | |
| 415 attr = getattr(self, field) | |
| 416 if not attr: # None or empty listy / tuple | |
| 417 continue | |
| 418 if isinstance(attr, (list, tuple)): | |
| 419 return attr[-1] | |
| 420 else: | |
| 421 return attr | |
| 422 return None | |
| 423 | |
| 424 def parent_of(self, node): | |
| 425 """return true if i'm a parent of the given node""" | |
| 426 parent = node.parent | |
| 427 while parent is not None: | |
| 428 if self is parent: | |
| 429 return True | |
| 430 parent = parent.parent | |
| 431 return False | |
| 432 | |
| 433 def statement(self): | |
| 434 """return the first parent node marked as statement node""" | |
| 435 if self.is_statement: | |
| 436 return self | |
| 437 return self.parent.statement() | |
| 438 | |
| 439 def frame(self): | |
| 440 """return the first parent frame node (i.e. Module, Function or Class) | |
| 441 """ | |
| 442 return self.parent.frame() | |
| 443 | |
| 444 def scope(self): | |
| 445 """return the first node defining a new scope (i.e. Module, Function, | |
| 446 Class, Lambda but also GenExpr) | |
| 447 """ | |
| 448 return self.parent.scope() | |
| 449 | |
| 450 def root(self): | |
| 451 """return the root node of the tree, (i.e. a Module)""" | |
| 452 if self.parent: | |
| 453 return self.parent.root() | |
| 454 return self | |
| 455 | |
| 456 def child_sequence(self, child): | |
| 457 """search for the right sequence where the child lies in""" | |
| 458 for field in self._astroid_fields: | |
| 459 node_or_sequence = getattr(self, field) | |
| 460 if node_or_sequence is child: | |
| 461 return [node_or_sequence] | |
| 462 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes | |
| 463 if isinstance(node_or_sequence, (tuple, list)) and child in node_or_
sequence: | |
| 464 return node_or_sequence | |
| 465 else: | |
| 466 msg = 'Could not find %s in %s\'s children' | |
| 467 raise AstroidError(msg % (repr(child), repr(self))) | |
| 468 | |
| 469 def locate_child(self, child): | |
| 470 """return a 2-uple (child attribute name, sequence or node)""" | |
| 471 for field in self._astroid_fields: | |
| 472 node_or_sequence = getattr(self, field) | |
| 473 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes | |
| 474 if child is node_or_sequence: | |
| 475 return field, child | |
| 476 if isinstance(node_or_sequence, (tuple, list)) and child in node_or_
sequence: | |
| 477 return field, node_or_sequence | |
| 478 msg = 'Could not find %s in %s\'s children' | |
| 479 raise AstroidError(msg % (repr(child), repr(self))) | |
| 480 # FIXME : should we merge child_sequence and locate_child ? locate_child | |
| 481 # is only used in are_exclusive, child_sequence one time in pylint. | |
| 482 | |
| 483 def next_sibling(self): | |
| 484 """return the next sibling statement""" | |
| 485 return self.parent.next_sibling() | |
| 486 | |
| 487 def previous_sibling(self): | |
| 488 """return the previous sibling statement""" | |
| 489 return self.parent.previous_sibling() | |
| 490 | |
| 491 def nearest(self, nodes): | |
| 492 """return the node which is the nearest before this one in the | |
| 493 given list of nodes | |
| 494 """ | |
| 495 myroot = self.root() | |
| 496 mylineno = self.fromlineno | |
| 497 nearest = None, 0 | |
| 498 for node in nodes: | |
| 499 assert node.root() is myroot, \ | |
| 500 'nodes %s and %s are not from the same module' % (self, node) | |
| 501 lineno = node.fromlineno | |
| 502 if node.fromlineno > mylineno: | |
| 503 break | |
| 504 if lineno > nearest[1]: | |
| 505 nearest = node, lineno | |
| 506 # FIXME: raise an exception if nearest is None ? | |
| 507 return nearest[0] | |
| 508 | |
| 509 def set_line_info(self, lastchild): | |
| 510 if self.lineno is None: | |
| 511 self.fromlineno = self._fixed_source_line() | |
| 512 else: | |
| 513 self.fromlineno = self.lineno | |
| 514 if lastchild is None: | |
| 515 self.tolineno = self.fromlineno | |
| 516 else: | |
| 517 self.tolineno = lastchild.tolineno | |
| 518 return | |
| 519 # TODO / FIXME: | |
| 520 assert self.fromlineno is not None, self | |
| 521 assert self.tolineno is not None, self | |
| 522 | |
| 523 def _fixed_source_line(self): | |
| 524 """return the line number where the given node appears | |
| 525 | |
| 526 we need this method since not all nodes have the lineno attribute | |
| 527 correctly set... | |
| 528 """ | |
| 529 line = self.lineno | |
| 530 _node = self | |
| 531 try: | |
| 532 while line is None: | |
| 533 _node = _node.get_children().next() | |
| 534 line = _node.lineno | |
| 535 except StopIteration: | |
| 536 _node = self.parent | |
| 537 while _node and line is None: | |
| 538 line = _node.lineno | |
| 539 _node = _node.parent | |
| 540 return line | |
| 541 | |
| 542 def block_range(self, lineno): | |
| 543 """handle block line numbers range for non block opening statements | |
| 544 """ | |
| 545 return lineno, self.tolineno | |
| 546 | |
| 547 def set_local(self, name, stmt): | |
| 548 """delegate to a scoped parent handling a locals dictionary""" | |
| 549 self.parent.set_local(name, stmt) | |
| 550 | |
| 551 def nodes_of_class(self, klass, skip_klass=None): | |
| 552 """return an iterator on nodes which are instance of the given class(es) | |
| 553 | |
| 554 klass may be a class object or a tuple of class objects | |
| 555 """ | |
| 556 if isinstance(self, klass): | |
| 557 yield self | |
| 558 for child_node in self.get_children(): | |
| 559 if skip_klass is not None and isinstance(child_node, skip_klass): | |
| 560 continue | |
| 561 for matching in child_node.nodes_of_class(klass, skip_klass): | |
| 562 yield matching | |
| 563 | |
| 564 def _infer_name(self, frame, name): | |
| 565 # overridden for From, Import, Global, TryExcept and Arguments | |
| 566 return None | |
| 567 | |
| 568 def _infer(self, context=None): | |
| 569 """we don't know how to resolve a statement by default""" | |
| 570 # this method is overridden by most concrete classes | |
| 571 raise InferenceError(self.__class__.__name__) | |
| 572 | |
| 573 def infered(self): | |
| 574 '''return list of infered values for a more simple inference usage''' | |
| 575 return list(self.infer()) | |
| 576 | |
| 577 def instanciate_class(self): | |
| 578 """instanciate a node if it is a Class node, else return self""" | |
| 579 return self | |
| 580 | |
| 581 def has_base(self, node): | |
| 582 return False | |
| 583 | |
| 584 def callable(self): | |
| 585 return False | |
| 586 | |
| 587 def eq(self, value): | |
| 588 return False | |
| 589 | |
| 590 def as_string(self): | |
| 591 from astroid.as_string import to_code | |
| 592 return to_code(self) | |
| 593 | |
| 594 def repr_tree(self, ids=False): | |
| 595 from astroid.as_string import dump | |
| 596 return dump(self) | |
| 597 | |
| 598 | |
| 599 class Statement(NodeNG): | |
| 600 """Statement node adding a few attributes""" | |
| 601 is_statement = True | |
| 602 | |
| 603 def next_sibling(self): | |
| 604 """return the next sibling statement""" | |
| 605 stmts = self.parent.child_sequence(self) | |
| 606 index = stmts.index(self) | |
| 607 try: | |
| 608 return stmts[index +1] | |
| 609 except IndexError: | |
| 610 pass | |
| 611 | |
| 612 def previous_sibling(self): | |
| 613 """return the previous sibling statement""" | |
| 614 stmts = self.parent.child_sequence(self) | |
| 615 index = stmts.index(self) | |
| 616 if index >= 1: | |
| 617 return stmts[index -1] | |
| OLD | NEW |