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

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

Powered by Google App Engine
This is Rietveld 408576698