1
0
forked from VimPlug/jedi
Files
jedi-fork/jedi/evaluate/context/typing.py
2018-08-24 01:13:54 +02:00

260 lines
8.4 KiB
Python

"""
We need to somehow work with the typing objects. Since the typing objects are
pretty bare we need to add all the Jedi customizations to make them work as
contexts.
"""
from parso.python import tree
from jedi import debug
from jedi.evaluate.compiled import builtin_from_name, CompiledObject
from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS, Context
from jedi.evaluate.context.iterable import SequenceLiteralContext
from jedi.evaluate.filters import FilterWrapper, NameWrapper
_PROXY_TYPES = 'Optional Union Callable Type ClassVar Tuple Generic Protocol'.split()
_TYPE_ALIAS_TYPES = 'List Dict DefaultDict Set FrozenSet Counter Deque ChainMap'.split()
class _TypingBase(object):
def __init__(self, name, typing_context):
self._name = name
self._context = typing_context
def __getattr__(self, name):
return getattr(self._context, name)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, self._context)
class TypingModuleName(NameWrapper):
def infer(self):
return ContextSet.from_iterable(
self._remap(context) for context in self._wrapped_name.infer()
)
def _remap(self, context):
name = self.string_name
print('name', name)
if name in (_PROXY_TYPES + _TYPE_ALIAS_TYPES):
print('NAME', name)
return TypingProxy(name, context)
elif name == 'TypeVar':
return TypeVarClass(context.evaluator)
elif name == 'Any':
return Any(context)
elif name == 'TYPE_CHECKING':
# This is needed for e.g. imports that are only available for type
# checking or are in cycles. The user can then check this variable.
return builtin_from_name(context.evaluator, u'True')
elif name == 'overload':
# TODO implement overload
return context
elif name == 'cast':
# TODO implement cast
return context
elif name == 'TypedDict':
# TODO implement
# e.g. Movie = TypedDict('Movie', {'name': str, 'year': int})
return context
elif name in ('no_type_check', 'no_type_check_decorator'):
# This is not necessary, as long as we are not doing type checking.
return context
return context
class TypingModuleFilterWrapper(FilterWrapper):
name_wrapper_class = TypingModuleName
class TypingProxy(_TypingBase):
py__simple_getitem__ = None
def py__getitem__(self, index_context_set, contextualized_node):
return ContextSet.from_iterable(
TypingProxyWithIndex(self._name, self._context, index_context)
for index_context in index_context_set
)
class _WithIndexBase(_TypingBase):
def __init__(self, name, class_context, index_context):
super(_WithIndexBase, self).__init__(name, class_context)
self._index_context = index_context
def __repr__(self):
return '%s(%s, %s)' % (
self.__class__.__name__,
self._context,
self._index_context
)
def _execute_annotations_for_all_indexes(self):
return ContextSet.from_sets(
_iter_over_arguments(self._index_context)
).execute_annotation()
class TypingProxyWithIndex(_WithIndexBase):
def execute_annotation(self):
name = self._name
if name in _TYPE_ALIAS_TYPES:
debug.warning('type aliases are not yet implemented')
return NO_CONTEXTS
if name == 'Union':
# This is kind of a special case, because we have Unions (in Jedi
# ContextSets).
return self._execute_annotations_for_all_indexes()
elif name == 'Optional':
# Optional is basically just saying it's either None or the actual
# type.
return ContextSet(self._context) \
| ContextSet(builtin_from_name(self.evaluator, u'None'))
elif name == 'Type':
# The type is actually already given in the index_context
return ContextSet(self._index_context)
elif name == 'ClassVar':
# For now don't do anything here, ClassVars are always used.
return self._context.execute_annotation()
cls = globals()[name]
return ContextSet(cls(name, self._context, self._index_context))
def _iter_over_arguments(maybe_tuple_context):
if isinstance(maybe_tuple_context, SequenceLiteralContext):
for lazy_context in maybe_tuple_context.py__iter__():
yield lazy_context.infer()
else:
yield ContextSet(maybe_tuple_context)
class _ContainerBase(_WithIndexBase):
def get_filters(self):
pass
def _get_getitem_contexts(self, index):
for i, contexts in enumerate(_iter_over_arguments(self._index_context)):
if i == index:
return contexts
debug.warning('No param #%s found for annotation %s', index, self._index_context)
return NO_CONTEXTS
class Callable(_ContainerBase):
def py__call__(self, arguments):
# The 0th index are the arguments.
return self._get_getitem_contexts(1)
class Tuple(_ContainerBase):
def _is_homogenous(self):
# To specify a variable-length tuple of homogeneous type, Tuple[T, ...]
# is used.
if isinstance(self._index_context, SequenceLiteralContext):
pass
return False
def py__simple_getitem__(self, index):
if self._is_homogenous():
return self._get_getitem_contexts(0)
else:
if isinstance(index, int):
return self._get_getitem_contexts(index).execute_annotation()
debug.dbg('The getitem type on Tuple was %s' % index)
return NO_CONTEXTS
def py__getitem__(self):
if self._is_homogenous():
return self._get_getitem_contexts(0).execute_annotation()
return self._execute_annotations_for_all_indexes()
class Generic(_ContainerBase):
# TODO implement typevars
pass
# For pure type inference these two classes are basically the same. It's much
# more interesting once you do type checking.
Protocol = Generic
class Any(_TypingBase):
def __init__(self):
# Any is basically object, when it comes to type inference/completions.
# This is obviously not correct, but let's just use this for now.
context = ContextSet(builtin_from_name(self.evaluator, u'object'))
super(Any, self).__init__(context)
def execute_annotation(self):
debug.warning('Used Any, which is not implemented, yet.')
return NO_CONTEXTS
class GenericClass(object):
def __init__(self, class_context, ):
self._class_context = class_context
def __getattr__(self, name):
return getattr(self._class_context, name)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, self._class_context)
class TypeVarClass(Context):
def py__call__(self, arguments):
unpacked = arguments.unpack()
key, lazy_context = next(unpacked, (None, None))
name = self._find_name(lazy_context)
# The name must be given, otherwise it's useless.
if name is None or key is not None:
debug.warning('Found a variable without a name %s', arguments)
return NO_CONTEXTS
return ContextSet(TypeVar(self.evaluator, name, unpacked))
def _find_name(self, lazy_context):
if lazy_context is None:
return None
context_set = lazy_context.infer()
if not context_set:
return None
if len(context_set) > 1:
debug.warning('Found multiple contexts for a type variable: %s', context_set)
name_context = next(iter(context_set))
if isinstance(name_context, CompiledObject):
return name_context.get_safe_value(default=None)
return None
class TypeVar(Context):
def __init__(self, evaluator, name, unpacked_args):
super(TypeVar, self).__init__(evaluator)
self._name = name
self._unpacked_args = unpacked_args
def _unpack(self):
# TODO
constraints = ContextSet()
bound = None
covariant = False
contravariant = False
for key, lazy_context in unpacked:
if key is None:
constraints |= lazy_context.infer()
else:
if name == 'bound':
bound = lazy_context.infer()
def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, self._name)