Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(85)

Side by Side Diff: Tools/Scripts/webkitpy/thirdparty/logilab/astng/scoped_nodes.py

Issue 18418010: Check in the thirdparty libs needed for webkitpy. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698