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

Side by Side Diff: third_party/logilab/astng/scoped_nodes.py

Issue 739393004: Revert "Revert "pylint: upgrade to 1.3.1"" (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools/
Patch Set: Created 6 years 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
« no previous file with comments | « third_party/logilab/astng/rebuilder.py ('k') | third_party/logilab/astng/utils.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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()
OLDNEW
« no previous file with comments | « third_party/logilab/astng/rebuilder.py ('k') | third_party/logilab/astng/utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698