OLD | NEW |
| (Empty) |
1 # copyright 2003-2011 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 # the module name | |
224 name = None | |
225 # boolean for astng built from source (i.e. ast) | |
226 pure_python = None | |
227 # boolean for package module | |
228 package = None | |
229 # dictionary of globals with name as key and node defining the global | |
230 # as value | |
231 globals = None | |
232 | |
233 # names of python special attributes (handled by getattr impl.) | |
234 special_attributes = set(('__name__', '__doc__', '__file__', '__path__', | |
235 '__dict__')) | |
236 # names of module attributes available through the global scope | |
237 scope_attrs = set(('__name__', '__doc__', '__file__', '__path__')) | |
238 | |
239 def __init__(self, name, doc, pure_python=True): | |
240 self.name = name | |
241 self.doc = doc | |
242 self.pure_python = pure_python | |
243 self.locals = self.globals = {} | |
244 self.body = [] | |
245 | |
246 @property | |
247 def file_stream(self): | |
248 if self.file is not None: | |
249 return file(self.file) | |
250 return None | |
251 | |
252 def block_range(self, lineno): | |
253 """return block line numbers. | |
254 | |
255 start from the beginning whatever the given lineno | |
256 """ | |
257 return self.fromlineno, self.tolineno | |
258 | |
259 def scope_lookup(self, node, name, offset=0): | |
260 if name in self.scope_attrs and not name in self.locals: | |
261 try: | |
262 return self, self.getattr(name) | |
263 except NotFoundError: | |
264 return self, () | |
265 return self._scope_lookup(node, name, offset) | |
266 | |
267 def pytype(self): | |
268 return '%s.module' % BUILTINS_MODULE | |
269 | |
270 def display_type(self): | |
271 return 'Module' | |
272 | |
273 def getattr(self, name, context=None, ignore_locals=False): | |
274 if name in self.special_attributes: | |
275 if name == '__file__': | |
276 return [cf(self.file)] + self.locals.get(name, []) | |
277 if name == '__path__' and self.package: | |
278 return [List()] + self.locals.get(name, []) | |
279 return std_special_attributes(self, name) | |
280 if not ignore_locals and name in self.locals: | |
281 return self.locals[name] | |
282 if self.package: | |
283 try: | |
284 return [self.import_module(name, relative_only=True)] | |
285 except ASTNGBuildingException: | |
286 raise NotFoundError(name) | |
287 except Exception:# XXX pylint tests never pass here; do we need it? | |
288 import traceback | |
289 traceback.print_exc() | |
290 raise NotFoundError(name) | |
291 getattr = remove_nodes(getattr, DelName) | |
292 | |
293 def igetattr(self, name, context=None): | |
294 """inferred getattr""" | |
295 # set lookup name since this is necessary to infer on import nodes for | |
296 # instance | |
297 context = copy_context(context) | |
298 context.lookupname = name | |
299 try: | |
300 return _infer_stmts(self.getattr(name, context), context, frame=self
) | |
301 except NotFoundError: | |
302 raise InferenceError(name) | |
303 | |
304 def fully_defined(self): | |
305 """return True if this module has been built from a .py file | |
306 and so contains a complete representation including the code | |
307 """ | |
308 return self.file is not None and self.file.endswith('.py') | |
309 | |
310 def statement(self): | |
311 """return the first parent node marked as statement node | |
312 consider a module as a statement... | |
313 """ | |
314 return self | |
315 | |
316 def previous_sibling(self): | |
317 """module has no sibling""" | |
318 return | |
319 | |
320 def next_sibling(self): | |
321 """module has no sibling""" | |
322 return | |
323 | |
324 if sys.version_info < (2, 8): | |
325 def absolute_import_activated(self): | |
326 for stmt in self.locals.get('absolute_import', ()): | |
327 if isinstance(stmt, From) and stmt.modname == '__future__': | |
328 return True | |
329 return False | |
330 else: | |
331 absolute_import_activated = lambda self: True | |
332 | |
333 def import_module(self, modname, relative_only=False, level=None): | |
334 """import the given module considering self as context""" | |
335 if relative_only and level is None: | |
336 level = 0 | |
337 absmodname = self.relative_to_absolute_name(modname, level) | |
338 try: | |
339 return MANAGER.astng_from_module_name(absmodname) | |
340 except ASTNGBuildingException: | |
341 # we only want to import a sub module or package of this module, | |
342 # skip here | |
343 if relative_only: | |
344 raise | |
345 return MANAGER.astng_from_module_name(modname) | |
346 | |
347 def relative_to_absolute_name(self, modname, level): | |
348 """return the absolute module name for a relative import. | |
349 | |
350 The relative import can be implicit or explicit. | |
351 """ | |
352 # XXX this returns non sens when called on an absolute import | |
353 # like 'pylint.checkers.logilab.astng.utils' | |
354 # XXX doesn't return absolute name if self.name isn't absolute name | |
355 if self.absolute_import_activated() and level is None: | |
356 return modname | |
357 if level: | |
358 if self.package: | |
359 level = level - 1 | |
360 package_name = self.name.rsplit('.', level)[0] | |
361 elif self.package: | |
362 package_name = self.name | |
363 else: | |
364 package_name = self.name.rsplit('.', 1)[0] | |
365 if package_name: | |
366 if not modname: | |
367 return package_name | |
368 return '%s.%s' % (package_name, modname) | |
369 return modname | |
370 | |
371 | |
372 def wildcard_import_names(self): | |
373 """return the list of imported names when this module is 'wildcard | |
374 imported' | |
375 | |
376 It doesn't include the '__builtins__' name which is added by the | |
377 current CPython implementation of wildcard imports. | |
378 """ | |
379 # take advantage of a living module if it exists | |
380 try: | |
381 living = sys.modules[self.name] | |
382 except KeyError: | |
383 pass | |
384 else: | |
385 try: | |
386 return living.__all__ | |
387 except AttributeError: | |
388 return [name for name in living.__dict__.keys() | |
389 if not name.startswith('_')] | |
390 # else lookup the astng | |
391 # | |
392 # We separate the different steps of lookup in try/excepts | |
393 # to avoid catching too many Exceptions | |
394 # However, we can not analyse dynamically constructed __all__ | |
395 try: | |
396 all = self['__all__'] | |
397 except KeyError: | |
398 return [name for name in self.keys() if not name.startswith('_')] | |
399 try: | |
400 explicit = all.assigned_stmts().next() | |
401 except InferenceError: | |
402 return [name for name in self.keys() if not name.startswith('_')] | |
403 except AttributeError: | |
404 # not an assignment node | |
405 # XXX infer? | |
406 return [name for name in self.keys() if not name.startswith('_')] | |
407 try: | |
408 # should be a Tuple/List of constant string / 1 string not allowed | |
409 return [const.value for const in explicit.elts] | |
410 except AttributeError: | |
411 return [name for name in self.keys() if not name.startswith('_')] | |
412 | |
413 | |
414 class ComprehensionScope(LocalsDictNodeNG): | |
415 def frame(self): | |
416 return self.parent.frame() | |
417 | |
418 scope_lookup = LocalsDictNodeNG._scope_lookup | |
419 | |
420 | |
421 class GenExpr(ComprehensionScope): | |
422 _astng_fields = ('elt', 'generators') | |
423 | |
424 def __init__(self): | |
425 self.locals = {} | |
426 self.elt = None | |
427 self.generators = [] | |
428 | |
429 | |
430 class DictComp(ComprehensionScope): | |
431 _astng_fields = ('key', 'value', 'generators') | |
432 | |
433 def __init__(self): | |
434 self.locals = {} | |
435 self.key = None | |
436 self.value = None | |
437 self.generators = [] | |
438 | |
439 | |
440 class SetComp(ComprehensionScope): | |
441 _astng_fields = ('elt', 'generators') | |
442 | |
443 def __init__(self): | |
444 self.locals = {} | |
445 self.elt = None | |
446 self.generators = [] | |
447 | |
448 | |
449 class _ListComp(NodeNG): | |
450 """class representing a ListComp node""" | |
451 _astng_fields = ('elt', 'generators') | |
452 elt = None | |
453 generators = None | |
454 | |
455 if sys.version_info >= (3, 0): | |
456 class ListComp(_ListComp, ComprehensionScope): | |
457 """class representing a ListComp node""" | |
458 def __init__(self): | |
459 self.locals = {} | |
460 else: | |
461 class ListComp(_ListComp): | |
462 """class representing a ListComp node""" | |
463 | |
464 # Function ################################################################### | |
465 | |
466 | |
467 class Lambda(LocalsDictNodeNG, FilterStmtsMixin): | |
468 _astng_fields = ('args', 'body',) | |
469 name = '<lambda>' | |
470 | |
471 # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod' | |
472 type = 'function' | |
473 | |
474 def __init__(self): | |
475 self.locals = {} | |
476 self.args = [] | |
477 self.body = [] | |
478 | |
479 def pytype(self): | |
480 if 'method' in self.type: | |
481 return '%s.instancemethod' % BUILTINS_MODULE | |
482 return '%s.function' % BUILTINS_MODULE | |
483 | |
484 def display_type(self): | |
485 if 'method' in self.type: | |
486 return 'Method' | |
487 return 'Function' | |
488 | |
489 def callable(self): | |
490 return True | |
491 | |
492 def argnames(self): | |
493 """return a list of argument names""" | |
494 if self.args.args: # maybe None with builtin functions | |
495 names = _rec_get_names(self.args.args) | |
496 else: | |
497 names = [] | |
498 if self.args.vararg: | |
499 names.append(self.args.vararg) | |
500 if self.args.kwarg: | |
501 names.append(self.args.kwarg) | |
502 return names | |
503 | |
504 def infer_call_result(self, caller, context=None): | |
505 """infer what a function is returning when called""" | |
506 return self.body.infer(context) | |
507 | |
508 def scope_lookup(self, node, name, offset=0): | |
509 if node in self.args.defaults: | |
510 frame = self.parent.frame() | |
511 # line offset to avoid that def func(f=func) resolve the default | |
512 # value to the defined function | |
513 offset = -1 | |
514 else: | |
515 # check this is not used in function decorators | |
516 frame = self | |
517 return frame._scope_lookup(node, name, offset) | |
518 | |
519 | |
520 class Function(Statement, Lambda): | |
521 _astng_fields = ('decorators', 'args', 'body') | |
522 | |
523 special_attributes = set(('__name__', '__doc__', '__dict__')) | |
524 is_function = True | |
525 # attributes below are set by the builder module or by raw factories | |
526 blockstart_tolineno = None | |
527 decorators = None | |
528 | |
529 def __init__(self, name, doc): | |
530 self.locals = {} | |
531 self.args = [] | |
532 self.body = [] | |
533 self.decorators = None | |
534 self.name = name | |
535 self.doc = doc | |
536 self.extra_decorators = [] | |
537 self.instance_attrs = {} | |
538 | |
539 def set_line_info(self, lastchild): | |
540 self.fromlineno = self.lineno | |
541 # lineno is the line number of the first decorator, we want the def stat
ement lineno | |
542 if self.decorators is not None: | |
543 self.fromlineno += len(self.decorators.nodes) | |
544 self.tolineno = lastchild.tolineno | |
545 self.blockstart_tolineno = self.args.tolineno | |
546 | |
547 def block_range(self, lineno): | |
548 """return block line numbers. | |
549 | |
550 start from the "def" position whatever the given lineno | |
551 """ | |
552 return self.fromlineno, self.tolineno | |
553 | |
554 def getattr(self, name, context=None): | |
555 """this method doesn't look in the instance_attrs dictionary since it's | |
556 done by an Instance proxy at inference time. | |
557 """ | |
558 if name == '__module__': | |
559 return [cf(self.root().qname())] | |
560 if name in self.instance_attrs: | |
561 return self.instance_attrs[name] | |
562 return std_special_attributes(self, name, False) | |
563 | |
564 def is_method(self): | |
565 """return true if the function node should be considered as a method""" | |
566 # check we are defined in a Class, because this is usually expected | |
567 # (e.g. pylint...) when is_method() return True | |
568 return self.type != 'function' and isinstance(self.parent.frame(), Class
) | |
569 | |
570 def decoratornames(self): | |
571 """return a list of decorator qualified names""" | |
572 result = set() | |
573 decoratornodes = [] | |
574 if self.decorators is not None: | |
575 decoratornodes += self.decorators.nodes | |
576 decoratornodes += self.extra_decorators | |
577 for decnode in decoratornodes: | |
578 for infnode in decnode.infer(): | |
579 result.add(infnode.qname()) | |
580 return result | |
581 decoratornames = cached(decoratornames) | |
582 | |
583 def is_bound(self): | |
584 """return true if the function is bound to an Instance or a class""" | |
585 return self.type == 'classmethod' | |
586 | |
587 def is_abstract(self, pass_is_abstract=True): | |
588 """return true if the method is abstract | |
589 It's considered as abstract if the only statement is a raise of | |
590 NotImplementError, or, if pass_is_abstract, a pass statement | |
591 """ | |
592 for child_node in self.body: | |
593 if isinstance(child_node, Raise): | |
594 if child_node.raises_not_implemented(): | |
595 return True | |
596 if pass_is_abstract and isinstance(child_node, Pass): | |
597 return True | |
598 return False | |
599 # empty function is the same as function with a single "pass" statement | |
600 if pass_is_abstract: | |
601 return True | |
602 | |
603 def is_generator(self): | |
604 """return true if this is a generator function""" | |
605 # XXX should be flagged, not computed | |
606 try: | |
607 return self.nodes_of_class(Yield, skip_klass=Function).next() | |
608 except StopIteration: | |
609 return False | |
610 | |
611 def infer_call_result(self, caller, context=None): | |
612 """infer what a function is returning when called""" | |
613 if self.is_generator(): | |
614 yield Generator(self) | |
615 return | |
616 returns = self.nodes_of_class(Return, skip_klass=Function) | |
617 for returnnode in returns: | |
618 if returnnode.value is None: | |
619 yield Const(None) | |
620 else: | |
621 try: | |
622 for infered in returnnode.value.infer(context): | |
623 yield infered | |
624 except InferenceError: | |
625 yield YES | |
626 | |
627 | |
628 def _rec_get_names(args, names=None): | |
629 """return a list of all argument names""" | |
630 if names is None: | |
631 names = [] | |
632 for arg in args: | |
633 if isinstance(arg, Tuple): | |
634 _rec_get_names(arg.elts, names) | |
635 else: | |
636 names.append(arg.name) | |
637 return names | |
638 | |
639 | |
640 # Class ###################################################################### | |
641 | |
642 def _class_type(klass, ancestors=None): | |
643 """return a Class node type to differ metaclass, interface and exception | |
644 from 'regular' classes | |
645 """ | |
646 # XXX we have to store ancestors in case we have a ancestor loop | |
647 if klass._type is not None: | |
648 return klass._type | |
649 if klass.name == 'type': | |
650 klass._type = 'metaclass' | |
651 elif klass.name.endswith('Interface'): | |
652 klass._type = 'interface' | |
653 elif klass.name.endswith('Exception'): | |
654 klass._type = 'exception' | |
655 else: | |
656 if ancestors is None: | |
657 ancestors = set() | |
658 if klass in ancestors: | |
659 # XXX we are in loop ancestors, and have found no type | |
660 klass._type = 'class' | |
661 return 'class' | |
662 ancestors.add(klass) | |
663 # print >> sys.stderr, '_class_type', repr(klass) | |
664 for base in klass.ancestors(recurs=False): | |
665 if _class_type(base, ancestors) != 'class': | |
666 klass._type = base.type | |
667 break | |
668 if klass._type is None: | |
669 klass._type = 'class' | |
670 return klass._type | |
671 | |
672 def _iface_hdlr(iface_node): | |
673 """a handler function used by interfaces to handle suspicious | |
674 interface nodes | |
675 """ | |
676 return True | |
677 | |
678 | |
679 class Class(Statement, LocalsDictNodeNG, FilterStmtsMixin): | |
680 | |
681 # some of the attributes below are set by the builder module or | |
682 # by a raw factories | |
683 | |
684 # a dictionary of class instances attributes | |
685 _astng_fields = ('decorators', 'bases', 'body') # name | |
686 | |
687 decorators = None | |
688 special_attributes = set(('__name__', '__doc__', '__dict__', '__module__', | |
689 '__bases__', '__mro__', '__subclasses__')) | |
690 blockstart_tolineno = None | |
691 | |
692 _type = None | |
693 type = property(_class_type, | |
694 doc="class'type, possible values are 'class' | " | |
695 "'metaclass' | 'interface' | 'exception'") | |
696 | |
697 def __init__(self, name, doc): | |
698 self.instance_attrs = {} | |
699 self.locals = {} | |
700 self.bases = [] | |
701 self.body = [] | |
702 self.name = name | |
703 self.doc = doc | |
704 | |
705 def _newstyle_impl(self, context=None): | |
706 if context is None: | |
707 context = InferenceContext() | |
708 if self._newstyle is not None: | |
709 return self._newstyle | |
710 for base in self.ancestors(recurs=False, context=context): | |
711 if base._newstyle_impl(context): | |
712 self._newstyle = True | |
713 break | |
714 if self._newstyle is None: | |
715 self._newstyle = False | |
716 return self._newstyle | |
717 | |
718 _newstyle = None | |
719 newstyle = property(_newstyle_impl, | |
720 doc="boolean indicating if it's a new style class" | |
721 "or not") | |
722 | |
723 def set_line_info(self, lastchild): | |
724 self.fromlineno = self.lineno | |
725 self.blockstart_tolineno = self.bases and self.bases[-1].tolineno or sel
f.fromlineno | |
726 if lastchild is not None: | |
727 self.tolineno = lastchild.tolineno | |
728 # else this is a class with only a docstring, then tolineno is (should b
e) already ok | |
729 | |
730 def block_range(self, lineno): | |
731 """return block line numbers. | |
732 | |
733 start from the "class" position whatever the given lineno | |
734 """ | |
735 return self.fromlineno, self.tolineno | |
736 | |
737 def pytype(self): | |
738 if self.newstyle: | |
739 return '%s.type' % BUILTINS_MODULE | |
740 return '%s.classobj' % BUILTINS_MODULE | |
741 | |
742 def display_type(self): | |
743 return 'Class' | |
744 | |
745 def callable(self): | |
746 return True | |
747 | |
748 def infer_call_result(self, caller, context=None): | |
749 """infer what a class is returning when called""" | |
750 yield Instance(self) | |
751 | |
752 def scope_lookup(self, node, name, offset=0): | |
753 if node in self.bases: | |
754 frame = self.parent.frame() | |
755 # line offset to avoid that class A(A) resolve the ancestor to | |
756 # the defined class | |
757 offset = -1 | |
758 else: | |
759 frame = self | |
760 return frame._scope_lookup(node, name, offset) | |
761 | |
762 # list of parent class as a list of string (i.e. names as they appear | |
763 # in the class definition) XXX bw compat | |
764 def basenames(self): | |
765 return [bnode.as_string() for bnode in self.bases] | |
766 basenames = property(basenames) | |
767 | |
768 def ancestors(self, recurs=True, context=None): | |
769 """return an iterator on the node base classes in a prefixed | |
770 depth first order | |
771 | |
772 :param recurs: | |
773 boolean indicating if it should recurse or return direct | |
774 ancestors only | |
775 """ | |
776 # FIXME: should be possible to choose the resolution order | |
777 # XXX inference make infinite loops possible here (see BaseTransformer | |
778 # manipulation in the builder module for instance) | |
779 yielded = set([self]) | |
780 if context is None: | |
781 context = InferenceContext() | |
782 for stmt in self.bases: | |
783 with context.restore_path(): | |
784 try: | |
785 for baseobj in stmt.infer(context): | |
786 if not isinstance(baseobj, Class): | |
787 # duh ? | |
788 continue | |
789 if baseobj in yielded: | |
790 continue # cf xxx above | |
791 yielded.add(baseobj) | |
792 yield baseobj | |
793 if recurs: | |
794 for grandpa in baseobj.ancestors(True, context): | |
795 if grandpa in yielded: | |
796 continue # cf xxx above | |
797 yielded.add(grandpa) | |
798 yield grandpa | |
799 except InferenceError: | |
800 # XXX log error ? | |
801 continue | |
802 | |
803 def local_attr_ancestors(self, name, context=None): | |
804 """return an iterator on astng representation of parent classes | |
805 which have <name> defined in their locals | |
806 """ | |
807 for astng in self.ancestors(context=context): | |
808 if name in astng: | |
809 yield astng | |
810 | |
811 def instance_attr_ancestors(self, name, context=None): | |
812 """return an iterator on astng representation of parent classes | |
813 which have <name> defined in their instance attribute dictionary | |
814 """ | |
815 for astng in self.ancestors(context=context): | |
816 if name in astng.instance_attrs: | |
817 yield astng | |
818 | |
819 def has_base(self, node): | |
820 return node in self.bases | |
821 | |
822 def local_attr(self, name, context=None): | |
823 """return the list of assign node associated to name in this class | |
824 locals or in its parents | |
825 | |
826 :raises `NotFoundError`: | |
827 if no attribute with this name has been find in this class or | |
828 its parent classes | |
829 """ | |
830 try: | |
831 return self.locals[name] | |
832 except KeyError: | |
833 # get if from the first parent implementing it if any | |
834 for class_node in self.local_attr_ancestors(name, context): | |
835 return class_node.locals[name] | |
836 raise NotFoundError(name) | |
837 local_attr = remove_nodes(local_attr, DelAttr) | |
838 | |
839 def instance_attr(self, name, context=None): | |
840 """return the astng nodes associated to name in this class instance | |
841 attributes dictionary and in its parents | |
842 | |
843 :raises `NotFoundError`: | |
844 if no attribute with this name has been find in this class or | |
845 its parent classes | |
846 """ | |
847 values = self.instance_attrs.get(name, []) | |
848 # get all values from parents | |
849 for class_node in self.instance_attr_ancestors(name, context): | |
850 values += class_node.instance_attrs[name] | |
851 if not values: | |
852 raise NotFoundError(name) | |
853 return values | |
854 instance_attr = remove_nodes(instance_attr, DelAttr) | |
855 | |
856 def instanciate_class(self): | |
857 """return Instance of Class node, else return self""" | |
858 return Instance(self) | |
859 | |
860 def getattr(self, name, context=None): | |
861 """this method doesn't look in the instance_attrs dictionary since it's | |
862 done by an Instance proxy at inference time. | |
863 | |
864 It may return a YES object if the attribute has not been actually | |
865 found but a __getattr__ or __getattribute__ method is defined | |
866 """ | |
867 values = self.locals.get(name, []) | |
868 if name in self.special_attributes: | |
869 if name == '__module__': | |
870 return [cf(self.root().qname())] + values | |
871 # FIXME : what is expected by passing the list of ancestors to cf: | |
872 # you can just do [cf(tuple())] + values without breaking any test | |
873 # this is ticket http://www.logilab.org/ticket/52785 | |
874 if name == '__bases__': | |
875 return [cf(tuple(self.ancestors(recurs=False, context=context)))
] + values | |
876 # XXX need proper meta class handling + MRO implementation | |
877 if name == '__mro__' and self.newstyle: | |
878 # XXX mro is read-only but that's not our job to detect that | |
879 return [cf(tuple(self.ancestors(recurs=True, context=context)))]
+ values | |
880 return std_special_attributes(self, name) | |
881 # don't modify the list in self.locals! | |
882 values = list(values) | |
883 for classnode in self.ancestors(recurs=True, context=context): | |
884 values += classnode.locals.get(name, []) | |
885 if not values: | |
886 raise NotFoundError(name) | |
887 return values | |
888 | |
889 def igetattr(self, name, context=None): | |
890 """inferred getattr, need special treatment in class to handle | |
891 descriptors | |
892 """ | |
893 # set lookup name since this is necessary to infer on import nodes for | |
894 # instance | |
895 context = copy_context(context) | |
896 context.lookupname = name | |
897 try: | |
898 for infered in _infer_stmts(self.getattr(name, context), context, | |
899 frame=self): | |
900 # yield YES object instead of descriptors when necessary | |
901 if not isinstance(infered, Const) and isinstance(infered, Instan
ce): | |
902 try: | |
903 infered._proxied.getattr('__get__', context) | |
904 except NotFoundError: | |
905 yield infered | |
906 else: | |
907 yield YES | |
908 else: | |
909 yield function_to_method(infered, self) | |
910 except NotFoundError: | |
911 if not name.startswith('__') and self.has_dynamic_getattr(context): | |
912 # class handle some dynamic attributes, return a YES object | |
913 yield YES | |
914 else: | |
915 raise InferenceError(name) | |
916 | |
917 def has_dynamic_getattr(self, context=None): | |
918 """return True if the class has a custom __getattr__ or | |
919 __getattribute__ method | |
920 """ | |
921 # need to explicitly handle optparse.Values (setattr is not detected) | |
922 if self.name == 'Values' and self.root().name == 'optparse': | |
923 return True | |
924 try: | |
925 self.getattr('__getattr__', context) | |
926 return True | |
927 except NotFoundError: | |
928 #if self.newstyle: XXX cause an infinite recursion error | |
929 try: | |
930 getattribute = self.getattr('__getattribute__', context)[0] | |
931 if getattribute.root().name != BUILTINS_NAME: | |
932 # class has a custom __getattribute__ defined | |
933 return True | |
934 except NotFoundError: | |
935 pass | |
936 return False | |
937 | |
938 def methods(self): | |
939 """return an iterator on all methods defined in the class and | |
940 its ancestors | |
941 """ | |
942 done = {} | |
943 for astng in chain(iter((self,)), self.ancestors()): | |
944 for meth in astng.mymethods(): | |
945 if meth.name in done: | |
946 continue | |
947 done[meth.name] = None | |
948 yield meth | |
949 | |
950 def mymethods(self): | |
951 """return an iterator on all methods defined in the class""" | |
952 for member in self.values(): | |
953 if isinstance(member, Function): | |
954 yield member | |
955 | |
956 def interfaces(self, herited=True, handler_func=_iface_hdlr): | |
957 """return an iterator on interfaces implemented by the given | |
958 class node | |
959 """ | |
960 # FIXME: what if __implements__ = (MyIFace, MyParent.__implements__)... | |
961 try: | |
962 implements = Instance(self).getattr('__implements__')[0] | |
963 except NotFoundError: | |
964 return | |
965 if not herited and not implements.frame() is self: | |
966 return | |
967 found = set() | |
968 missing = False | |
969 for iface in unpack_infer(implements): | |
970 if iface is YES: | |
971 missing = True | |
972 continue | |
973 if not iface in found and handler_func(iface): | |
974 found.add(iface) | |
975 yield iface | |
976 if missing: | |
977 raise InferenceError() | |
OLD | NEW |