| OLD | NEW |
| (Empty) | |
| 1 # copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
| 2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
| 3 # copyright 2003-2010 Sylvain Thenault, all rights reserved. |
| 4 # contact mailto:thenault@gmail.com |
| 5 # |
| 6 # This file is part of logilab-astng. |
| 7 # |
| 8 # logilab-astng is free software: you can redistribute it and/or modify it |
| 9 # under the terms of the GNU Lesser General Public License as published by the |
| 10 # Free Software Foundation, either version 2.1 of the License, or (at your |
| 11 # option) any later version. |
| 12 # |
| 13 # logilab-astng is distributed in the hope that it will be useful, but |
| 14 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 15 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License |
| 16 # for more details. |
| 17 # |
| 18 # You should have received a copy of the GNU Lesser General Public License along |
| 19 # with logilab-astng. If not, see <http://www.gnu.org/licenses/>. |
| 20 """This module contains the classes for "scoped" node, i.e. which are opening a |
| 21 new local scope in the language definition : Module, Class, Function (and |
| 22 Lambda, GenExpr, DictComp and SetComp to some extent). |
| 23 """ |
| 24 from __future__ import with_statement |
| 25 |
| 26 __doctype__ = "restructuredtext en" |
| 27 |
| 28 import sys |
| 29 from itertools import chain |
| 30 |
| 31 from logilab.common.compat import builtins |
| 32 from logilab.common.decorators import cached |
| 33 |
| 34 from logilab.astng import BUILTINS_MODULE |
| 35 from logilab.astng.exceptions import NotFoundError, NoDefault, \ |
| 36 ASTNGBuildingException, InferenceError |
| 37 from logilab.astng.node_classes import Const, DelName, DelAttr, \ |
| 38 Dict, From, List, Name, Pass, Raise, Return, Tuple, Yield, \ |
| 39 are_exclusive, LookupMixIn, const_factory as cf, unpack_infer |
| 40 from logilab.astng.bases import NodeNG, InferenceContext, Instance,\ |
| 41 YES, Generator, UnboundMethod, BoundMethod, _infer_stmts, copy_context, \ |
| 42 BUILTINS_NAME |
| 43 from logilab.astng.mixins import FilterStmtsMixin |
| 44 from logilab.astng.bases import Statement |
| 45 from logilab.astng.manager import ASTNGManager |
| 46 |
| 47 |
| 48 def remove_nodes(func, cls): |
| 49 def wrapper(*args, **kwargs): |
| 50 nodes = [n for n in func(*args, **kwargs) if not isinstance(n, cls)] |
| 51 if not nodes: |
| 52 raise NotFoundError() |
| 53 return nodes |
| 54 return wrapper |
| 55 |
| 56 |
| 57 def function_to_method(n, klass): |
| 58 if isinstance(n, Function): |
| 59 if n.type == 'classmethod': |
| 60 return BoundMethod(n, klass) |
| 61 if n.type != 'staticmethod': |
| 62 return UnboundMethod(n) |
| 63 return n |
| 64 |
| 65 def std_special_attributes(self, name, add_locals=True): |
| 66 if add_locals: |
| 67 locals = self.locals |
| 68 else: |
| 69 locals = {} |
| 70 if name == '__name__': |
| 71 return [cf(self.name)] + locals.get(name, []) |
| 72 if name == '__doc__': |
| 73 return [cf(self.doc)] + locals.get(name, []) |
| 74 if name == '__dict__': |
| 75 return [Dict()] + locals.get(name, []) |
| 76 raise NotFoundError(name) |
| 77 |
| 78 MANAGER = ASTNGManager() |
| 79 def builtin_lookup(name): |
| 80 """lookup a name into the builtin module |
| 81 return the list of matching statements and the astng for the builtin |
| 82 module |
| 83 """ |
| 84 builtin_astng = MANAGER.astng_from_module(builtins) |
| 85 if name == '__dict__': |
| 86 return builtin_astng, () |
| 87 try: |
| 88 stmts = builtin_astng.locals[name] |
| 89 except KeyError: |
| 90 stmts = () |
| 91 return builtin_astng, stmts |
| 92 |
| 93 |
| 94 # TODO move this Mixin to mixins.py; problem: 'Function' in _scope_lookup |
| 95 class LocalsDictNodeNG(LookupMixIn, NodeNG): |
| 96 """ this class provides locals handling common to Module, Function |
| 97 and Class nodes, including a dict like interface for direct access |
| 98 to locals information |
| 99 """ |
| 100 |
| 101 # attributes below are set by the builder module or by raw factories |
| 102 |
| 103 # dictionary of locals with name as key and node defining the local as |
| 104 # value |
| 105 |
| 106 def qname(self): |
| 107 """return the 'qualified' name of the node, eg module.name, |
| 108 module.class.name ... |
| 109 """ |
| 110 if self.parent is None: |
| 111 return self.name |
| 112 return '%s.%s' % (self.parent.frame().qname(), self.name) |
| 113 |
| 114 def frame(self): |
| 115 """return the first parent frame node (i.e. Module, Function or Class) |
| 116 """ |
| 117 return self |
| 118 |
| 119 def scope(self): |
| 120 """return the first node defining a new scope (i.e. Module, |
| 121 Function, Class, Lambda but also GenExpr, DictComp and SetComp) |
| 122 """ |
| 123 return self |
| 124 |
| 125 |
| 126 def _scope_lookup(self, node, name, offset=0): |
| 127 """XXX method for interfacing the scope lookup""" |
| 128 try: |
| 129 stmts = node._filter_stmts(self.locals[name], self, offset) |
| 130 except KeyError: |
| 131 stmts = () |
| 132 if stmts: |
| 133 return self, stmts |
| 134 if self.parent: # i.e. not Module |
| 135 # nested scope: if parent scope is a function, that's fine |
| 136 # else jump to the module |
| 137 pscope = self.parent.scope() |
| 138 if not pscope.is_function: |
| 139 pscope = pscope.root() |
| 140 return pscope.scope_lookup(node, name) |
| 141 return builtin_lookup(name) # Module |
| 142 |
| 143 |
| 144 |
| 145 def set_local(self, name, stmt): |
| 146 """define <name> in locals (<stmt> is the node defining the name) |
| 147 if the node is a Module node (i.e. has globals), add the name to |
| 148 globals |
| 149 |
| 150 if the name is already defined, ignore it |
| 151 """ |
| 152 #assert not stmt in self.locals.get(name, ()), (self, stmt) |
| 153 self.locals.setdefault(name, []).append(stmt) |
| 154 |
| 155 __setitem__ = set_local |
| 156 |
| 157 def _append_node(self, child): |
| 158 """append a child, linking it in the tree""" |
| 159 self.body.append(child) |
| 160 child.parent = self |
| 161 |
| 162 def add_local_node(self, child_node, name=None): |
| 163 """append a child which should alter locals to the given node""" |
| 164 if name != '__class__': |
| 165 # add __class__ node as a child will cause infinite recursion later! |
| 166 self._append_node(child_node) |
| 167 self.set_local(name or child_node.name, child_node) |
| 168 |
| 169 |
| 170 def __getitem__(self, item): |
| 171 """method from the `dict` interface returning the first node |
| 172 associated with the given name in the locals dictionary |
| 173 |
| 174 :type item: str |
| 175 :param item: the name of the locally defined object |
| 176 :raises KeyError: if the name is not defined |
| 177 """ |
| 178 return self.locals[item][0] |
| 179 |
| 180 def __iter__(self): |
| 181 """method from the `dict` interface returning an iterator on |
| 182 `self.keys()` |
| 183 """ |
| 184 return iter(self.keys()) |
| 185 |
| 186 def keys(self): |
| 187 """method from the `dict` interface returning a tuple containing |
| 188 locally defined names |
| 189 """ |
| 190 return self.locals.keys() |
| 191 |
| 192 def values(self): |
| 193 """method from the `dict` interface returning a tuple containing |
| 194 locally defined nodes which are instance of `Function` or `Class` |
| 195 """ |
| 196 return [self[key] for key in self.keys()] |
| 197 |
| 198 def items(self): |
| 199 """method from the `dict` interface returning a list of tuple |
| 200 containing each locally defined name with its associated node, |
| 201 which is an instance of `Function` or `Class` |
| 202 """ |
| 203 return zip(self.keys(), self.values()) |
| 204 |
| 205 |
| 206 def __contains__(self, name): |
| 207 return name in self.locals |
| 208 has_key = __contains__ |
| 209 |
| 210 # Module ##################################################################### |
| 211 |
| 212 class Module(LocalsDictNodeNG): |
| 213 _astng_fields = ('body',) |
| 214 |
| 215 fromlineno = 0 |
| 216 lineno = 0 |
| 217 |
| 218 # attributes below are set by the builder module or by raw factories |
| 219 |
| 220 # the file from which as been extracted the astng representation. It may |
| 221 # be None if the representation has been built from a built-in module |
| 222 file = None |
| 223 # encoding of python source file, so we can get unicode out of it (python2 |
| 224 # only) |
| 225 file_encoding = None |
| 226 # the module name |
| 227 name = None |
| 228 # boolean for astng built from source (i.e. ast) |
| 229 pure_python = None |
| 230 # boolean for package module |
| 231 package = None |
| 232 # dictionary of globals with name as key and node defining the global |
| 233 # as value |
| 234 globals = None |
| 235 |
| 236 # names of python special attributes (handled by getattr impl.) |
| 237 special_attributes = set(('__name__', '__doc__', '__file__', '__path__', |
| 238 '__dict__')) |
| 239 # names of module attributes available through the global scope |
| 240 scope_attrs = set(('__name__', '__doc__', '__file__', '__path__')) |
| 241 |
| 242 def __init__(self, name, doc, pure_python=True): |
| 243 self.name = name |
| 244 self.doc = doc |
| 245 self.pure_python = pure_python |
| 246 self.locals = self.globals = {} |
| 247 self.body = [] |
| 248 |
| 249 @property |
| 250 def file_stream(self): |
| 251 if self.file is not None: |
| 252 return open(self.file) |
| 253 return None |
| 254 |
| 255 def block_range(self, lineno): |
| 256 """return block line numbers. |
| 257 |
| 258 start from the beginning whatever the given lineno |
| 259 """ |
| 260 return self.fromlineno, self.tolineno |
| 261 |
| 262 def scope_lookup(self, node, name, offset=0): |
| 263 if name in self.scope_attrs and not name in self.locals: |
| 264 try: |
| 265 return self, self.getattr(name) |
| 266 except NotFoundError: |
| 267 return self, () |
| 268 return self._scope_lookup(node, name, offset) |
| 269 |
| 270 def pytype(self): |
| 271 return '%s.module' % BUILTINS_MODULE |
| 272 |
| 273 def display_type(self): |
| 274 return 'Module' |
| 275 |
| 276 def getattr(self, name, context=None, ignore_locals=False): |
| 277 if name in self.special_attributes: |
| 278 if name == '__file__': |
| 279 return [cf(self.file)] + self.locals.get(name, []) |
| 280 if name == '__path__' and self.package: |
| 281 return [List()] + self.locals.get(name, []) |
| 282 return std_special_attributes(self, name) |
| 283 if not ignore_locals and name in self.locals: |
| 284 return self.locals[name] |
| 285 if self.package: |
| 286 try: |
| 287 return [self.import_module(name, relative_only=True)] |
| 288 except ASTNGBuildingException: |
| 289 raise NotFoundError(name) |
| 290 except Exception:# XXX pylint tests never pass here; do we need it? |
| 291 import traceback |
| 292 traceback.print_exc() |
| 293 raise NotFoundError(name) |
| 294 getattr = remove_nodes(getattr, DelName) |
| 295 |
| 296 def igetattr(self, name, context=None): |
| 297 """inferred getattr""" |
| 298 # set lookup name since this is necessary to infer on import nodes for |
| 299 # instance |
| 300 context = copy_context(context) |
| 301 context.lookupname = name |
| 302 try: |
| 303 return _infer_stmts(self.getattr(name, context), context, frame=self
) |
| 304 except NotFoundError: |
| 305 raise InferenceError(name) |
| 306 |
| 307 def fully_defined(self): |
| 308 """return True if this module has been built from a .py file |
| 309 and so contains a complete representation including the code |
| 310 """ |
| 311 return self.file is not None and self.file.endswith('.py') |
| 312 |
| 313 def statement(self): |
| 314 """return the first parent node marked as statement node |
| 315 consider a module as a statement... |
| 316 """ |
| 317 return self |
| 318 |
| 319 def previous_sibling(self): |
| 320 """module has no sibling""" |
| 321 return |
| 322 |
| 323 def next_sibling(self): |
| 324 """module has no sibling""" |
| 325 return |
| 326 |
| 327 if sys.version_info < (2, 8): |
| 328 def absolute_import_activated(self): |
| 329 for stmt in self.locals.get('absolute_import', ()): |
| 330 if isinstance(stmt, From) and stmt.modname == '__future__': |
| 331 return True |
| 332 return False |
| 333 else: |
| 334 absolute_import_activated = lambda self: True |
| 335 |
| 336 def import_module(self, modname, relative_only=False, level=None): |
| 337 """import the given module considering self as context""" |
| 338 if relative_only and level is None: |
| 339 level = 0 |
| 340 absmodname = self.relative_to_absolute_name(modname, level) |
| 341 try: |
| 342 return MANAGER.astng_from_module_name(absmodname) |
| 343 except ASTNGBuildingException: |
| 344 # we only want to import a sub module or package of this module, |
| 345 # skip here |
| 346 if relative_only: |
| 347 raise |
| 348 return MANAGER.astng_from_module_name(modname) |
| 349 |
| 350 def relative_to_absolute_name(self, modname, level): |
| 351 """return the absolute module name for a relative import. |
| 352 |
| 353 The relative import can be implicit or explicit. |
| 354 """ |
| 355 # XXX this returns non sens when called on an absolute import |
| 356 # like 'pylint.checkers.logilab.astng.utils' |
| 357 # XXX doesn't return absolute name if self.name isn't absolute name |
| 358 if self.absolute_import_activated() and level is None: |
| 359 return modname |
| 360 if level: |
| 361 if self.package: |
| 362 level = level - 1 |
| 363 package_name = self.name.rsplit('.', level)[0] |
| 364 elif self.package: |
| 365 package_name = self.name |
| 366 else: |
| 367 package_name = self.name.rsplit('.', 1)[0] |
| 368 if package_name: |
| 369 if not modname: |
| 370 return package_name |
| 371 return '%s.%s' % (package_name, modname) |
| 372 return modname |
| 373 |
| 374 |
| 375 def wildcard_import_names(self): |
| 376 """return the list of imported names when this module is 'wildcard |
| 377 imported' |
| 378 |
| 379 It doesn't include the '__builtins__' name which is added by the |
| 380 current CPython implementation of wildcard imports. |
| 381 """ |
| 382 # take advantage of a living module if it exists |
| 383 try: |
| 384 living = sys.modules[self.name] |
| 385 except KeyError: |
| 386 pass |
| 387 else: |
| 388 try: |
| 389 return living.__all__ |
| 390 except AttributeError: |
| 391 return [name for name in living.__dict__.keys() |
| 392 if not name.startswith('_')] |
| 393 # else lookup the astng |
| 394 # |
| 395 # We separate the different steps of lookup in try/excepts |
| 396 # to avoid catching too many Exceptions |
| 397 # However, we can not analyse dynamically constructed __all__ |
| 398 try: |
| 399 all = self['__all__'] |
| 400 except KeyError: |
| 401 return [name for name in self.keys() if not name.startswith('_')] |
| 402 try: |
| 403 explicit = all.assigned_stmts().next() |
| 404 except InferenceError: |
| 405 return [name for name in self.keys() if not name.startswith('_')] |
| 406 except AttributeError: |
| 407 # not an assignment node |
| 408 # XXX infer? |
| 409 return [name for name in self.keys() if not name.startswith('_')] |
| 410 try: |
| 411 # should be a Tuple/List of constant string / 1 string not allowed |
| 412 return [const.value for const in explicit.elts] |
| 413 except AttributeError: |
| 414 return [name for name in self.keys() if not name.startswith('_')] |
| 415 |
| 416 |
| 417 class ComprehensionScope(LocalsDictNodeNG): |
| 418 def frame(self): |
| 419 return self.parent.frame() |
| 420 |
| 421 scope_lookup = LocalsDictNodeNG._scope_lookup |
| 422 |
| 423 |
| 424 class GenExpr(ComprehensionScope): |
| 425 _astng_fields = ('elt', 'generators') |
| 426 |
| 427 def __init__(self): |
| 428 self.locals = {} |
| 429 self.elt = None |
| 430 self.generators = [] |
| 431 |
| 432 |
| 433 class DictComp(ComprehensionScope): |
| 434 _astng_fields = ('key', 'value', 'generators') |
| 435 |
| 436 def __init__(self): |
| 437 self.locals = {} |
| 438 self.key = None |
| 439 self.value = None |
| 440 self.generators = [] |
| 441 |
| 442 |
| 443 class SetComp(ComprehensionScope): |
| 444 _astng_fields = ('elt', 'generators') |
| 445 |
| 446 def __init__(self): |
| 447 self.locals = {} |
| 448 self.elt = None |
| 449 self.generators = [] |
| 450 |
| 451 |
| 452 class _ListComp(NodeNG): |
| 453 """class representing a ListComp node""" |
| 454 _astng_fields = ('elt', 'generators') |
| 455 elt = None |
| 456 generators = None |
| 457 |
| 458 if sys.version_info >= (3, 0): |
| 459 class ListComp(_ListComp, ComprehensionScope): |
| 460 """class representing a ListComp node""" |
| 461 def __init__(self): |
| 462 self.locals = {} |
| 463 else: |
| 464 class ListComp(_ListComp): |
| 465 """class representing a ListComp node""" |
| 466 |
| 467 # Function ################################################################### |
| 468 |
| 469 |
| 470 class Lambda(LocalsDictNodeNG, FilterStmtsMixin): |
| 471 _astng_fields = ('args', 'body',) |
| 472 name = '<lambda>' |
| 473 |
| 474 # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod' |
| 475 type = 'function' |
| 476 |
| 477 def __init__(self): |
| 478 self.locals = {} |
| 479 self.args = [] |
| 480 self.body = [] |
| 481 |
| 482 def pytype(self): |
| 483 if 'method' in self.type: |
| 484 return '%s.instancemethod' % BUILTINS_MODULE |
| 485 return '%s.function' % BUILTINS_MODULE |
| 486 |
| 487 def display_type(self): |
| 488 if 'method' in self.type: |
| 489 return 'Method' |
| 490 return 'Function' |
| 491 |
| 492 def callable(self): |
| 493 return True |
| 494 |
| 495 def argnames(self): |
| 496 """return a list of argument names""" |
| 497 if self.args.args: # maybe None with builtin functions |
| 498 names = _rec_get_names(self.args.args) |
| 499 else: |
| 500 names = [] |
| 501 if self.args.vararg: |
| 502 names.append(self.args.vararg) |
| 503 if self.args.kwarg: |
| 504 names.append(self.args.kwarg) |
| 505 return names |
| 506 |
| 507 def infer_call_result(self, caller, context=None): |
| 508 """infer what a function is returning when called""" |
| 509 return self.body.infer(context) |
| 510 |
| 511 def scope_lookup(self, node, name, offset=0): |
| 512 if node in self.args.defaults: |
| 513 frame = self.parent.frame() |
| 514 # line offset to avoid that def func(f=func) resolve the default |
| 515 # value to the defined function |
| 516 offset = -1 |
| 517 else: |
| 518 # check this is not used in function decorators |
| 519 frame = self |
| 520 return frame._scope_lookup(node, name, offset) |
| 521 |
| 522 |
| 523 class Function(Statement, Lambda): |
| 524 _astng_fields = ('decorators', 'args', 'body') |
| 525 |
| 526 special_attributes = set(('__name__', '__doc__', '__dict__')) |
| 527 is_function = True |
| 528 # attributes below are set by the builder module or by raw factories |
| 529 blockstart_tolineno = None |
| 530 decorators = None |
| 531 |
| 532 def __init__(self, name, doc): |
| 533 self.locals = {} |
| 534 self.args = [] |
| 535 self.body = [] |
| 536 self.decorators = None |
| 537 self.name = name |
| 538 self.doc = doc |
| 539 self.extra_decorators = [] |
| 540 self.instance_attrs = {} |
| 541 |
| 542 def set_line_info(self, lastchild): |
| 543 self.fromlineno = self.lineno |
| 544 # lineno is the line number of the first decorator, we want the def stat
ement lineno |
| 545 if self.decorators is not None: |
| 546 self.fromlineno += sum(node.tolineno - node.lineno + 1 |
| 547 for node in self.decorators.nodes) |
| 548 self.tolineno = lastchild.tolineno |
| 549 self.blockstart_tolineno = self.args.tolineno |
| 550 |
| 551 def block_range(self, lineno): |
| 552 """return block line numbers. |
| 553 |
| 554 start from the "def" position whatever the given lineno |
| 555 """ |
| 556 return self.fromlineno, self.tolineno |
| 557 |
| 558 def getattr(self, name, context=None): |
| 559 """this method doesn't look in the instance_attrs dictionary since it's |
| 560 done by an Instance proxy at inference time. |
| 561 """ |
| 562 if name == '__module__': |
| 563 return [cf(self.root().qname())] |
| 564 if name in self.instance_attrs: |
| 565 return self.instance_attrs[name] |
| 566 return std_special_attributes(self, name, False) |
| 567 |
| 568 def is_method(self): |
| 569 """return true if the function node should be considered as a method""" |
| 570 # check we are defined in a Class, because this is usually expected |
| 571 # (e.g. pylint...) when is_method() return True |
| 572 return self.type != 'function' and isinstance(self.parent.frame(), Class
) |
| 573 |
| 574 def decoratornames(self): |
| 575 """return a list of decorator qualified names""" |
| 576 result = set() |
| 577 decoratornodes = [] |
| 578 if self.decorators is not None: |
| 579 decoratornodes += self.decorators.nodes |
| 580 decoratornodes += self.extra_decorators |
| 581 for decnode in decoratornodes: |
| 582 for infnode in decnode.infer(): |
| 583 result.add(infnode.qname()) |
| 584 return result |
| 585 decoratornames = cached(decoratornames) |
| 586 |
| 587 def is_bound(self): |
| 588 """return true if the function is bound to an Instance or a class""" |
| 589 return self.type == 'classmethod' |
| 590 |
| 591 def is_abstract(self, pass_is_abstract=True): |
| 592 """return true if the method is abstract |
| 593 It's considered as abstract if the only statement is a raise of |
| 594 NotImplementError, or, if pass_is_abstract, a pass statement |
| 595 """ |
| 596 for child_node in self.body: |
| 597 if isinstance(child_node, Raise): |
| 598 if child_node.raises_not_implemented(): |
| 599 return True |
| 600 if pass_is_abstract and isinstance(child_node, Pass): |
| 601 return True |
| 602 return False |
| 603 # empty function is the same as function with a single "pass" statement |
| 604 if pass_is_abstract: |
| 605 return True |
| 606 |
| 607 def is_generator(self): |
| 608 """return true if this is a generator function""" |
| 609 # XXX should be flagged, not computed |
| 610 try: |
| 611 return self.nodes_of_class(Yield, skip_klass=Function).next() |
| 612 except StopIteration: |
| 613 return False |
| 614 |
| 615 def infer_call_result(self, caller, context=None): |
| 616 """infer what a function is returning when called""" |
| 617 if self.is_generator(): |
| 618 yield Generator(self) |
| 619 return |
| 620 returns = self.nodes_of_class(Return, skip_klass=Function) |
| 621 for returnnode in returns: |
| 622 if returnnode.value is None: |
| 623 yield Const(None) |
| 624 else: |
| 625 try: |
| 626 for infered in returnnode.value.infer(context): |
| 627 yield infered |
| 628 except InferenceError: |
| 629 yield YES |
| 630 |
| 631 |
| 632 def _rec_get_names(args, names=None): |
| 633 """return a list of all argument names""" |
| 634 if names is None: |
| 635 names = [] |
| 636 for arg in args: |
| 637 if isinstance(arg, Tuple): |
| 638 _rec_get_names(arg.elts, names) |
| 639 else: |
| 640 names.append(arg.name) |
| 641 return names |
| 642 |
| 643 |
| 644 # Class ###################################################################### |
| 645 |
| 646 def _class_type(klass, ancestors=None): |
| 647 """return a Class node type to differ metaclass, interface and exception |
| 648 from 'regular' classes |
| 649 """ |
| 650 # XXX we have to store ancestors in case we have a ancestor loop |
| 651 if klass._type is not None: |
| 652 return klass._type |
| 653 if klass.name == 'type': |
| 654 klass._type = 'metaclass' |
| 655 elif klass.name.endswith('Interface'): |
| 656 klass._type = 'interface' |
| 657 elif klass.name.endswith('Exception'): |
| 658 klass._type = 'exception' |
| 659 else: |
| 660 if ancestors is None: |
| 661 ancestors = set() |
| 662 if klass in ancestors: |
| 663 # XXX we are in loop ancestors, and have found no type |
| 664 klass._type = 'class' |
| 665 return 'class' |
| 666 ancestors.add(klass) |
| 667 # print >> sys.stderr, '_class_type', repr(klass) |
| 668 for base in klass.ancestors(recurs=False): |
| 669 if _class_type(base, ancestors) != 'class': |
| 670 klass._type = base.type |
| 671 break |
| 672 if klass._type is None: |
| 673 klass._type = 'class' |
| 674 return klass._type |
| 675 |
| 676 def _iface_hdlr(iface_node): |
| 677 """a handler function used by interfaces to handle suspicious |
| 678 interface nodes |
| 679 """ |
| 680 return True |
| 681 |
| 682 |
| 683 class Class(Statement, LocalsDictNodeNG, FilterStmtsMixin): |
| 684 |
| 685 # some of the attributes below are set by the builder module or |
| 686 # by a raw factories |
| 687 |
| 688 # a dictionary of class instances attributes |
| 689 _astng_fields = ('decorators', 'bases', 'body') # name |
| 690 |
| 691 decorators = None |
| 692 special_attributes = set(('__name__', '__doc__', '__dict__', '__module__', |
| 693 '__bases__', '__mro__', '__subclasses__')) |
| 694 blockstart_tolineno = None |
| 695 |
| 696 _type = None |
| 697 type = property(_class_type, |
| 698 doc="class'type, possible values are 'class' | " |
| 699 "'metaclass' | 'interface' | 'exception'") |
| 700 |
| 701 def __init__(self, name, doc): |
| 702 self.instance_attrs = {} |
| 703 self.locals = {} |
| 704 self.bases = [] |
| 705 self.body = [] |
| 706 self.name = name |
| 707 self.doc = doc |
| 708 |
| 709 def _newstyle_impl(self, context=None): |
| 710 if context is None: |
| 711 context = InferenceContext() |
| 712 if self._newstyle is not None: |
| 713 return self._newstyle |
| 714 for base in self.ancestors(recurs=False, context=context): |
| 715 if base._newstyle_impl(context): |
| 716 self._newstyle = True |
| 717 break |
| 718 if self._newstyle is None: |
| 719 self._newstyle = False |
| 720 return self._newstyle |
| 721 |
| 722 _newstyle = None |
| 723 newstyle = property(_newstyle_impl, |
| 724 doc="boolean indicating if it's a new style class" |
| 725 "or not") |
| 726 |
| 727 def set_line_info(self, lastchild): |
| 728 self.fromlineno = self.lineno |
| 729 self.blockstart_tolineno = self.bases and self.bases[-1].tolineno or sel
f.fromlineno |
| 730 if lastchild is not None: |
| 731 self.tolineno = lastchild.tolineno |
| 732 # else this is a class with only a docstring, then tolineno is (should b
e) already ok |
| 733 |
| 734 def block_range(self, lineno): |
| 735 """return block line numbers. |
| 736 |
| 737 start from the "class" position whatever the given lineno |
| 738 """ |
| 739 return self.fromlineno, self.tolineno |
| 740 |
| 741 def pytype(self): |
| 742 if self.newstyle: |
| 743 return '%s.type' % BUILTINS_MODULE |
| 744 return '%s.classobj' % BUILTINS_MODULE |
| 745 |
| 746 def display_type(self): |
| 747 return 'Class' |
| 748 |
| 749 def callable(self): |
| 750 return True |
| 751 |
| 752 def infer_call_result(self, caller, context=None): |
| 753 """infer what a class is returning when called""" |
| 754 yield Instance(self) |
| 755 |
| 756 def scope_lookup(self, node, name, offset=0): |
| 757 if node in self.bases: |
| 758 frame = self.parent.frame() |
| 759 # line offset to avoid that class A(A) resolve the ancestor to |
| 760 # the defined class |
| 761 offset = -1 |
| 762 else: |
| 763 frame = self |
| 764 return frame._scope_lookup(node, name, offset) |
| 765 |
| 766 # list of parent class as a list of string (i.e. names as they appear |
| 767 # in the class definition) XXX bw compat |
| 768 def basenames(self): |
| 769 return [bnode.as_string() for bnode in self.bases] |
| 770 basenames = property(basenames) |
| 771 |
| 772 def ancestors(self, recurs=True, context=None): |
| 773 """return an iterator on the node base classes in a prefixed |
| 774 depth first order |
| 775 |
| 776 :param recurs: |
| 777 boolean indicating if it should recurse or return direct |
| 778 ancestors only |
| 779 """ |
| 780 # FIXME: should be possible to choose the resolution order |
| 781 # XXX inference make infinite loops possible here (see BaseTransformer |
| 782 # manipulation in the builder module for instance) |
| 783 yielded = set([self]) |
| 784 if context is None: |
| 785 context = InferenceContext() |
| 786 for stmt in self.bases: |
| 787 with context.restore_path(): |
| 788 try: |
| 789 for baseobj in stmt.infer(context): |
| 790 if not isinstance(baseobj, Class): |
| 791 # duh ? |
| 792 continue |
| 793 if baseobj in yielded: |
| 794 continue # cf xxx above |
| 795 yielded.add(baseobj) |
| 796 yield baseobj |
| 797 if recurs: |
| 798 for grandpa in baseobj.ancestors(True, context): |
| 799 if grandpa in yielded: |
| 800 continue # cf xxx above |
| 801 yielded.add(grandpa) |
| 802 yield grandpa |
| 803 except InferenceError: |
| 804 # XXX log error ? |
| 805 continue |
| 806 |
| 807 def local_attr_ancestors(self, name, context=None): |
| 808 """return an iterator on astng representation of parent classes |
| 809 which have <name> defined in their locals |
| 810 """ |
| 811 for astng in self.ancestors(context=context): |
| 812 if name in astng: |
| 813 yield astng |
| 814 |
| 815 def instance_attr_ancestors(self, name, context=None): |
| 816 """return an iterator on astng representation of parent classes |
| 817 which have <name> defined in their instance attribute dictionary |
| 818 """ |
| 819 for astng in self.ancestors(context=context): |
| 820 if name in astng.instance_attrs: |
| 821 yield astng |
| 822 |
| 823 def has_base(self, node): |
| 824 return node in self.bases |
| 825 |
| 826 def local_attr(self, name, context=None): |
| 827 """return the list of assign node associated to name in this class |
| 828 locals or in its parents |
| 829 |
| 830 :raises `NotFoundError`: |
| 831 if no attribute with this name has been find in this class or |
| 832 its parent classes |
| 833 """ |
| 834 try: |
| 835 return self.locals[name] |
| 836 except KeyError: |
| 837 # get if from the first parent implementing it if any |
| 838 for class_node in self.local_attr_ancestors(name, context): |
| 839 return class_node.locals[name] |
| 840 raise NotFoundError(name) |
| 841 local_attr = remove_nodes(local_attr, DelAttr) |
| 842 |
| 843 def instance_attr(self, name, context=None): |
| 844 """return the astng nodes associated to name in this class instance |
| 845 attributes dictionary and in its parents |
| 846 |
| 847 :raises `NotFoundError`: |
| 848 if no attribute with this name has been find in this class or |
| 849 its parent classes |
| 850 """ |
| 851 values = self.instance_attrs.get(name, []) |
| 852 # get all values from parents |
| 853 for class_node in self.instance_attr_ancestors(name, context): |
| 854 values += class_node.instance_attrs[name] |
| 855 if not values: |
| 856 raise NotFoundError(name) |
| 857 return values |
| 858 instance_attr = remove_nodes(instance_attr, DelAttr) |
| 859 |
| 860 def instanciate_class(self): |
| 861 """return Instance of Class node, else return self""" |
| 862 return Instance(self) |
| 863 |
| 864 def getattr(self, name, context=None): |
| 865 """this method doesn't look in the instance_attrs dictionary since it's |
| 866 done by an Instance proxy at inference time. |
| 867 |
| 868 It may return a YES object if the attribute has not been actually |
| 869 found but a __getattr__ or __getattribute__ method is defined |
| 870 """ |
| 871 values = self.locals.get(name, []) |
| 872 if name in self.special_attributes: |
| 873 if name == '__module__': |
| 874 return [cf(self.root().qname())] + values |
| 875 # FIXME : what is expected by passing the list of ancestors to cf: |
| 876 # you can just do [cf(tuple())] + values without breaking any test |
| 877 # this is ticket http://www.logilab.org/ticket/52785 |
| 878 if name == '__bases__': |
| 879 return [cf(tuple(self.ancestors(recurs=False, context=context)))
] + values |
| 880 # XXX need proper meta class handling + MRO implementation |
| 881 if name == '__mro__' and self.newstyle: |
| 882 # XXX mro is read-only but that's not our job to detect that |
| 883 return [cf(tuple(self.ancestors(recurs=True, context=context)))]
+ values |
| 884 return std_special_attributes(self, name) |
| 885 # don't modify the list in self.locals! |
| 886 values = list(values) |
| 887 for classnode in self.ancestors(recurs=True, context=context): |
| 888 values += classnode.locals.get(name, []) |
| 889 if not values: |
| 890 raise NotFoundError(name) |
| 891 return values |
| 892 |
| 893 def igetattr(self, name, context=None): |
| 894 """inferred getattr, need special treatment in class to handle |
| 895 descriptors |
| 896 """ |
| 897 # set lookup name since this is necessary to infer on import nodes for |
| 898 # instance |
| 899 context = copy_context(context) |
| 900 context.lookupname = name |
| 901 try: |
| 902 for infered in _infer_stmts(self.getattr(name, context), context, |
| 903 frame=self): |
| 904 # yield YES object instead of descriptors when necessary |
| 905 if not isinstance(infered, Const) and isinstance(infered, Instan
ce): |
| 906 try: |
| 907 infered._proxied.getattr('__get__', context) |
| 908 except NotFoundError: |
| 909 yield infered |
| 910 else: |
| 911 yield YES |
| 912 else: |
| 913 yield function_to_method(infered, self) |
| 914 except NotFoundError: |
| 915 if not name.startswith('__') and self.has_dynamic_getattr(context): |
| 916 # class handle some dynamic attributes, return a YES object |
| 917 yield YES |
| 918 else: |
| 919 raise InferenceError(name) |
| 920 |
| 921 def has_dynamic_getattr(self, context=None): |
| 922 """return True if the class has a custom __getattr__ or |
| 923 __getattribute__ method |
| 924 """ |
| 925 # need to explicitly handle optparse.Values (setattr is not detected) |
| 926 if self.name == 'Values' and self.root().name == 'optparse': |
| 927 return True |
| 928 try: |
| 929 self.getattr('__getattr__', context) |
| 930 return True |
| 931 except NotFoundError: |
| 932 #if self.newstyle: XXX cause an infinite recursion error |
| 933 try: |
| 934 getattribute = self.getattr('__getattribute__', context)[0] |
| 935 if getattribute.root().name != BUILTINS_NAME: |
| 936 # class has a custom __getattribute__ defined |
| 937 return True |
| 938 except NotFoundError: |
| 939 pass |
| 940 return False |
| 941 |
| 942 def methods(self): |
| 943 """return an iterator on all methods defined in the class and |
| 944 its ancestors |
| 945 """ |
| 946 done = {} |
| 947 for astng in chain(iter((self,)), self.ancestors()): |
| 948 for meth in astng.mymethods(): |
| 949 if meth.name in done: |
| 950 continue |
| 951 done[meth.name] = None |
| 952 yield meth |
| 953 |
| 954 def mymethods(self): |
| 955 """return an iterator on all methods defined in the class""" |
| 956 for member in self.values(): |
| 957 if isinstance(member, Function): |
| 958 yield member |
| 959 |
| 960 def interfaces(self, herited=True, handler_func=_iface_hdlr): |
| 961 """return an iterator on interfaces implemented by the given |
| 962 class node |
| 963 """ |
| 964 # FIXME: what if __implements__ = (MyIFace, MyParent.__implements__)... |
| 965 try: |
| 966 implements = Instance(self).getattr('__implements__')[0] |
| 967 except NotFoundError: |
| 968 return |
| 969 if not herited and not implements.frame() is self: |
| 970 return |
| 971 found = set() |
| 972 missing = False |
| 973 for iface in unpack_infer(implements): |
| 974 if iface is YES: |
| 975 missing = True |
| 976 continue |
| 977 if not iface in found and handler_func(iface): |
| 978 found.add(iface) |
| 979 yield iface |
| 980 if missing: |
| 981 raise InferenceError() |
| OLD | NEW |