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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: third_party/logilab/logilab/astroid/bases.py
diff --git a/third_party/logilab/logilab/astroid/bases.py b/third_party/logilab/logilab/astroid/bases.py
new file mode 100644
index 0000000000000000000000000000000000000000..f1f4cc4b0b35f6e9012c76cb8ba4e26e1e8fe36e
--- /dev/null
+++ b/third_party/logilab/logilab/astroid/bases.py
@@ -0,0 +1,665 @@
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of astroid.
+#
+# astroid is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 2.1 of the License, or (at your
+# option) any later version.
+#
+# astroid is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+"""This module contains base classes and functions for the nodes and some
+inference utils.
+"""
+
+__docformat__ = "restructuredtext en"
+
+import sys
+from contextlib import contextmanager
+
+from logilab.common.decorators import cachedproperty
+
+from astroid.exceptions import (InferenceError, AstroidError, NotFoundError,
+ UnresolvableName, UseInferenceDefault)
+
+
+if sys.version_info >= (3, 0):
+ BUILTINS = 'builtins'
+else:
+ BUILTINS = '__builtin__'
+
+
+class Proxy(object):
+ """a simple proxy object"""
+
+ _proxied = None # proxied object may be set by class or by instance
+
+ def __init__(self, proxied=None):
+ if proxied is not None:
+ self._proxied = proxied
+
+ def __getattr__(self, name):
+ if name == '_proxied':
+ return getattr(self.__class__, '_proxied')
+ if name in self.__dict__:
+ return self.__dict__[name]
+ return getattr(self._proxied, name)
+
+ def infer(self, context=None):
+ yield self
+
+
+# Inference ##################################################################
+
+MISSING = object()
+
+
+class InferenceContext(object):
+ __slots__ = ('path', 'callcontext', 'boundnode', 'infered')
+
+ def __init__(self,
+ path=None, callcontext=None, boundnode=None, infered=None):
+ if path is None:
+ self.path = frozenset()
+ else:
+ self.path = path
+ self.callcontext = callcontext
+ self.boundnode = boundnode
+ if infered is None:
+ self.infered = {}
+ else:
+ self.infered = infered
+
+ def push(self, key):
+ # This returns a NEW context with the same attributes, but a new key
+ # added to `path`. The intention is that it's only passed to callees
+ # and then destroyed; otherwise scope() may not work correctly.
+ # The cache will be shared, since it's the same exact dict.
+ if key in self.path:
+ # End the containing generator
+ raise StopIteration
+
+ return InferenceContext(
+ self.path.union([key]),
+ self.callcontext,
+ self.boundnode,
+ self.infered,
+ )
+
+ @contextmanager
+ def scope(self, callcontext=MISSING, boundnode=MISSING):
+ try:
+ orig = self.callcontext, self.boundnode
+ if callcontext is not MISSING:
+ self.callcontext = callcontext
+ if boundnode is not MISSING:
+ self.boundnode = boundnode
+ yield
+ finally:
+ self.callcontext, self.boundnode = orig
+
+ def cache_generator(self, key, generator):
+ results = []
+ for result in generator:
+ results.append(result)
+ yield result
+
+ self.infered[key] = tuple(results)
+ return
+
+
+def _infer_stmts(stmts, context, frame=None, lookupname=None):
+ """return an iterator on statements inferred by each statement in <stmts>
+ """
+ stmt = None
+ infered = False
+ if context is None:
+ context = InferenceContext()
+ for stmt in stmts:
+ if stmt is YES:
+ yield stmt
+ infered = True
+ continue
+
+ kw = {}
+ infered_name = stmt._infer_name(frame, lookupname)
+ if infered_name is not None:
+ # only returns not None if .infer() accepts a lookupname kwarg
+ kw['lookupname'] = infered_name
+
+ try:
+ for infered in stmt.infer(context, **kw):
+ yield infered
+ infered = True
+ except UnresolvableName:
+ continue
+ except InferenceError:
+ yield YES
+ infered = True
+ if not infered:
+ raise InferenceError(str(stmt))
+
+
+# special inference objects (e.g. may be returned as nodes by .infer()) #######
+
+class _Yes(object):
+ """a yes object"""
+ def __repr__(self):
+ return 'YES'
+ def __getattribute__(self, name):
+ if name == 'next':
+ raise AttributeError('next method should not be called')
+ if name.startswith('__') and name.endswith('__'):
+ # to avoid inspection pb
+ return super(_Yes, self).__getattribute__(name)
+ return self
+ def __call__(self, *args, **kwargs):
+ return self
+
+
+YES = _Yes()
+
+
+class Instance(Proxy):
+ """a special node representing a class instance"""
+ def getattr(self, name, context=None, lookupclass=True):
+ try:
+ values = self._proxied.instance_attr(name, context)
+ except NotFoundError:
+ if name == '__class__':
+ return [self._proxied]
+ if lookupclass:
+ # class attributes not available through the instance
+ # unless they are explicitly defined
+ if name in ('__name__', '__bases__', '__mro__', '__subclasses__'):
+ return self._proxied.local_attr(name)
+ return self._proxied.getattr(name, context)
+ raise NotFoundError(name)
+ # since we've no context information, return matching class members as
+ # well
+ if lookupclass:
+ try:
+ return values + self._proxied.getattr(name, context)
+ except NotFoundError:
+ pass
+ return values
+
+ def igetattr(self, name, context=None):
+ """inferred getattr"""
+ if not context:
+ context = InferenceContext()
+ try:
+ # avoid recursively inferring the same attr on the same class
+ new_context = context.push((self._proxied, name))
+ # XXX frame should be self._proxied, or not ?
+ get_attr = self.getattr(name, new_context, lookupclass=False)
+ return _infer_stmts(
+ self._wrap_attr(get_attr, new_context),
+ new_context,
+ frame=self,
+ )
+ except NotFoundError:
+ try:
+ # fallback to class'igetattr since it has some logic to handle
+ # descriptors
+ return self._wrap_attr(self._proxied.igetattr(name, context),
+ context)
+ except NotFoundError:
+ raise InferenceError(name)
+
+ def _wrap_attr(self, attrs, context=None):
+ """wrap bound methods of attrs in a InstanceMethod proxies"""
+ for attr in attrs:
+ if isinstance(attr, UnboundMethod):
+ if BUILTINS + '.property' in attr.decoratornames():
+ for infered in attr.infer_call_result(self, context):
+ yield infered
+ else:
+ yield BoundMethod(attr, self)
+ else:
+ yield attr
+
+ def infer_call_result(self, caller, context=None):
+ """infer what a class instance is returning when called"""
+ infered = False
+ for node in self._proxied.igetattr('__call__', context):
+ if node is YES:
+ continue
+ for res in node.infer_call_result(caller, context):
+ infered = True
+ yield res
+ if not infered:
+ raise InferenceError()
+
+ def __repr__(self):
+ return '<Instance of %s.%s at 0x%s>' % (self._proxied.root().name,
+ self._proxied.name,
+ id(self))
+ def __str__(self):
+ return 'Instance of %s.%s' % (self._proxied.root().name,
+ self._proxied.name)
+
+ def callable(self):
+ try:
+ self._proxied.getattr('__call__')
+ return True
+ except NotFoundError:
+ return False
+
+ def pytype(self):
+ return self._proxied.qname()
+
+ def display_type(self):
+ return 'Instance of'
+
+
+class UnboundMethod(Proxy):
+ """a special node representing a method not bound to an instance"""
+ def __repr__(self):
+ frame = self._proxied.parent.frame()
+ return '<%s %s of %s at 0x%s' % (self.__class__.__name__,
+ self._proxied.name,
+ frame.qname(), id(self))
+
+ def is_bound(self):
+ return False
+
+ def getattr(self, name, context=None):
+ if name == 'im_func':
+ return [self._proxied]
+ return super(UnboundMethod, self).getattr(name, context)
+
+ def igetattr(self, name, context=None):
+ if name == 'im_func':
+ return iter((self._proxied,))
+ return super(UnboundMethod, self).igetattr(name, context)
+
+ def infer_call_result(self, caller, context):
+ # If we're unbound method __new__ of builtin object, the result is an
+ # instance of the class given as first argument.
+ if (self._proxied.name == '__new__' and
+ self._proxied.parent.frame().qname() == '%s.object' % BUILTINS):
+ infer = caller.args[0].infer() if caller.args else []
+ return ((x is YES and x or Instance(x)) for x in infer)
+ return self._proxied.infer_call_result(caller, context)
+
+
+class BoundMethod(UnboundMethod):
+ """a special node representing a method bound to an instance"""
+ def __init__(self, proxy, bound):
+ UnboundMethod.__init__(self, proxy)
+ self.bound = bound
+
+ def is_bound(self):
+ return True
+
+ def infer_call_result(self, caller, context):
+ with context.scope(boundnode=self.bound):
+ for infered in self._proxied.infer_call_result(caller, context):
+ yield infered
+
+
+class Generator(Instance):
+ """a special node representing a generator.
+
+ Proxied class is set once for all in raw_building.
+ """
+ def callable(self):
+ return False
+
+ def pytype(self):
+ return '%s.generator' % BUILTINS
+
+ def display_type(self):
+ return 'Generator'
+
+ def __repr__(self):
+ return '<Generator(%s) l.%s at 0x%s>' % (self._proxied.name, self.lineno, id(self))
+
+ def __str__(self):
+ return 'Generator(%s)' % (self._proxied.name)
+
+
+# decorators ##################################################################
+
+def path_wrapper(func):
+ """return the given infer function wrapped to handle the path"""
+ def wrapped(node, context=None, _func=func, **kwargs):
+ """wrapper function handling context"""
+ if context is None:
+ context = InferenceContext()
+ context = context.push((node, kwargs.get('lookupname')))
+
+ yielded = set()
+ for res in _func(node, context, **kwargs):
+ # unproxy only true instance, not const, tuple, dict...
+ if res.__class__ is Instance:
+ ares = res._proxied
+ else:
+ ares = res
+ if not ares in yielded:
+ yield res
+ yielded.add(ares)
+ return wrapped
+
+def yes_if_nothing_infered(func):
+ def wrapper(*args, **kwargs):
+ infered = False
+ for node in func(*args, **kwargs):
+ infered = True
+ yield node
+ if not infered:
+ yield YES
+ return wrapper
+
+def raise_if_nothing_infered(func):
+ def wrapper(*args, **kwargs):
+ infered = False
+ for node in func(*args, **kwargs):
+ infered = True
+ yield node
+ if not infered:
+ raise InferenceError()
+ return wrapper
+
+
+# Node ######################################################################
+
+class NodeNG(object):
+ """Base Class for all Astroid node classes.
+
+ It represents a node of the new abstract syntax tree.
+ """
+ is_statement = False
+ optional_assign = False # True for For (and for Comprehension if py <3.0)
+ is_function = False # True for Function nodes
+ # attributes below are set by the builder module or by raw factories
+ lineno = None
+ fromlineno = None
+ tolineno = None
+ col_offset = None
+ # parent node in the tree
+ parent = None
+ # attributes containing child node(s) redefined in most concrete classes:
+ _astroid_fields = ()
+ # instance specific inference function infer(node, context)
+ _explicit_inference = None
+
+ def infer(self, context=None, **kwargs):
+ """main interface to the interface system, return a generator on infered
+ values.
+
+ If the instance has some explicit inference function set, it will be
+ called instead of the default interface.
+ """
+ if self._explicit_inference is not None:
+ # explicit_inference is not bound, give it self explicitly
+ try:
+ return self._explicit_inference(self, context, **kwargs)
+ except UseInferenceDefault:
+ pass
+
+ if not context:
+ return self._infer(context, **kwargs)
+
+ key = (self, kwargs.get('lookupname'), context.callcontext, context.boundnode)
+ if key in context.infered:
+ return iter(context.infered[key])
+
+ return context.cache_generator(key, self._infer(context, **kwargs))
+
+ def _repr_name(self):
+ """return self.name or self.attrname or '' for nice representation"""
+ return getattr(self, 'name', getattr(self, 'attrname', ''))
+
+ def __str__(self):
+ return '%s(%s)' % (self.__class__.__name__, self._repr_name())
+
+ def __repr__(self):
+ return '<%s(%s) l.%s [%s] at 0x%x>' % (self.__class__.__name__,
+ self._repr_name(),
+ self.fromlineno,
+ self.root().name,
+ id(self))
+
+
+ def accept(self, visitor):
+ func = getattr(visitor, "visit_" + self.__class__.__name__.lower())
+ return func(self)
+
+ def get_children(self):
+ for field in self._astroid_fields:
+ attr = getattr(self, field)
+ if attr is None:
+ continue
+ if isinstance(attr, (list, tuple)):
+ for elt in attr:
+ yield elt
+ else:
+ yield attr
+
+ def last_child(self):
+ """an optimized version of list(get_children())[-1]"""
+ for field in self._astroid_fields[::-1]:
+ attr = getattr(self, field)
+ if not attr: # None or empty listy / tuple
+ continue
+ if attr.__class__ in (list, tuple):
+ return attr[-1]
+ else:
+ return attr
+ return None
+
+ def parent_of(self, node):
+ """return true if i'm a parent of the given node"""
+ parent = node.parent
+ while parent is not None:
+ if self is parent:
+ return True
+ parent = parent.parent
+ return False
+
+ def statement(self):
+ """return the first parent node marked as statement node"""
+ if self.is_statement:
+ return self
+ return self.parent.statement()
+
+ def frame(self):
+ """return the first parent frame node (i.e. Module, Function or Class)
+ """
+ return self.parent.frame()
+
+ def scope(self):
+ """return the first node defining a new scope (i.e. Module, Function,
+ Class, Lambda but also GenExpr)
+ """
+ return self.parent.scope()
+
+ def root(self):
+ """return the root node of the tree, (i.e. a Module)"""
+ if self.parent:
+ return self.parent.root()
+ return self
+
+ def child_sequence(self, child):
+ """search for the right sequence where the child lies in"""
+ for field in self._astroid_fields:
+ node_or_sequence = getattr(self, field)
+ if node_or_sequence is child:
+ return [node_or_sequence]
+ # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
+ if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence:
+ return node_or_sequence
+ else:
+ msg = 'Could not find %s in %s\'s children'
+ raise AstroidError(msg % (repr(child), repr(self)))
+
+ def locate_child(self, child):
+ """return a 2-uple (child attribute name, sequence or node)"""
+ for field in self._astroid_fields:
+ node_or_sequence = getattr(self, field)
+ # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
+ if child is node_or_sequence:
+ return field, child
+ if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence:
+ return field, node_or_sequence
+ msg = 'Could not find %s in %s\'s children'
+ raise AstroidError(msg % (repr(child), repr(self)))
+ # FIXME : should we merge child_sequence and locate_child ? locate_child
+ # is only used in are_exclusive, child_sequence one time in pylint.
+
+ def next_sibling(self):
+ """return the next sibling statement"""
+ return self.parent.next_sibling()
+
+ def previous_sibling(self):
+ """return the previous sibling statement"""
+ return self.parent.previous_sibling()
+
+ def nearest(self, nodes):
+ """return the node which is the nearest before this one in the
+ given list of nodes
+ """
+ myroot = self.root()
+ mylineno = self.fromlineno
+ nearest = None, 0
+ for node in nodes:
+ assert node.root() is myroot, \
+ 'nodes %s and %s are not from the same module' % (self, node)
+ lineno = node.fromlineno
+ if node.fromlineno > mylineno:
+ break
+ if lineno > nearest[1]:
+ nearest = node, lineno
+ # FIXME: raise an exception if nearest is None ?
+ return nearest[0]
+
+ # these are lazy because they're relatively expensive to compute for every
+ # single node, and they rarely get looked at
+
+ @cachedproperty
+ def fromlineno(self):
+ if self.lineno is None:
+ return self._fixed_source_line()
+ else:
+ return self.lineno
+
+ @cachedproperty
+ def tolineno(self):
+ if not self._astroid_fields:
+ # can't have children
+ lastchild = None
+ else:
+ lastchild = self.last_child()
+ if lastchild is None:
+ return self.fromlineno
+ else:
+ return lastchild.tolineno
+
+ # TODO / FIXME:
+ assert self.fromlineno is not None, self
+ assert self.tolineno is not None, self
+
+ def _fixed_source_line(self):
+ """return the line number where the given node appears
+
+ we need this method since not all nodes have the lineno attribute
+ correctly set...
+ """
+ line = self.lineno
+ _node = self
+ try:
+ while line is None:
+ _node = next(_node.get_children())
+ line = _node.lineno
+ except StopIteration:
+ _node = self.parent
+ while _node and line is None:
+ line = _node.lineno
+ _node = _node.parent
+ return line
+
+ def block_range(self, lineno):
+ """handle block line numbers range for non block opening statements
+ """
+ return lineno, self.tolineno
+
+ def set_local(self, name, stmt):
+ """delegate to a scoped parent handling a locals dictionary"""
+ self.parent.set_local(name, stmt)
+
+ def nodes_of_class(self, klass, skip_klass=None):
+ """return an iterator on nodes which are instance of the given class(es)
+
+ klass may be a class object or a tuple of class objects
+ """
+ if isinstance(self, klass):
+ yield self
+ for child_node in self.get_children():
+ if skip_klass is not None and isinstance(child_node, skip_klass):
+ continue
+ for matching in child_node.nodes_of_class(klass, skip_klass):
+ yield matching
+
+ def _infer_name(self, frame, name):
+ # overridden for From, Import, Global, TryExcept and Arguments
+ return None
+
+ def _infer(self, context=None):
+ """we don't know how to resolve a statement by default"""
+ # this method is overridden by most concrete classes
+ raise InferenceError(self.__class__.__name__)
+
+ def infered(self):
+ '''return list of infered values for a more simple inference usage'''
+ return list(self.infer())
+
+ def instanciate_class(self):
+ """instanciate a node if it is a Class node, else return self"""
+ return self
+
+ def has_base(self, node):
+ return False
+
+ def callable(self):
+ return False
+
+ def eq(self, value):
+ return False
+
+ def as_string(self):
+ from astroid.as_string import to_code
+ return to_code(self)
+
+ def repr_tree(self, ids=False):
+ from astroid.as_string import dump
+ return dump(self)
+
+
+class Statement(NodeNG):
+ """Statement node adding a few attributes"""
+ is_statement = True
+
+ def next_sibling(self):
+ """return the next sibling statement"""
+ stmts = self.parent.child_sequence(self)
+ index = stmts.index(self)
+ try:
+ return stmts[index +1]
+ except IndexError:
+ pass
+
+ def previous_sibling(self):
+ """return the previous sibling statement"""
+ stmts = self.parent.child_sequence(self)
+ index = stmts.index(self)
+ if index >= 1:
+ return stmts[index -1]
« 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