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

Side by Side Diff: third_party/logilab/logilab/astroid/bases.py

Issue 1920403002: [content/test/gpu] Run pylint check of gpu tests in unittest instead of PRESUBMIT (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update path to LICENSE.txt of logilab/README.chromium Created 4 years, 7 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
OLDNEW
(Empty)
1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
3 #
4 # This file is part of astroid.
5 #
6 # astroid is free software: you can redistribute it and/or modify it
7 # under the terms of the GNU Lesser General Public License as published by the
8 # Free Software Foundation, either version 2.1 of the License, or (at your
9 # option) any later version.
10 #
11 # astroid is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
14 # for more details.
15 #
16 # You should have received a copy of the GNU Lesser General Public License along
17 # with astroid. If not, see <http://www.gnu.org/licenses/>.
18 """This module contains base classes and functions for the nodes and some
19 inference utils.
20 """
21
22 __docformat__ = "restructuredtext en"
23
24 import sys
25 from contextlib import contextmanager
26
27 from logilab.common.decorators import cachedproperty
28
29 from astroid.exceptions import (InferenceError, AstroidError, NotFoundError,
30 UnresolvableName, UseInferenceDefault)
31
32
33 if sys.version_info >= (3, 0):
34 BUILTINS = 'builtins'
35 else:
36 BUILTINS = '__builtin__'
37
38
39 class Proxy(object):
40 """a simple proxy object"""
41
42 _proxied = None # proxied object may be set by class or by instance
43
44 def __init__(self, proxied=None):
45 if proxied is not None:
46 self._proxied = proxied
47
48 def __getattr__(self, name):
49 if name == '_proxied':
50 return getattr(self.__class__, '_proxied')
51 if name in self.__dict__:
52 return self.__dict__[name]
53 return getattr(self._proxied, name)
54
55 def infer(self, context=None):
56 yield self
57
58
59 # Inference ##################################################################
60
61 MISSING = object()
62
63
64 class InferenceContext(object):
65 __slots__ = ('path', 'callcontext', 'boundnode', 'infered')
66
67 def __init__(self,
68 path=None, callcontext=None, boundnode=None, infered=None):
69 if path is None:
70 self.path = frozenset()
71 else:
72 self.path = path
73 self.callcontext = callcontext
74 self.boundnode = boundnode
75 if infered is None:
76 self.infered = {}
77 else:
78 self.infered = infered
79
80 def push(self, key):
81 # This returns a NEW context with the same attributes, but a new key
82 # added to `path`. The intention is that it's only passed to callees
83 # and then destroyed; otherwise scope() may not work correctly.
84 # The cache will be shared, since it's the same exact dict.
85 if key in self.path:
86 # End the containing generator
87 raise StopIteration
88
89 return InferenceContext(
90 self.path.union([key]),
91 self.callcontext,
92 self.boundnode,
93 self.infered,
94 )
95
96 @contextmanager
97 def scope(self, callcontext=MISSING, boundnode=MISSING):
98 try:
99 orig = self.callcontext, self.boundnode
100 if callcontext is not MISSING:
101 self.callcontext = callcontext
102 if boundnode is not MISSING:
103 self.boundnode = boundnode
104 yield
105 finally:
106 self.callcontext, self.boundnode = orig
107
108 def cache_generator(self, key, generator):
109 results = []
110 for result in generator:
111 results.append(result)
112 yield result
113
114 self.infered[key] = tuple(results)
115 return
116
117
118 def _infer_stmts(stmts, context, frame=None, lookupname=None):
119 """return an iterator on statements inferred by each statement in <stmts>
120 """
121 stmt = None
122 infered = False
123 if context is None:
124 context = InferenceContext()
125 for stmt in stmts:
126 if stmt is YES:
127 yield stmt
128 infered = True
129 continue
130
131 kw = {}
132 infered_name = stmt._infer_name(frame, lookupname)
133 if infered_name is not None:
134 # only returns not None if .infer() accepts a lookupname kwarg
135 kw['lookupname'] = infered_name
136
137 try:
138 for infered in stmt.infer(context, **kw):
139 yield infered
140 infered = True
141 except UnresolvableName:
142 continue
143 except InferenceError:
144 yield YES
145 infered = True
146 if not infered:
147 raise InferenceError(str(stmt))
148
149
150 # special inference objects (e.g. may be returned as nodes by .infer()) #######
151
152 class _Yes(object):
153 """a yes object"""
154 def __repr__(self):
155 return 'YES'
156 def __getattribute__(self, name):
157 if name == 'next':
158 raise AttributeError('next method should not be called')
159 if name.startswith('__') and name.endswith('__'):
160 # to avoid inspection pb
161 return super(_Yes, self).__getattribute__(name)
162 return self
163 def __call__(self, *args, **kwargs):
164 return self
165
166
167 YES = _Yes()
168
169
170 class Instance(Proxy):
171 """a special node representing a class instance"""
172 def getattr(self, name, context=None, lookupclass=True):
173 try:
174 values = self._proxied.instance_attr(name, context)
175 except NotFoundError:
176 if name == '__class__':
177 return [self._proxied]
178 if lookupclass:
179 # class attributes not available through the instance
180 # unless they are explicitly defined
181 if name in ('__name__', '__bases__', '__mro__', '__subclasses__' ):
182 return self._proxied.local_attr(name)
183 return self._proxied.getattr(name, context)
184 raise NotFoundError(name)
185 # since we've no context information, return matching class members as
186 # well
187 if lookupclass:
188 try:
189 return values + self._proxied.getattr(name, context)
190 except NotFoundError:
191 pass
192 return values
193
194 def igetattr(self, name, context=None):
195 """inferred getattr"""
196 if not context:
197 context = InferenceContext()
198 try:
199 # avoid recursively inferring the same attr on the same class
200 new_context = context.push((self._proxied, name))
201 # XXX frame should be self._proxied, or not ?
202 get_attr = self.getattr(name, new_context, lookupclass=False)
203 return _infer_stmts(
204 self._wrap_attr(get_attr, new_context),
205 new_context,
206 frame=self,
207 )
208 except NotFoundError:
209 try:
210 # fallback to class'igetattr since it has some logic to handle
211 # descriptors
212 return self._wrap_attr(self._proxied.igetattr(name, context),
213 context)
214 except NotFoundError:
215 raise InferenceError(name)
216
217 def _wrap_attr(self, attrs, context=None):
218 """wrap bound methods of attrs in a InstanceMethod proxies"""
219 for attr in attrs:
220 if isinstance(attr, UnboundMethod):
221 if BUILTINS + '.property' in attr.decoratornames():
222 for infered in attr.infer_call_result(self, context):
223 yield infered
224 else:
225 yield BoundMethod(attr, self)
226 else:
227 yield attr
228
229 def infer_call_result(self, caller, context=None):
230 """infer what a class instance is returning when called"""
231 infered = False
232 for node in self._proxied.igetattr('__call__', context):
233 if node is YES:
234 continue
235 for res in node.infer_call_result(caller, context):
236 infered = True
237 yield res
238 if not infered:
239 raise InferenceError()
240
241 def __repr__(self):
242 return '<Instance of %s.%s at 0x%s>' % (self._proxied.root().name,
243 self._proxied.name,
244 id(self))
245 def __str__(self):
246 return 'Instance of %s.%s' % (self._proxied.root().name,
247 self._proxied.name)
248
249 def callable(self):
250 try:
251 self._proxied.getattr('__call__')
252 return True
253 except NotFoundError:
254 return False
255
256 def pytype(self):
257 return self._proxied.qname()
258
259 def display_type(self):
260 return 'Instance of'
261
262
263 class UnboundMethod(Proxy):
264 """a special node representing a method not bound to an instance"""
265 def __repr__(self):
266 frame = self._proxied.parent.frame()
267 return '<%s %s of %s at 0x%s' % (self.__class__.__name__,
268 self._proxied.name,
269 frame.qname(), id(self))
270
271 def is_bound(self):
272 return False
273
274 def getattr(self, name, context=None):
275 if name == 'im_func':
276 return [self._proxied]
277 return super(UnboundMethod, self).getattr(name, context)
278
279 def igetattr(self, name, context=None):
280 if name == 'im_func':
281 return iter((self._proxied,))
282 return super(UnboundMethod, self).igetattr(name, context)
283
284 def infer_call_result(self, caller, context):
285 # If we're unbound method __new__ of builtin object, the result is an
286 # instance of the class given as first argument.
287 if (self._proxied.name == '__new__' and
288 self._proxied.parent.frame().qname() == '%s.object' % BUILTINS):
289 infer = caller.args[0].infer() if caller.args else []
290 return ((x is YES and x or Instance(x)) for x in infer)
291 return self._proxied.infer_call_result(caller, context)
292
293
294 class BoundMethod(UnboundMethod):
295 """a special node representing a method bound to an instance"""
296 def __init__(self, proxy, bound):
297 UnboundMethod.__init__(self, proxy)
298 self.bound = bound
299
300 def is_bound(self):
301 return True
302
303 def infer_call_result(self, caller, context):
304 with context.scope(boundnode=self.bound):
305 for infered in self._proxied.infer_call_result(caller, context):
306 yield infered
307
308
309 class Generator(Instance):
310 """a special node representing a generator.
311
312 Proxied class is set once for all in raw_building.
313 """
314 def callable(self):
315 return False
316
317 def pytype(self):
318 return '%s.generator' % BUILTINS
319
320 def display_type(self):
321 return 'Generator'
322
323 def __repr__(self):
324 return '<Generator(%s) l.%s at 0x%s>' % (self._proxied.name, self.lineno , id(self))
325
326 def __str__(self):
327 return 'Generator(%s)' % (self._proxied.name)
328
329
330 # decorators ##################################################################
331
332 def path_wrapper(func):
333 """return the given infer function wrapped to handle the path"""
334 def wrapped(node, context=None, _func=func, **kwargs):
335 """wrapper function handling context"""
336 if context is None:
337 context = InferenceContext()
338 context = context.push((node, kwargs.get('lookupname')))
339
340 yielded = set()
341 for res in _func(node, context, **kwargs):
342 # unproxy only true instance, not const, tuple, dict...
343 if res.__class__ is Instance:
344 ares = res._proxied
345 else:
346 ares = res
347 if not ares in yielded:
348 yield res
349 yielded.add(ares)
350 return wrapped
351
352 def yes_if_nothing_infered(func):
353 def wrapper(*args, **kwargs):
354 infered = False
355 for node in func(*args, **kwargs):
356 infered = True
357 yield node
358 if not infered:
359 yield YES
360 return wrapper
361
362 def raise_if_nothing_infered(func):
363 def wrapper(*args, **kwargs):
364 infered = False
365 for node in func(*args, **kwargs):
366 infered = True
367 yield node
368 if not infered:
369 raise InferenceError()
370 return wrapper
371
372
373 # Node ######################################################################
374
375 class NodeNG(object):
376 """Base Class for all Astroid node classes.
377
378 It represents a node of the new abstract syntax tree.
379 """
380 is_statement = False
381 optional_assign = False # True for For (and for Comprehension if py <3.0)
382 is_function = False # True for Function nodes
383 # attributes below are set by the builder module or by raw factories
384 lineno = None
385 fromlineno = None
386 tolineno = None
387 col_offset = None
388 # parent node in the tree
389 parent = None
390 # attributes containing child node(s) redefined in most concrete classes:
391 _astroid_fields = ()
392 # instance specific inference function infer(node, context)
393 _explicit_inference = None
394
395 def infer(self, context=None, **kwargs):
396 """main interface to the interface system, return a generator on infered
397 values.
398
399 If the instance has some explicit inference function set, it will be
400 called instead of the default interface.
401 """
402 if self._explicit_inference is not None:
403 # explicit_inference is not bound, give it self explicitly
404 try:
405 return self._explicit_inference(self, context, **kwargs)
406 except UseInferenceDefault:
407 pass
408
409 if not context:
410 return self._infer(context, **kwargs)
411
412 key = (self, kwargs.get('lookupname'), context.callcontext, context.boun dnode)
413 if key in context.infered:
414 return iter(context.infered[key])
415
416 return context.cache_generator(key, self._infer(context, **kwargs))
417
418 def _repr_name(self):
419 """return self.name or self.attrname or '' for nice representation"""
420 return getattr(self, 'name', getattr(self, 'attrname', ''))
421
422 def __str__(self):
423 return '%s(%s)' % (self.__class__.__name__, self._repr_name())
424
425 def __repr__(self):
426 return '<%s(%s) l.%s [%s] at 0x%x>' % (self.__class__.__name__,
427 self._repr_name(),
428 self.fromlineno,
429 self.root().name,
430 id(self))
431
432
433 def accept(self, visitor):
434 func = getattr(visitor, "visit_" + self.__class__.__name__.lower())
435 return func(self)
436
437 def get_children(self):
438 for field in self._astroid_fields:
439 attr = getattr(self, field)
440 if attr is None:
441 continue
442 if isinstance(attr, (list, tuple)):
443 for elt in attr:
444 yield elt
445 else:
446 yield attr
447
448 def last_child(self):
449 """an optimized version of list(get_children())[-1]"""
450 for field in self._astroid_fields[::-1]:
451 attr = getattr(self, field)
452 if not attr: # None or empty listy / tuple
453 continue
454 if attr.__class__ in (list, tuple):
455 return attr[-1]
456 else:
457 return attr
458 return None
459
460 def parent_of(self, node):
461 """return true if i'm a parent of the given node"""
462 parent = node.parent
463 while parent is not None:
464 if self is parent:
465 return True
466 parent = parent.parent
467 return False
468
469 def statement(self):
470 """return the first parent node marked as statement node"""
471 if self.is_statement:
472 return self
473 return self.parent.statement()
474
475 def frame(self):
476 """return the first parent frame node (i.e. Module, Function or Class)
477 """
478 return self.parent.frame()
479
480 def scope(self):
481 """return the first node defining a new scope (i.e. Module, Function,
482 Class, Lambda but also GenExpr)
483 """
484 return self.parent.scope()
485
486 def root(self):
487 """return the root node of the tree, (i.e. a Module)"""
488 if self.parent:
489 return self.parent.root()
490 return self
491
492 def child_sequence(self, child):
493 """search for the right sequence where the child lies in"""
494 for field in self._astroid_fields:
495 node_or_sequence = getattr(self, field)
496 if node_or_sequence is child:
497 return [node_or_sequence]
498 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
499 if isinstance(node_or_sequence, (tuple, list)) and child in node_or_ sequence:
500 return node_or_sequence
501 else:
502 msg = 'Could not find %s in %s\'s children'
503 raise AstroidError(msg % (repr(child), repr(self)))
504
505 def locate_child(self, child):
506 """return a 2-uple (child attribute name, sequence or node)"""
507 for field in self._astroid_fields:
508 node_or_sequence = getattr(self, field)
509 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
510 if child is node_or_sequence:
511 return field, child
512 if isinstance(node_or_sequence, (tuple, list)) and child in node_or_ sequence:
513 return field, node_or_sequence
514 msg = 'Could not find %s in %s\'s children'
515 raise AstroidError(msg % (repr(child), repr(self)))
516 # FIXME : should we merge child_sequence and locate_child ? locate_child
517 # is only used in are_exclusive, child_sequence one time in pylint.
518
519 def next_sibling(self):
520 """return the next sibling statement"""
521 return self.parent.next_sibling()
522
523 def previous_sibling(self):
524 """return the previous sibling statement"""
525 return self.parent.previous_sibling()
526
527 def nearest(self, nodes):
528 """return the node which is the nearest before this one in the
529 given list of nodes
530 """
531 myroot = self.root()
532 mylineno = self.fromlineno
533 nearest = None, 0
534 for node in nodes:
535 assert node.root() is myroot, \
536 'nodes %s and %s are not from the same module' % (self, node)
537 lineno = node.fromlineno
538 if node.fromlineno > mylineno:
539 break
540 if lineno > nearest[1]:
541 nearest = node, lineno
542 # FIXME: raise an exception if nearest is None ?
543 return nearest[0]
544
545 # these are lazy because they're relatively expensive to compute for every
546 # single node, and they rarely get looked at
547
548 @cachedproperty
549 def fromlineno(self):
550 if self.lineno is None:
551 return self._fixed_source_line()
552 else:
553 return self.lineno
554
555 @cachedproperty
556 def tolineno(self):
557 if not self._astroid_fields:
558 # can't have children
559 lastchild = None
560 else:
561 lastchild = self.last_child()
562 if lastchild is None:
563 return self.fromlineno
564 else:
565 return lastchild.tolineno
566
567 # TODO / FIXME:
568 assert self.fromlineno is not None, self
569 assert self.tolineno is not None, self
570
571 def _fixed_source_line(self):
572 """return the line number where the given node appears
573
574 we need this method since not all nodes have the lineno attribute
575 correctly set...
576 """
577 line = self.lineno
578 _node = self
579 try:
580 while line is None:
581 _node = next(_node.get_children())
582 line = _node.lineno
583 except StopIteration:
584 _node = self.parent
585 while _node and line is None:
586 line = _node.lineno
587 _node = _node.parent
588 return line
589
590 def block_range(self, lineno):
591 """handle block line numbers range for non block opening statements
592 """
593 return lineno, self.tolineno
594
595 def set_local(self, name, stmt):
596 """delegate to a scoped parent handling a locals dictionary"""
597 self.parent.set_local(name, stmt)
598
599 def nodes_of_class(self, klass, skip_klass=None):
600 """return an iterator on nodes which are instance of the given class(es)
601
602 klass may be a class object or a tuple of class objects
603 """
604 if isinstance(self, klass):
605 yield self
606 for child_node in self.get_children():
607 if skip_klass is not None and isinstance(child_node, skip_klass):
608 continue
609 for matching in child_node.nodes_of_class(klass, skip_klass):
610 yield matching
611
612 def _infer_name(self, frame, name):
613 # overridden for From, Import, Global, TryExcept and Arguments
614 return None
615
616 def _infer(self, context=None):
617 """we don't know how to resolve a statement by default"""
618 # this method is overridden by most concrete classes
619 raise InferenceError(self.__class__.__name__)
620
621 def infered(self):
622 '''return list of infered values for a more simple inference usage'''
623 return list(self.infer())
624
625 def instanciate_class(self):
626 """instanciate a node if it is a Class node, else return self"""
627 return self
628
629 def has_base(self, node):
630 return False
631
632 def callable(self):
633 return False
634
635 def eq(self, value):
636 return False
637
638 def as_string(self):
639 from astroid.as_string import to_code
640 return to_code(self)
641
642 def repr_tree(self, ids=False):
643 from astroid.as_string import dump
644 return dump(self)
645
646
647 class Statement(NodeNG):
648 """Statement node adding a few attributes"""
649 is_statement = True
650
651 def next_sibling(self):
652 """return the next sibling statement"""
653 stmts = self.parent.child_sequence(self)
654 index = stmts.index(self)
655 try:
656 return stmts[index +1]
657 except IndexError:
658 pass
659
660 def previous_sibling(self):
661 """return the previous sibling statement"""
662 stmts = self.parent.child_sequence(self)
663 index = stmts.index(self)
664 if index >= 1:
665 return stmts[index -1]
OLDNEW
« no previous file with comments | « third_party/logilab/logilab/astroid/as_string.py ('k') | third_party/logilab/logilab/astroid/brain/builtin_inference.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698