|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- # Copyright (c) 2015-2016 Cara Vinson <ceridwenv@gmail.com>
- # Copyright (c) 2015-2016 Claudiu Popa <pcmanticore@gmail.com>
-
- # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
- # For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
-
-
- """
- Inference objects are a way to represent composite AST nodes,
- which are used only as inference results, so they can't be found in the
- original AST tree. For instance, inferring the following frozenset use,
- leads to an inferred FrozenSet:
-
- Call(func=Name('frozenset'), args=Tuple(...))
- """
-
- import six
-
- from astroid import bases
- from astroid import decorators
- from astroid import exceptions
- from astroid import MANAGER
- from astroid import node_classes
- from astroid import scoped_nodes
- from astroid import util
-
-
- BUILTINS = six.moves.builtins.__name__
- objectmodel = util.lazy_import('interpreter.objectmodel')
-
-
- class FrozenSet(node_classes._BaseContainer):
- """class representing a FrozenSet composite node"""
-
- def pytype(self):
- return '%s.frozenset' % BUILTINS
-
- def _infer(self, context=None):
- yield self
-
- @decorators.cachedproperty
- def _proxied(self): # pylint: disable=method-hidden
- builtins = MANAGER.astroid_cache[BUILTINS]
- return builtins.getattr('frozenset')[0]
-
-
- class Super(node_classes.NodeNG):
- """Proxy class over a super call.
-
- This class offers almost the same behaviour as Python's super,
- which is MRO lookups for retrieving attributes from the parents.
-
- The *mro_pointer* is the place in the MRO from where we should
- start looking, not counting it. *mro_type* is the object which
- provides the MRO, it can be both a type or an instance.
- *self_class* is the class where the super call is, while
- *scope* is the function where the super call is.
- """
- # pylint: disable=unnecessary-lambda
- special_attributes = util.lazy_descriptor(lambda: objectmodel.SuperModel())
-
- # pylint: disable=super-init-not-called
- def __init__(self, mro_pointer, mro_type, self_class, scope):
- self.type = mro_type
- self.mro_pointer = mro_pointer
- self._class_based = False
- self._self_class = self_class
- self._scope = scope
-
- def _infer(self, context=None):
- yield self
-
- def super_mro(self):
- """Get the MRO which will be used to lookup attributes in this super."""
- if not isinstance(self.mro_pointer, scoped_nodes.ClassDef):
- raise exceptions.SuperError(
- "The first argument to super must be a subtype of "
- "type, not {mro_pointer}.", super_=self)
-
- if isinstance(self.type, scoped_nodes.ClassDef):
- # `super(type, type)`, most likely in a class method.
- self._class_based = True
- mro_type = self.type
- else:
- mro_type = getattr(self.type, '_proxied', None)
- if not isinstance(mro_type, (bases.Instance, scoped_nodes.ClassDef)):
- raise exceptions.SuperError(
- "The second argument to super must be an "
- "instance or subtype of type, not {type}.",
- super_=self)
-
- if not mro_type.newstyle:
- raise exceptions.SuperError("Unable to call super on old-style classes.", super_=self)
-
- mro = mro_type.mro()
- if self.mro_pointer not in mro:
- raise exceptions.SuperError(
- "The second argument to super must be an "
- "instance or subtype of type, not {type}.",
- super_=self)
-
- index = mro.index(self.mro_pointer)
- return mro[index + 1:]
-
- @decorators.cachedproperty
- def _proxied(self):
- builtins = MANAGER.astroid_cache[BUILTINS]
- return builtins.getattr('super')[0]
-
- def pytype(self):
- return '%s.super' % BUILTINS
-
- def display_type(self):
- return 'Super of'
-
- @property
- def name(self):
- """Get the name of the MRO pointer."""
- return self.mro_pointer.name
-
- def igetattr(self, name, context=None):
- """Retrieve the inferred values of the given attribute name."""
-
- if name in self.special_attributes:
- yield self.special_attributes.lookup(name)
- return
-
- try:
- mro = self.super_mro()
- # Don't let invalid MROs or invalid super calls
- # leak out as is from this function.
- except exceptions.SuperError as exc:
- util.reraise(exceptions.AttributeInferenceError(
- ('Lookup for {name} on {target!r} because super call {super!r} '
- 'is invalid.'),
- target=self, attribute=name, context=context, super_=exc.super_))
- except exceptions.MroError as exc:
- util.reraise(exceptions.AttributeInferenceError(
- ('Lookup for {name} on {target!r} failed because {cls!r} has an '
- 'invalid MRO.'),
- target=self, attribute=name, context=context, mros=exc.mros,
- cls=exc.cls))
- found = False
- for cls in mro:
- if name not in cls.locals:
- continue
-
- found = True
- for inferred in bases._infer_stmts([cls[name]], context, frame=self):
- if not isinstance(inferred, scoped_nodes.FunctionDef):
- yield inferred
- continue
-
- # We can obtain different descriptors from a super depending
- # on what we are accessing and where the super call is.
- if inferred.type == 'classmethod':
- yield bases.BoundMethod(inferred, cls)
- elif self._scope.type == 'classmethod' and inferred.type == 'method':
- yield inferred
- elif self._class_based or inferred.type == 'staticmethod':
- yield inferred
- elif bases._is_property(inferred):
- # TODO: support other descriptors as well.
- for value in inferred.infer_call_result(self, context):
- yield value
- else:
- yield bases.BoundMethod(inferred, cls)
-
- if not found:
- raise exceptions.AttributeInferenceError(target=self,
- attribute=name,
- context=context)
-
- def getattr(self, name, context=None):
- return list(self.igetattr(name, context=context))
-
-
- class ExceptionInstance(bases.Instance):
- """Class for instances of exceptions
-
- It has special treatment for some of the exceptions's attributes,
- which are transformed at runtime into certain concrete objects, such as
- the case of .args.
- """
-
- # pylint: disable=unnecessary-lambda
- special_attributes = util.lazy_descriptor(lambda: objectmodel.ExceptionInstanceModel())
-
-
- class DictInstance(bases.Instance):
- """Special kind of instances for dictionaries
-
- This instance knows the underlying object model of the dictionaries, which means
- that methods such as .values or .items can be properly inferred.
- """
-
- # pylint: disable=unnecessary-lambda
- special_attributes = util.lazy_descriptor(lambda: objectmodel.DictModel())
-
-
- # Custom objects tailored for dictionaries, which are used to
- # disambiguate between the types of Python 2 dict's method returns
- # and Python 3 (where they return set like objects).
- class DictItems(bases.Proxy):
- __str__ = node_classes.NodeNG.__str__
- __repr__ = node_classes.NodeNG.__repr__
-
-
- class DictKeys(bases.Proxy):
- __str__ = node_classes.NodeNG.__str__
- __repr__ = node_classes.NodeNG.__repr__
-
-
- class DictValues(bases.Proxy):
- __str__ = node_classes.NodeNG.__str__
- __repr__ = node_classes.NodeNG.__repr__
-
- # TODO: Hack to solve the circular import problem between node_classes and objects
- # This is not needed in 2.0, which has a cleaner design overall
- node_classes.Dict.__bases__ = (node_classes.NodeNG, DictInstance)
|