| 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 the classes for "scoped" node, i.e. which are opening a | |
| 19 new local scope in the language definition : Module, Class, Function (and | |
| 20 Lambda, GenExpr, DictComp and SetComp to some extent). | |
| 21 """ | |
| 22 from __future__ import with_statement | |
| 23 | |
| 24 __doctype__ = "restructuredtext en" | |
| 25 | |
| 26 import sys | |
| 27 from itertools import chain | |
| 28 try: | |
| 29 from io import BytesIO | |
| 30 except ImportError: | |
| 31 from cStringIO import StringIO as BytesIO | |
| 32 | |
| 33 from logilab.common.compat import builtins | |
| 34 from logilab.common.decorators import cached, cachedproperty | |
| 35 | |
| 36 from astroid.exceptions import NotFoundError, \ | |
| 37 AstroidBuildingException, InferenceError | |
| 38 from astroid.node_classes import Const, DelName, DelAttr, \ | |
| 39 Dict, From, List, Pass, Raise, Return, Tuple, Yield, YieldFrom, \ | |
| 40 LookupMixIn, const_factory as cf, unpack_infer, Name, CallFunc | |
| 41 from astroid.bases import NodeNG, InferenceContext, Instance,\ | |
| 42 YES, Generator, UnboundMethod, BoundMethod, _infer_stmts, copy_context, \ | |
| 43 BUILTINS | |
| 44 from astroid.mixins import FilterStmtsMixin | |
| 45 from astroid.bases import Statement | |
| 46 from astroid.manager import AstroidManager | |
| 47 | |
| 48 ITER_METHODS = ('__iter__', '__getitem__') | |
| 49 PY3K = sys.version_info >= (3, 0) | |
| 50 | |
| 51 | |
| 52 def remove_nodes(func, cls): | |
| 53 def wrapper(*args, **kwargs): | |
| 54 nodes = [n for n in func(*args, **kwargs) if not isinstance(n, cls)] | |
| 55 if not nodes: | |
| 56 raise NotFoundError() | |
| 57 return nodes | |
| 58 return wrapper | |
| 59 | |
| 60 | |
| 61 def function_to_method(n, klass): | |
| 62 if isinstance(n, Function): | |
| 63 if n.type == 'classmethod': | |
| 64 return BoundMethod(n, klass) | |
| 65 if n.type != 'staticmethod': | |
| 66 return UnboundMethod(n) | |
| 67 return n | |
| 68 | |
| 69 def std_special_attributes(self, name, add_locals=True): | |
| 70 if add_locals: | |
| 71 locals = self.locals | |
| 72 else: | |
| 73 locals = {} | |
| 74 if name == '__name__': | |
| 75 return [cf(self.name)] + locals.get(name, []) | |
| 76 if name == '__doc__': | |
| 77 return [cf(self.doc)] + locals.get(name, []) | |
| 78 if name == '__dict__': | |
| 79 return [Dict()] + locals.get(name, []) | |
| 80 raise NotFoundError(name) | |
| 81 | |
| 82 MANAGER = AstroidManager() | |
| 83 def builtin_lookup(name): | |
| 84 """lookup a name into the builtin module | |
| 85 return the list of matching statements and the astroid for the builtin | |
| 86 module | |
| 87 """ | |
| 88 builtin_astroid = MANAGER.ast_from_module(builtins) | |
| 89 if name == '__dict__': | |
| 90 return builtin_astroid, () | |
| 91 try: | |
| 92 stmts = builtin_astroid.locals[name] | |
| 93 except KeyError: | |
| 94 stmts = () | |
| 95 return builtin_astroid, stmts | |
| 96 | |
| 97 | |
| 98 # TODO move this Mixin to mixins.py; problem: 'Function' in _scope_lookup | |
| 99 class LocalsDictNodeNG(LookupMixIn, NodeNG): | |
| 100 """ this class provides locals handling common to Module, Function | |
| 101 and Class nodes, including a dict like interface for direct access | |
| 102 to locals information | |
| 103 """ | |
| 104 | |
| 105 # attributes below are set by the builder module or by raw factories | |
| 106 | |
| 107 # dictionary of locals with name as key and node defining the local as | |
| 108 # value | |
| 109 | |
| 110 def qname(self): | |
| 111 """return the 'qualified' name of the node, eg module.name, | |
| 112 module.class.name ... | |
| 113 """ | |
| 114 if self.parent is None: | |
| 115 return self.name | |
| 116 return '%s.%s' % (self.parent.frame().qname(), self.name) | |
| 117 | |
| 118 def frame(self): | |
| 119 """return the first parent frame node (i.e. Module, Function or Class) | |
| 120 """ | |
| 121 return self | |
| 122 | |
| 123 def scope(self): | |
| 124 """return the first node defining a new scope (i.e. Module, | |
| 125 Function, Class, Lambda but also GenExpr, DictComp and SetComp) | |
| 126 """ | |
| 127 return self | |
| 128 | |
| 129 | |
| 130 def _scope_lookup(self, node, name, offset=0): | |
| 131 """XXX method for interfacing the scope lookup""" | |
| 132 try: | |
| 133 stmts = node._filter_stmts(self.locals[name], self, offset) | |
| 134 except KeyError: | |
| 135 stmts = () | |
| 136 if stmts: | |
| 137 return self, stmts | |
| 138 if self.parent: # i.e. not Module | |
| 139 # nested scope: if parent scope is a function, that's fine | |
| 140 # else jump to the module | |
| 141 pscope = self.parent.scope() | |
| 142 if not pscope.is_function: | |
| 143 pscope = pscope.root() | |
| 144 return pscope.scope_lookup(node, name) | |
| 145 return builtin_lookup(name) # Module | |
| 146 | |
| 147 | |
| 148 | |
| 149 def set_local(self, name, stmt): | |
| 150 """define <name> in locals (<stmt> is the node defining the name) | |
| 151 if the node is a Module node (i.e. has globals), add the name to | |
| 152 globals | |
| 153 | |
| 154 if the name is already defined, ignore it | |
| 155 """ | |
| 156 #assert not stmt in self.locals.get(name, ()), (self, stmt) | |
| 157 self.locals.setdefault(name, []).append(stmt) | |
| 158 | |
| 159 __setitem__ = set_local | |
| 160 | |
| 161 def _append_node(self, child): | |
| 162 """append a child, linking it in the tree""" | |
| 163 self.body.append(child) | |
| 164 child.parent = self | |
| 165 | |
| 166 def add_local_node(self, child_node, name=None): | |
| 167 """append a child which should alter locals to the given node""" | |
| 168 if name != '__class__': | |
| 169 # add __class__ node as a child will cause infinite recursion later! | |
| 170 self._append_node(child_node) | |
| 171 self.set_local(name or child_node.name, child_node) | |
| 172 | |
| 173 | |
| 174 def __getitem__(self, item): | |
| 175 """method from the `dict` interface returning the first node | |
| 176 associated with the given name in the locals dictionary | |
| 177 | |
| 178 :type item: str | |
| 179 :param item: the name of the locally defined object | |
| 180 :raises KeyError: if the name is not defined | |
| 181 """ | |
| 182 return self.locals[item][0] | |
| 183 | |
| 184 def __iter__(self): | |
| 185 """method from the `dict` interface returning an iterator on | |
| 186 `self.keys()` | |
| 187 """ | |
| 188 return iter(self.keys()) | |
| 189 | |
| 190 def keys(self): | |
| 191 """method from the `dict` interface returning a tuple containing | |
| 192 locally defined names | |
| 193 """ | |
| 194 return self.locals.keys() | |
| 195 | |
| 196 def values(self): | |
| 197 """method from the `dict` interface returning a tuple containing | |
| 198 locally defined nodes which are instance of `Function` or `Class` | |
| 199 """ | |
| 200 return [self[key] for key in self.keys()] | |
| 201 | |
| 202 def items(self): | |
| 203 """method from the `dict` interface returning a list of tuple | |
| 204 containing each locally defined name with its associated node, | |
| 205 which is an instance of `Function` or `Class` | |
| 206 """ | |
| 207 return zip(self.keys(), self.values()) | |
| 208 | |
| 209 | |
| 210 def __contains__(self, name): | |
| 211 return name in self.locals | |
| 212 has_key = __contains__ | |
| 213 | |
| 214 # Module ##################################################################### | |
| 215 | |
| 216 class Module(LocalsDictNodeNG): | |
| 217 _astroid_fields = ('body',) | |
| 218 | |
| 219 fromlineno = 0 | |
| 220 lineno = 0 | |
| 221 | |
| 222 # attributes below are set by the builder module or by raw factories | |
| 223 | |
| 224 # the file from which as been extracted the astroid representation. It may | |
| 225 # be None if the representation has been built from a built-in module | |
| 226 file = None | |
| 227 # Alternatively, if built from a string/bytes, this can be set | |
| 228 file_bytes = None | |
| 229 # encoding of python source file, so we can get unicode out of it (python2 | |
| 230 # only) | |
| 231 file_encoding = None | |
| 232 # the module name | |
| 233 name = None | |
| 234 # boolean for astroid built from source (i.e. ast) | |
| 235 pure_python = None | |
| 236 # boolean for package module | |
| 237 package = None | |
| 238 # dictionary of globals with name as key and node defining the global | |
| 239 # as value | |
| 240 globals = None | |
| 241 | |
| 242 # Future imports | |
| 243 future_imports = None | |
| 244 | |
| 245 # names of python special attributes (handled by getattr impl.) | |
| 246 special_attributes = set(('__name__', '__doc__', '__file__', '__path__', | |
| 247 '__dict__')) | |
| 248 # names of module attributes available through the global scope | |
| 249 scope_attrs = set(('__name__', '__doc__', '__file__', '__path__')) | |
| 250 | |
| 251 def __init__(self, name, doc, pure_python=True): | |
| 252 self.name = name | |
| 253 self.doc = doc | |
| 254 self.pure_python = pure_python | |
| 255 self.locals = self.globals = {} | |
| 256 self.body = [] | |
| 257 self.future_imports = set() | |
| 258 | |
| 259 @property | |
| 260 def file_stream(self): | |
| 261 if self.file_bytes is not None: | |
| 262 return BytesIO(self.file_bytes) | |
| 263 if self.file is not None: | |
| 264 return open(self.file, 'rb') | |
| 265 return None | |
| 266 | |
| 267 def block_range(self, lineno): | |
| 268 """return block line numbers. | |
| 269 | |
| 270 start from the beginning whatever the given lineno | |
| 271 """ | |
| 272 return self.fromlineno, self.tolineno | |
| 273 | |
| 274 def scope_lookup(self, node, name, offset=0): | |
| 275 if name in self.scope_attrs and not name in self.locals: | |
| 276 try: | |
| 277 return self, self.getattr(name) | |
| 278 except NotFoundError: | |
| 279 return self, () | |
| 280 return self._scope_lookup(node, name, offset) | |
| 281 | |
| 282 def pytype(self): | |
| 283 return '%s.module' % BUILTINS | |
| 284 | |
| 285 def display_type(self): | |
| 286 return 'Module' | |
| 287 | |
| 288 def getattr(self, name, context=None, ignore_locals=False): | |
| 289 if name in self.special_attributes: | |
| 290 if name == '__file__': | |
| 291 return [cf(self.file)] + self.locals.get(name, []) | |
| 292 if name == '__path__' and self.package: | |
| 293 return [List()] + self.locals.get(name, []) | |
| 294 return std_special_attributes(self, name) | |
| 295 if not ignore_locals and name in self.locals: | |
| 296 return self.locals[name] | |
| 297 if self.package: | |
| 298 try: | |
| 299 return [self.import_module(name, relative_only=True)] | |
| 300 except AstroidBuildingException: | |
| 301 raise NotFoundError(name) | |
| 302 except SyntaxError: | |
| 303 raise NotFoundError(name) | |
| 304 except Exception:# XXX pylint tests never pass here; do we need it? | |
| 305 import traceback | |
| 306 traceback.print_exc() | |
| 307 raise NotFoundError(name) | |
| 308 getattr = remove_nodes(getattr, DelName) | |
| 309 | |
| 310 def igetattr(self, name, context=None): | |
| 311 """inferred getattr""" | |
| 312 # set lookup name since this is necessary to infer on import nodes for | |
| 313 # instance | |
| 314 context = copy_context(context) | |
| 315 context.lookupname = name | |
| 316 try: | |
| 317 return _infer_stmts(self.getattr(name, context), context, frame=self
) | |
| 318 except NotFoundError: | |
| 319 raise InferenceError(name) | |
| 320 | |
| 321 def fully_defined(self): | |
| 322 """return True if this module has been built from a .py file | |
| 323 and so contains a complete representation including the code | |
| 324 """ | |
| 325 return self.file is not None and self.file.endswith('.py') | |
| 326 | |
| 327 def statement(self): | |
| 328 """return the first parent node marked as statement node | |
| 329 consider a module as a statement... | |
| 330 """ | |
| 331 return self | |
| 332 | |
| 333 def previous_sibling(self): | |
| 334 """module has no sibling""" | |
| 335 return | |
| 336 | |
| 337 def next_sibling(self): | |
| 338 """module has no sibling""" | |
| 339 return | |
| 340 | |
| 341 if sys.version_info < (2, 8): | |
| 342 def absolute_import_activated(self): | |
| 343 for stmt in self.locals.get('absolute_import', ()): | |
| 344 if isinstance(stmt, From) and stmt.modname == '__future__': | |
| 345 return True | |
| 346 return False | |
| 347 else: | |
| 348 absolute_import_activated = lambda self: True | |
| 349 | |
| 350 def import_module(self, modname, relative_only=False, level=None): | |
| 351 """import the given module considering self as context""" | |
| 352 if relative_only and level is None: | |
| 353 level = 0 | |
| 354 absmodname = self.relative_to_absolute_name(modname, level) | |
| 355 try: | |
| 356 return MANAGER.ast_from_module_name(absmodname) | |
| 357 except AstroidBuildingException: | |
| 358 # we only want to import a sub module or package of this module, | |
| 359 # skip here | |
| 360 if relative_only: | |
| 361 raise | |
| 362 return MANAGER.ast_from_module_name(modname) | |
| 363 | |
| 364 def relative_to_absolute_name(self, modname, level): | |
| 365 """return the absolute module name for a relative import. | |
| 366 | |
| 367 The relative import can be implicit or explicit. | |
| 368 """ | |
| 369 # XXX this returns non sens when called on an absolute import | |
| 370 # like 'pylint.checkers.astroid.utils' | |
| 371 # XXX doesn't return absolute name if self.name isn't absolute name | |
| 372 if self.absolute_import_activated() and level is None: | |
| 373 return modname | |
| 374 if level: | |
| 375 if self.package: | |
| 376 level = level - 1 | |
| 377 package_name = self.name.rsplit('.', level)[0] | |
| 378 elif self.package: | |
| 379 package_name = self.name | |
| 380 else: | |
| 381 package_name = self.name.rsplit('.', 1)[0] | |
| 382 if package_name: | |
| 383 if not modname: | |
| 384 return package_name | |
| 385 return '%s.%s' % (package_name, modname) | |
| 386 return modname | |
| 387 | |
| 388 | |
| 389 def wildcard_import_names(self): | |
| 390 """return the list of imported names when this module is 'wildcard | |
| 391 imported' | |
| 392 | |
| 393 It doesn't include the '__builtins__' name which is added by the | |
| 394 current CPython implementation of wildcard imports. | |
| 395 """ | |
| 396 # take advantage of a living module if it exists | |
| 397 try: | |
| 398 living = sys.modules[self.name] | |
| 399 except KeyError: | |
| 400 pass | |
| 401 else: | |
| 402 try: | |
| 403 return living.__all__ | |
| 404 except AttributeError: | |
| 405 return [name for name in living.__dict__.keys() | |
| 406 if not name.startswith('_')] | |
| 407 # else lookup the astroid | |
| 408 # | |
| 409 # We separate the different steps of lookup in try/excepts | |
| 410 # to avoid catching too many Exceptions | |
| 411 # However, we can not analyse dynamically constructed __all__ | |
| 412 try: | |
| 413 all = self['__all__'] | |
| 414 except KeyError: | |
| 415 return [name for name in self.keys() if not name.startswith('_')] | |
| 416 try: | |
| 417 explicit = all.assigned_stmts().next() | |
| 418 except InferenceError: | |
| 419 return [name for name in self.keys() if not name.startswith('_')] | |
| 420 except AttributeError: | |
| 421 # not an assignment node | |
| 422 # XXX infer? | |
| 423 return [name for name in self.keys() if not name.startswith('_')] | |
| 424 try: | |
| 425 # should be a Tuple/List of constant string / 1 string not allowed | |
| 426 return [const.value for const in explicit.elts] | |
| 427 except AttributeError: | |
| 428 return [name for name in self.keys() if not name.startswith('_')] | |
| 429 | |
| 430 | |
| 431 class ComprehensionScope(LocalsDictNodeNG): | |
| 432 def frame(self): | |
| 433 return self.parent.frame() | |
| 434 | |
| 435 scope_lookup = LocalsDictNodeNG._scope_lookup | |
| 436 | |
| 437 | |
| 438 class GenExpr(ComprehensionScope): | |
| 439 _astroid_fields = ('elt', 'generators') | |
| 440 | |
| 441 def __init__(self): | |
| 442 self.locals = {} | |
| 443 self.elt = None | |
| 444 self.generators = [] | |
| 445 | |
| 446 | |
| 447 class DictComp(ComprehensionScope): | |
| 448 _astroid_fields = ('key', 'value', 'generators') | |
| 449 | |
| 450 def __init__(self): | |
| 451 self.locals = {} | |
| 452 self.key = None | |
| 453 self.value = None | |
| 454 self.generators = [] | |
| 455 | |
| 456 | |
| 457 class SetComp(ComprehensionScope): | |
| 458 _astroid_fields = ('elt', 'generators') | |
| 459 | |
| 460 def __init__(self): | |
| 461 self.locals = {} | |
| 462 self.elt = None | |
| 463 self.generators = [] | |
| 464 | |
| 465 | |
| 466 class _ListComp(NodeNG): | |
| 467 """class representing a ListComp node""" | |
| 468 _astroid_fields = ('elt', 'generators') | |
| 469 elt = None | |
| 470 generators = None | |
| 471 | |
| 472 if sys.version_info >= (3, 0): | |
| 473 class ListComp(_ListComp, ComprehensionScope): | |
| 474 """class representing a ListComp node""" | |
| 475 def __init__(self): | |
| 476 self.locals = {} | |
| 477 else: | |
| 478 class ListComp(_ListComp): | |
| 479 """class representing a ListComp node""" | |
| 480 | |
| 481 # Function ################################################################### | |
| 482 | |
| 483 def _infer_decorator_callchain(node): | |
| 484 """ Detect decorator call chaining and see if the | |
| 485 end result is a static or a classmethod. | |
| 486 """ | |
| 487 current = node | |
| 488 while True: | |
| 489 if isinstance(current, CallFunc): | |
| 490 try: | |
| 491 current = current.func.infer().next() | |
| 492 except InferenceError: | |
| 493 return | |
| 494 elif isinstance(current, Function): | |
| 495 if not current.parent: | |
| 496 return | |
| 497 try: | |
| 498 # TODO: We don't handle multiple inference results right now, | |
| 499 # because there's no flow to reason when the return | |
| 500 # is what we are looking for, a static or a class method. | |
| 501 result = current.infer_call_result(current.parent).next() | |
| 502 except (StopIteration, InferenceError): | |
| 503 return | |
| 504 if isinstance(result, (Function, CallFunc)): | |
| 505 current = result | |
| 506 else: | |
| 507 if isinstance(result, Instance): | |
| 508 result = result._proxied | |
| 509 if isinstance(result, Class): | |
| 510 if (result.name == 'classmethod' and | |
| 511 result.root().name == BUILTINS): | |
| 512 return 'classmethod' | |
| 513 elif (result.name == 'staticmethod' and | |
| 514 result.root().name == BUILTINS): | |
| 515 return 'staticmethod' | |
| 516 else: | |
| 517 return | |
| 518 else: | |
| 519 # We aren't interested in anything else returned, | |
| 520 # so go back to the function type inference. | |
| 521 return | |
| 522 else: | |
| 523 return | |
| 524 | |
| 525 def _function_type(self): | |
| 526 """ | |
| 527 Function type, possible values are: | |
| 528 method, function, staticmethod, classmethod. | |
| 529 """ | |
| 530 # Can't infer that this node is decorated | |
| 531 # with a subclass of `classmethod` where `type` is first set, | |
| 532 # so do it here. | |
| 533 if self.decorators: | |
| 534 for node in self.decorators.nodes: | |
| 535 if isinstance(node, CallFunc): | |
| 536 _type = _infer_decorator_callchain(node) | |
| 537 if _type is None: | |
| 538 continue | |
| 539 else: | |
| 540 return _type | |
| 541 if not isinstance(node, Name): | |
| 542 continue | |
| 543 try: | |
| 544 for infered in node.infer(): | |
| 545 if not isinstance(infered, Class): | |
| 546 continue | |
| 547 for ancestor in infered.ancestors(): | |
| 548 if isinstance(ancestor, Class): | |
| 549 if (ancestor.name == 'classmethod' and | |
| 550 ancestor.root().name == BUILTINS): | |
| 551 return 'classmethod' | |
| 552 elif (ancestor.name == 'staticmethod' and | |
| 553 ancestor.root().name == BUILTINS): | |
| 554 return 'staticmethod' | |
| 555 except InferenceError: | |
| 556 pass | |
| 557 return self._type | |
| 558 | |
| 559 | |
| 560 class Lambda(LocalsDictNodeNG, FilterStmtsMixin): | |
| 561 _astroid_fields = ('args', 'body',) | |
| 562 name = '<lambda>' | |
| 563 | |
| 564 # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod' | |
| 565 type = 'function' | |
| 566 | |
| 567 def __init__(self): | |
| 568 self.locals = {} | |
| 569 self.args = [] | |
| 570 self.body = [] | |
| 571 | |
| 572 def pytype(self): | |
| 573 if 'method' in self.type: | |
| 574 return '%s.instancemethod' % BUILTINS | |
| 575 return '%s.function' % BUILTINS | |
| 576 | |
| 577 def display_type(self): | |
| 578 if 'method' in self.type: | |
| 579 return 'Method' | |
| 580 return 'Function' | |
| 581 | |
| 582 def callable(self): | |
| 583 return True | |
| 584 | |
| 585 def argnames(self): | |
| 586 """return a list of argument names""" | |
| 587 if self.args.args: # maybe None with builtin functions | |
| 588 names = _rec_get_names(self.args.args) | |
| 589 else: | |
| 590 names = [] | |
| 591 if self.args.vararg: | |
| 592 names.append(self.args.vararg) | |
| 593 if self.args.kwarg: | |
| 594 names.append(self.args.kwarg) | |
| 595 return names | |
| 596 | |
| 597 def infer_call_result(self, caller, context=None): | |
| 598 """infer what a function is returning when called""" | |
| 599 return self.body.infer(context) | |
| 600 | |
| 601 def scope_lookup(self, node, name, offset=0): | |
| 602 if node in self.args.defaults or node in self.args.kw_defaults: | |
| 603 frame = self.parent.frame() | |
| 604 # line offset to avoid that def func(f=func) resolve the default | |
| 605 # value to the defined function | |
| 606 offset = -1 | |
| 607 else: | |
| 608 # check this is not used in function decorators | |
| 609 frame = self | |
| 610 return frame._scope_lookup(node, name, offset) | |
| 611 | |
| 612 | |
| 613 class Function(Statement, Lambda): | |
| 614 if PY3K: | |
| 615 _astroid_fields = ('decorators', 'args', 'body', 'returns') | |
| 616 returns = None | |
| 617 else: | |
| 618 _astroid_fields = ('decorators', 'args', 'body') | |
| 619 | |
| 620 special_attributes = set(('__name__', '__doc__', '__dict__')) | |
| 621 is_function = True | |
| 622 # attributes below are set by the builder module or by raw factories | |
| 623 blockstart_tolineno = None | |
| 624 decorators = None | |
| 625 _type = "function" | |
| 626 type = cachedproperty(_function_type) | |
| 627 | |
| 628 def __init__(self, name, doc): | |
| 629 self.locals = {} | |
| 630 self.args = [] | |
| 631 self.body = [] | |
| 632 self.decorators = None | |
| 633 self.name = name | |
| 634 self.doc = doc | |
| 635 self.extra_decorators = [] | |
| 636 self.instance_attrs = {} | |
| 637 | |
| 638 def set_line_info(self, lastchild): | |
| 639 self.fromlineno = self.lineno | |
| 640 # lineno is the line number of the first decorator, we want the def stat
ement lineno | |
| 641 if self.decorators is not None: | |
| 642 self.fromlineno += sum(node.tolineno - node.lineno + 1 | |
| 643 for node in self.decorators.nodes) | |
| 644 if self.args.fromlineno < self.fromlineno: | |
| 645 self.args.fromlineno = self.fromlineno | |
| 646 self.tolineno = lastchild.tolineno | |
| 647 self.blockstart_tolineno = self.args.tolineno | |
| 648 | |
| 649 def block_range(self, lineno): | |
| 650 """return block line numbers. | |
| 651 | |
| 652 start from the "def" position whatever the given lineno | |
| 653 """ | |
| 654 return self.fromlineno, self.tolineno | |
| 655 | |
| 656 def getattr(self, name, context=None): | |
| 657 """this method doesn't look in the instance_attrs dictionary since it's | |
| 658 done by an Instance proxy at inference time. | |
| 659 """ | |
| 660 if name == '__module__': | |
| 661 return [cf(self.root().qname())] | |
| 662 if name in self.instance_attrs: | |
| 663 return self.instance_attrs[name] | |
| 664 return std_special_attributes(self, name, False) | |
| 665 | |
| 666 def is_method(self): | |
| 667 """return true if the function node should be considered as a method""" | |
| 668 # check we are defined in a Class, because this is usually expected | |
| 669 # (e.g. pylint...) when is_method() return True | |
| 670 return self.type != 'function' and isinstance(self.parent.frame(), Class
) | |
| 671 | |
| 672 def decoratornames(self): | |
| 673 """return a list of decorator qualified names""" | |
| 674 result = set() | |
| 675 decoratornodes = [] | |
| 676 if self.decorators is not None: | |
| 677 decoratornodes += self.decorators.nodes | |
| 678 decoratornodes += self.extra_decorators | |
| 679 for decnode in decoratornodes: | |
| 680 for infnode in decnode.infer(): | |
| 681 result.add(infnode.qname()) | |
| 682 return result | |
| 683 decoratornames = cached(decoratornames) | |
| 684 | |
| 685 def is_bound(self): | |
| 686 """return true if the function is bound to an Instance or a class""" | |
| 687 return self.type == 'classmethod' | |
| 688 | |
| 689 def is_abstract(self, pass_is_abstract=True): | |
| 690 """Returns True if the method is abstract. | |
| 691 | |
| 692 A method is considered abstract if | |
| 693 - the only statement is 'raise NotImplementedError', or | |
| 694 - the only statement is 'pass' and pass_is_abstract is True, or | |
| 695 - the method is annotated with abc.astractproperty/abc.abstractmethod | |
| 696 """ | |
| 697 if self.decorators: | |
| 698 for node in self.decorators.nodes: | |
| 699 try: | |
| 700 infered = node.infer().next() | |
| 701 except InferenceError: | |
| 702 continue | |
| 703 if infered and infered.qname() in ('abc.abstractproperty', | |
| 704 'abc.abstractmethod'): | |
| 705 return True | |
| 706 | |
| 707 for child_node in self.body: | |
| 708 if isinstance(child_node, Raise): | |
| 709 if child_node.raises_not_implemented(): | |
| 710 return True | |
| 711 if pass_is_abstract and isinstance(child_node, Pass): | |
| 712 return True | |
| 713 return False | |
| 714 # empty function is the same as function with a single "pass" statement | |
| 715 if pass_is_abstract: | |
| 716 return True | |
| 717 | |
| 718 def is_generator(self): | |
| 719 """return true if this is a generator function""" | |
| 720 # XXX should be flagged, not computed | |
| 721 try: | |
| 722 return self.nodes_of_class((Yield, YieldFrom), | |
| 723 skip_klass=(Function, Lambda)).next() | |
| 724 except StopIteration: | |
| 725 return False | |
| 726 | |
| 727 def infer_call_result(self, caller, context=None): | |
| 728 """infer what a function is returning when called""" | |
| 729 if self.is_generator(): | |
| 730 yield Generator() | |
| 731 return | |
| 732 returns = self.nodes_of_class(Return, skip_klass=Function) | |
| 733 for returnnode in returns: | |
| 734 if returnnode.value is None: | |
| 735 yield Const(None) | |
| 736 else: | |
| 737 try: | |
| 738 for infered in returnnode.value.infer(context): | |
| 739 yield infered | |
| 740 except InferenceError: | |
| 741 yield YES | |
| 742 | |
| 743 | |
| 744 def _rec_get_names(args, names=None): | |
| 745 """return a list of all argument names""" | |
| 746 if names is None: | |
| 747 names = [] | |
| 748 for arg in args: | |
| 749 if isinstance(arg, Tuple): | |
| 750 _rec_get_names(arg.elts, names) | |
| 751 else: | |
| 752 names.append(arg.name) | |
| 753 return names | |
| 754 | |
| 755 | |
| 756 # Class ###################################################################### | |
| 757 | |
| 758 | |
| 759 def _is_metaclass(klass, seen=None): | |
| 760 """ Return if the given class can be | |
| 761 used as a metaclass. | |
| 762 """ | |
| 763 if klass.name == 'type': | |
| 764 return True | |
| 765 if seen is None: | |
| 766 seen = set() | |
| 767 for base in klass.bases: | |
| 768 try: | |
| 769 for baseobj in base.infer(): | |
| 770 if baseobj in seen: | |
| 771 continue | |
| 772 else: | |
| 773 seen.add(baseobj) | |
| 774 if isinstance(baseobj, Instance): | |
| 775 # not abstract | |
| 776 return False | |
| 777 if baseobj is YES: | |
| 778 continue | |
| 779 if baseobj is klass: | |
| 780 continue | |
| 781 if not isinstance(baseobj, Class): | |
| 782 continue | |
| 783 if baseobj._type == 'metaclass': | |
| 784 return True | |
| 785 if _is_metaclass(baseobj, seen): | |
| 786 return True | |
| 787 except InferenceError: | |
| 788 continue | |
| 789 return False | |
| 790 | |
| 791 | |
| 792 def _class_type(klass, ancestors=None): | |
| 793 """return a Class node type to differ metaclass, interface and exception | |
| 794 from 'regular' classes | |
| 795 """ | |
| 796 # XXX we have to store ancestors in case we have a ancestor loop | |
| 797 if klass._type is not None: | |
| 798 return klass._type | |
| 799 if _is_metaclass(klass): | |
| 800 klass._type = 'metaclass' | |
| 801 elif klass.name.endswith('Interface'): | |
| 802 klass._type = 'interface' | |
| 803 elif klass.name.endswith('Exception'): | |
| 804 klass._type = 'exception' | |
| 805 else: | |
| 806 if ancestors is None: | |
| 807 ancestors = set() | |
| 808 if klass in ancestors: | |
| 809 # XXX we are in loop ancestors, and have found no type | |
| 810 klass._type = 'class' | |
| 811 return 'class' | |
| 812 ancestors.add(klass) | |
| 813 # print >> sys.stderr, '_class_type', repr(klass) | |
| 814 for base in klass.ancestors(recurs=False): | |
| 815 name = _class_type(base, ancestors) | |
| 816 if name != 'class': | |
| 817 if name == 'metaclass' and not _is_metaclass(klass): | |
| 818 # don't propagate it if the current class | |
| 819 # can't be a metaclass | |
| 820 continue | |
| 821 klass._type = base.type | |
| 822 break | |
| 823 if klass._type is None: | |
| 824 klass._type = 'class' | |
| 825 return klass._type | |
| 826 | |
| 827 def _iface_hdlr(iface_node): | |
| 828 """a handler function used by interfaces to handle suspicious | |
| 829 interface nodes | |
| 830 """ | |
| 831 return True | |
| 832 | |
| 833 | |
| 834 class Class(Statement, LocalsDictNodeNG, FilterStmtsMixin): | |
| 835 | |
| 836 # some of the attributes below are set by the builder module or | |
| 837 # by a raw factories | |
| 838 | |
| 839 # a dictionary of class instances attributes | |
| 840 _astroid_fields = ('decorators', 'bases', 'body') # name | |
| 841 | |
| 842 decorators = None | |
| 843 special_attributes = set(('__name__', '__doc__', '__dict__', '__module__', | |
| 844 '__bases__', '__mro__', '__subclasses__')) | |
| 845 blockstart_tolineno = None | |
| 846 | |
| 847 _type = None | |
| 848 type = property(_class_type, | |
| 849 doc="class'type, possible values are 'class' | " | |
| 850 "'metaclass' | 'interface' | 'exception'") | |
| 851 | |
| 852 def __init__(self, name, doc): | |
| 853 self.instance_attrs = {} | |
| 854 self.locals = {} | |
| 855 self.bases = [] | |
| 856 self.body = [] | |
| 857 self.name = name | |
| 858 self.doc = doc | |
| 859 | |
| 860 def _newstyle_impl(self, context=None): | |
| 861 if context is None: | |
| 862 context = InferenceContext() | |
| 863 if self._newstyle is not None: | |
| 864 return self._newstyle | |
| 865 for base in self.ancestors(recurs=False, context=context): | |
| 866 if base._newstyle_impl(context): | |
| 867 self._newstyle = True | |
| 868 break | |
| 869 klass = self._explicit_metaclass() | |
| 870 # could be any callable, we'd need to infer the result of klass(name, | |
| 871 # bases, dict). punt if it's not a class node. | |
| 872 if klass is not None and isinstance(klass, Class): | |
| 873 self._newstyle = klass._newstyle_impl(context) | |
| 874 if self._newstyle is None: | |
| 875 self._newstyle = False | |
| 876 return self._newstyle | |
| 877 | |
| 878 _newstyle = None | |
| 879 newstyle = property(_newstyle_impl, | |
| 880 doc="boolean indicating if it's a new style class" | |
| 881 "or not") | |
| 882 | |
| 883 def set_line_info(self, lastchild): | |
| 884 self.fromlineno = self.lineno | |
| 885 self.blockstart_tolineno = self.bases and self.bases[-1].tolineno or sel
f.fromlineno | |
| 886 if lastchild is not None: | |
| 887 self.tolineno = lastchild.tolineno | |
| 888 # else this is a class with only a docstring, then tolineno is (should b
e) already ok | |
| 889 | |
| 890 def block_range(self, lineno): | |
| 891 """return block line numbers. | |
| 892 | |
| 893 start from the "class" position whatever the given lineno | |
| 894 """ | |
| 895 return self.fromlineno, self.tolineno | |
| 896 | |
| 897 def pytype(self): | |
| 898 if self.newstyle: | |
| 899 return '%s.type' % BUILTINS | |
| 900 return '%s.classobj' % BUILTINS | |
| 901 | |
| 902 def display_type(self): | |
| 903 return 'Class' | |
| 904 | |
| 905 def callable(self): | |
| 906 return True | |
| 907 | |
| 908 def _is_subtype_of(self, type_name): | |
| 909 if self.qname() == type_name: | |
| 910 return True | |
| 911 for anc in self.ancestors(): | |
| 912 if anc.qname() == type_name: | |
| 913 return True | |
| 914 | |
| 915 def infer_call_result(self, caller, context=None): | |
| 916 """infer what a class is returning when called""" | |
| 917 if self._is_subtype_of('%s.type' % (BUILTINS,)) and len(caller.args) ==
3: | |
| 918 name_node = caller.args[0].infer().next() | |
| 919 if isinstance(name_node, Const) and isinstance(name_node.value, base
string): | |
| 920 name = name_node.value | |
| 921 else: | |
| 922 yield YES | |
| 923 return | |
| 924 result = Class(name, None) | |
| 925 bases = caller.args[1].infer().next() | |
| 926 if isinstance(bases, (Tuple, List)): | |
| 927 result.bases = bases.itered() | |
| 928 else: | |
| 929 # There is currently no AST node that can represent an 'unknown' | |
| 930 # node (YES is not an AST node), therefore we simply return YES
here | |
| 931 # although we know at least the name of the class. | |
| 932 yield YES | |
| 933 return | |
| 934 result.parent = caller.parent | |
| 935 yield result | |
| 936 else: | |
| 937 yield Instance(self) | |
| 938 | |
| 939 def scope_lookup(self, node, name, offset=0): | |
| 940 if node in self.bases: | |
| 941 frame = self.parent.frame() | |
| 942 # line offset to avoid that class A(A) resolve the ancestor to | |
| 943 # the defined class | |
| 944 offset = -1 | |
| 945 else: | |
| 946 frame = self | |
| 947 return frame._scope_lookup(node, name, offset) | |
| 948 | |
| 949 # list of parent class as a list of string (i.e. names as they appear | |
| 950 # in the class definition) XXX bw compat | |
| 951 def basenames(self): | |
| 952 return [bnode.as_string() for bnode in self.bases] | |
| 953 basenames = property(basenames) | |
| 954 | |
| 955 def ancestors(self, recurs=True, context=None): | |
| 956 """return an iterator on the node base classes in a prefixed | |
| 957 depth first order | |
| 958 | |
| 959 :param recurs: | |
| 960 boolean indicating if it should recurse or return direct | |
| 961 ancestors only | |
| 962 """ | |
| 963 # FIXME: should be possible to choose the resolution order | |
| 964 # XXX inference make infinite loops possible here (see BaseTransformer | |
| 965 # manipulation in the builder module for instance) | |
| 966 yielded = set([self]) | |
| 967 if context is None: | |
| 968 context = InferenceContext() | |
| 969 for stmt in self.bases: | |
| 970 with context.restore_path(): | |
| 971 try: | |
| 972 for baseobj in stmt.infer(context): | |
| 973 if not isinstance(baseobj, Class): | |
| 974 if isinstance(baseobj, Instance): | |
| 975 baseobj = baseobj._proxied | |
| 976 else: | |
| 977 # duh ? | |
| 978 continue | |
| 979 if baseobj in yielded: | |
| 980 continue # cf xxx above | |
| 981 yielded.add(baseobj) | |
| 982 yield baseobj | |
| 983 if recurs: | |
| 984 for grandpa in baseobj.ancestors(True, context): | |
| 985 if grandpa in yielded: | |
| 986 continue # cf xxx above | |
| 987 yielded.add(grandpa) | |
| 988 yield grandpa | |
| 989 except InferenceError: | |
| 990 # XXX log error ? | |
| 991 continue | |
| 992 | |
| 993 def local_attr_ancestors(self, name, context=None): | |
| 994 """return an iterator on astroid representation of parent classes | |
| 995 which have <name> defined in their locals | |
| 996 """ | |
| 997 for astroid in self.ancestors(context=context): | |
| 998 if name in astroid: | |
| 999 yield astroid | |
| 1000 | |
| 1001 def instance_attr_ancestors(self, name, context=None): | |
| 1002 """return an iterator on astroid representation of parent classes | |
| 1003 which have <name> defined in their instance attribute dictionary | |
| 1004 """ | |
| 1005 for astroid in self.ancestors(context=context): | |
| 1006 if name in astroid.instance_attrs: | |
| 1007 yield astroid | |
| 1008 | |
| 1009 def has_base(self, node): | |
| 1010 return node in self.bases | |
| 1011 | |
| 1012 def local_attr(self, name, context=None): | |
| 1013 """return the list of assign node associated to name in this class | |
| 1014 locals or in its parents | |
| 1015 | |
| 1016 :raises `NotFoundError`: | |
| 1017 if no attribute with this name has been find in this class or | |
| 1018 its parent classes | |
| 1019 """ | |
| 1020 try: | |
| 1021 return self.locals[name] | |
| 1022 except KeyError: | |
| 1023 # get if from the first parent implementing it if any | |
| 1024 for class_node in self.local_attr_ancestors(name, context): | |
| 1025 return class_node.locals[name] | |
| 1026 raise NotFoundError(name) | |
| 1027 local_attr = remove_nodes(local_attr, DelAttr) | |
| 1028 | |
| 1029 def instance_attr(self, name, context=None): | |
| 1030 """return the astroid nodes associated to name in this class instance | |
| 1031 attributes dictionary and in its parents | |
| 1032 | |
| 1033 :raises `NotFoundError`: | |
| 1034 if no attribute with this name has been find in this class or | |
| 1035 its parent classes | |
| 1036 """ | |
| 1037 # Return a copy, so we don't modify self.instance_attrs, | |
| 1038 # which could lead to infinite loop. | |
| 1039 values = list(self.instance_attrs.get(name, [])) | |
| 1040 # get all values from parents | |
| 1041 for class_node in self.instance_attr_ancestors(name, context): | |
| 1042 values += class_node.instance_attrs[name] | |
| 1043 if not values: | |
| 1044 raise NotFoundError(name) | |
| 1045 return values | |
| 1046 instance_attr = remove_nodes(instance_attr, DelAttr) | |
| 1047 | |
| 1048 def instanciate_class(self): | |
| 1049 """return Instance of Class node, else return self""" | |
| 1050 return Instance(self) | |
| 1051 | |
| 1052 def getattr(self, name, context=None): | |
| 1053 """this method doesn't look in the instance_attrs dictionary since it's | |
| 1054 done by an Instance proxy at inference time. | |
| 1055 | |
| 1056 It may return a YES object if the attribute has not been actually | |
| 1057 found but a __getattr__ or __getattribute__ method is defined | |
| 1058 """ | |
| 1059 values = self.locals.get(name, []) | |
| 1060 if name in self.special_attributes: | |
| 1061 if name == '__module__': | |
| 1062 return [cf(self.root().qname())] + values | |
| 1063 # FIXME: do we really need the actual list of ancestors? | |
| 1064 # returning [Tuple()] + values don't break any test | |
| 1065 # this is ticket http://www.logilab.org/ticket/52785 | |
| 1066 # XXX need proper meta class handling + MRO implementation | |
| 1067 if name == '__bases__' or (name == '__mro__' and self.newstyle): | |
| 1068 node = Tuple() | |
| 1069 node.items = self.ancestors(recurs=True, context=context) | |
| 1070 return [node] + values | |
| 1071 return std_special_attributes(self, name) | |
| 1072 # don't modify the list in self.locals! | |
| 1073 values = list(values) | |
| 1074 for classnode in self.ancestors(recurs=True, context=context): | |
| 1075 values += classnode.locals.get(name, []) | |
| 1076 if not values: | |
| 1077 raise NotFoundError(name) | |
| 1078 return values | |
| 1079 | |
| 1080 def igetattr(self, name, context=None): | |
| 1081 """inferred getattr, need special treatment in class to handle | |
| 1082 descriptors | |
| 1083 """ | |
| 1084 # set lookup name since this is necessary to infer on import nodes for | |
| 1085 # instance | |
| 1086 context = copy_context(context) | |
| 1087 context.lookupname = name | |
| 1088 try: | |
| 1089 for infered in _infer_stmts(self.getattr(name, context), context, | |
| 1090 frame=self): | |
| 1091 # yield YES object instead of descriptors when necessary | |
| 1092 if not isinstance(infered, Const) and isinstance(infered, Instan
ce): | |
| 1093 try: | |
| 1094 infered._proxied.getattr('__get__', context) | |
| 1095 except NotFoundError: | |
| 1096 yield infered | |
| 1097 else: | |
| 1098 yield YES | |
| 1099 else: | |
| 1100 yield function_to_method(infered, self) | |
| 1101 except NotFoundError: | |
| 1102 if not name.startswith('__') and self.has_dynamic_getattr(context): | |
| 1103 # class handle some dynamic attributes, return a YES object | |
| 1104 yield YES | |
| 1105 else: | |
| 1106 raise InferenceError(name) | |
| 1107 | |
| 1108 def has_dynamic_getattr(self, context=None): | |
| 1109 """return True if the class has a custom __getattr__ or | |
| 1110 __getattribute__ method | |
| 1111 """ | |
| 1112 # need to explicitly handle optparse.Values (setattr is not detected) | |
| 1113 if self.name == 'Values' and self.root().name == 'optparse': | |
| 1114 return True | |
| 1115 try: | |
| 1116 self.getattr('__getattr__', context) | |
| 1117 return True | |
| 1118 except NotFoundError: | |
| 1119 #if self.newstyle: XXX cause an infinite recursion error | |
| 1120 try: | |
| 1121 getattribute = self.getattr('__getattribute__', context)[0] | |
| 1122 if getattribute.root().name != BUILTINS: | |
| 1123 # class has a custom __getattribute__ defined | |
| 1124 return True | |
| 1125 except NotFoundError: | |
| 1126 pass | |
| 1127 return False | |
| 1128 | |
| 1129 def methods(self): | |
| 1130 """return an iterator on all methods defined in the class and | |
| 1131 its ancestors | |
| 1132 """ | |
| 1133 done = {} | |
| 1134 for astroid in chain(iter((self,)), self.ancestors()): | |
| 1135 for meth in astroid.mymethods(): | |
| 1136 if meth.name in done: | |
| 1137 continue | |
| 1138 done[meth.name] = None | |
| 1139 yield meth | |
| 1140 | |
| 1141 def mymethods(self): | |
| 1142 """return an iterator on all methods defined in the class""" | |
| 1143 for member in self.values(): | |
| 1144 if isinstance(member, Function): | |
| 1145 yield member | |
| 1146 | |
| 1147 def interfaces(self, herited=True, handler_func=_iface_hdlr): | |
| 1148 """return an iterator on interfaces implemented by the given | |
| 1149 class node | |
| 1150 """ | |
| 1151 # FIXME: what if __implements__ = (MyIFace, MyParent.__implements__)... | |
| 1152 try: | |
| 1153 implements = Instance(self).getattr('__implements__')[0] | |
| 1154 except NotFoundError: | |
| 1155 return | |
| 1156 if not herited and not implements.frame() is self: | |
| 1157 return | |
| 1158 found = set() | |
| 1159 missing = False | |
| 1160 for iface in unpack_infer(implements): | |
| 1161 if iface is YES: | |
| 1162 missing = True | |
| 1163 continue | |
| 1164 if not iface in found and handler_func(iface): | |
| 1165 found.add(iface) | |
| 1166 yield iface | |
| 1167 if missing: | |
| 1168 raise InferenceError() | |
| 1169 | |
| 1170 _metaclass = None | |
| 1171 def _explicit_metaclass(self): | |
| 1172 """ Return the explicit defined metaclass | |
| 1173 for the current class. | |
| 1174 | |
| 1175 An explicit defined metaclass is defined | |
| 1176 either by passing the ``metaclass`` keyword argument | |
| 1177 in the class definition line (Python 3) or (Python 2) by | |
| 1178 having a ``__metaclass__`` class attribute, or if there are | |
| 1179 no explicit bases but there is a global ``__metaclass__`` variable. | |
| 1180 """ | |
| 1181 if self._metaclass: | |
| 1182 # Expects this from Py3k TreeRebuilder | |
| 1183 try: | |
| 1184 return next(node for node in self._metaclass.infer() | |
| 1185 if node is not YES) | |
| 1186 except (InferenceError, StopIteration): | |
| 1187 return None | |
| 1188 if sys.version_info >= (3, ): | |
| 1189 return None | |
| 1190 | |
| 1191 if '__metaclass__' in self.locals: | |
| 1192 assignment = self.locals['__metaclass__'][-1] | |
| 1193 elif self.bases: | |
| 1194 return None | |
| 1195 elif '__metaclass__' in self.root().locals: | |
| 1196 assignments = [ass for ass in self.root().locals['__metaclass__'] | |
| 1197 if ass.lineno < self.lineno] | |
| 1198 if not assignments: | |
| 1199 return None | |
| 1200 assignment = assignments[-1] | |
| 1201 else: | |
| 1202 return None | |
| 1203 | |
| 1204 try: | |
| 1205 infered = assignment.infer().next() | |
| 1206 except InferenceError: | |
| 1207 return | |
| 1208 if infered is YES: # don't expose this | |
| 1209 return None | |
| 1210 return infered | |
| 1211 | |
| 1212 def metaclass(self): | |
| 1213 """ Return the metaclass of this class. | |
| 1214 | |
| 1215 If this class does not define explicitly a metaclass, | |
| 1216 then the first defined metaclass in ancestors will be used | |
| 1217 instead. | |
| 1218 """ | |
| 1219 klass = self._explicit_metaclass() | |
| 1220 if klass is None: | |
| 1221 for parent in self.ancestors(): | |
| 1222 klass = parent.metaclass() | |
| 1223 if klass is not None: | |
| 1224 break | |
| 1225 return klass | |
| 1226 | |
| 1227 def _islots(self): | |
| 1228 """ Return an iterator with the inferred slots. """ | |
| 1229 if '__slots__' not in self.locals: | |
| 1230 return | |
| 1231 for slots in self.igetattr('__slots__'): | |
| 1232 # check if __slots__ is a valid type | |
| 1233 for meth in ITER_METHODS: | |
| 1234 try: | |
| 1235 slots.getattr(meth) | |
| 1236 break | |
| 1237 except NotFoundError: | |
| 1238 continue | |
| 1239 else: | |
| 1240 continue | |
| 1241 | |
| 1242 if isinstance(slots, Const): | |
| 1243 # a string. Ignore the following checks, | |
| 1244 # but yield the node, only if it has a value | |
| 1245 if slots.value: | |
| 1246 yield slots | |
| 1247 continue | |
| 1248 if not hasattr(slots, 'itered'): | |
| 1249 # we can't obtain the values, maybe a .deque? | |
| 1250 continue | |
| 1251 | |
| 1252 if isinstance(slots, Dict): | |
| 1253 values = [item[0] for item in slots.items] | |
| 1254 else: | |
| 1255 values = slots.itered() | |
| 1256 if values is YES: | |
| 1257 continue | |
| 1258 | |
| 1259 for elt in values: | |
| 1260 try: | |
| 1261 for infered in elt.infer(): | |
| 1262 if infered is YES: | |
| 1263 continue | |
| 1264 if (not isinstance(infered, Const) or | |
| 1265 not isinstance(infered.value, str)): | |
| 1266 continue | |
| 1267 if not infered.value: | |
| 1268 continue | |
| 1269 yield infered | |
| 1270 except InferenceError: | |
| 1271 continue | |
| 1272 | |
| 1273 # Cached, because inferring them all the time is expensive | |
| 1274 @cached | |
| 1275 def slots(self): | |
| 1276 """ Return all the slots for this node. """ | |
| 1277 return list(self._islots()) | |
| OLD | NEW |