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