forked from VimPlug/jedi
Move all the gradual typing stuff into one folder
This commit is contained in:
@@ -0,0 +1,557 @@
|
||||
import os
|
||||
import re
|
||||
|
||||
from jedi._compatibility import FileNotFoundError
|
||||
from jedi.evaluate.cache import evaluator_function_cache
|
||||
from jedi.cache import memoize_method
|
||||
from jedi.parser_utils import get_call_signature_for_any, get_cached_code_lines
|
||||
from jedi.evaluate.base_context import ContextSet, iterator_to_context_set, \
|
||||
ContextWrapper, NO_CONTEXTS
|
||||
from jedi.evaluate.filters import ParserTreeFilter, \
|
||||
NameWrapper, AbstractFilter, TreeNameDefinition
|
||||
from jedi.evaluate.context import ModuleContext, FunctionContext, \
|
||||
MethodContext, ClassContext
|
||||
from jedi.evaluate.context.function import FunctionMixin
|
||||
from jedi.evaluate.context.klass import ClassMixin
|
||||
from jedi.evaluate.context.module import ModuleMixin
|
||||
from jedi.evaluate.gradual.typing import TypingModuleFilterWrapper, \
|
||||
TypingModuleName
|
||||
from jedi.evaluate.compiled.context import CompiledName
|
||||
from jedi.evaluate.utils import to_list, safe_property
|
||||
|
||||
_jedi_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
_TYPESHED_PATH = os.path.join(_jedi_path, 'third_party', 'typeshed')
|
||||
|
||||
|
||||
def _merge_create_stub_map(directories):
|
||||
map_ = {}
|
||||
for directory in directories:
|
||||
map_.update(_create_stub_map(directory))
|
||||
return map_
|
||||
|
||||
|
||||
def _create_stub_map(directory):
|
||||
"""
|
||||
Create a mapping of an importable name in Python to a stub file.
|
||||
"""
|
||||
def generate():
|
||||
try:
|
||||
listed = os.listdir(directory)
|
||||
except (FileNotFoundError, OSError):
|
||||
# OSError is Python 2
|
||||
return
|
||||
|
||||
for entry in listed:
|
||||
path = os.path.join(directory, entry)
|
||||
if os.path.isdir(path):
|
||||
init = os.path.join(path, '__init__.pyi')
|
||||
if os.path.isfile(init):
|
||||
yield entry, init
|
||||
elif entry.endswith('.pyi') and os.path.isfile(path):
|
||||
name = entry.rstrip('.pyi')
|
||||
if name != '__init__':
|
||||
yield name, path
|
||||
|
||||
# Create a dictionary from the tuple generator.
|
||||
return dict(generate())
|
||||
|
||||
|
||||
def _get_typeshed_directories(version_info):
|
||||
check_version_list = ['2and3', str(version_info.major)]
|
||||
for base in ['stdlib', 'third_party']:
|
||||
base = os.path.join(_TYPESHED_PATH, base)
|
||||
base_list = os.listdir(base)
|
||||
for base_list_entry in base_list:
|
||||
match = re.match(r'(\d+)\.(\d+)$', base_list_entry)
|
||||
if match is not None:
|
||||
if int(match.group(1)) == version_info.major \
|
||||
and int(match.group(2)) <= version_info.minor:
|
||||
check_version_list.append(base_list_entry)
|
||||
|
||||
for check_version in check_version_list:
|
||||
yield os.path.join(base, check_version)
|
||||
|
||||
|
||||
@evaluator_function_cache()
|
||||
def _load_stub(evaluator, path):
|
||||
return evaluator.parse(path=path, cache=True, use_latest_grammar=True)
|
||||
|
||||
|
||||
def _merge_modules(context_set, stub_context):
|
||||
if not context_set:
|
||||
# If there are no results for normal modules, just
|
||||
# use a normal context for stub modules and don't
|
||||
# merge the actual module contexts with stubs.
|
||||
yield stub_context
|
||||
return
|
||||
|
||||
for context in context_set:
|
||||
if isinstance(context, ModuleContext):
|
||||
yield StubModuleContext.create_cached(context.evaluator, context, stub_context)
|
||||
else:
|
||||
# TODO do we want this? This includes compiled?!
|
||||
yield stub_context
|
||||
|
||||
|
||||
_version_cache = {}
|
||||
|
||||
|
||||
def _cache_stub_file_map(version_info):
|
||||
"""
|
||||
Returns a map of an importable name in Python to a stub file.
|
||||
"""
|
||||
# TODO this caches the stub files indefinitely, maybe use a time cache
|
||||
# for that?
|
||||
version = version_info[:2]
|
||||
try:
|
||||
return _version_cache[version]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
_version_cache[version] = file_set = \
|
||||
_merge_create_stub_map(_get_typeshed_directories(version_info))
|
||||
return file_set
|
||||
|
||||
|
||||
def import_module_decorator(func):
|
||||
def wrapper(evaluator, import_names, parent_module_context, sys_path):
|
||||
if import_names == ('_sqlite3',):
|
||||
# TODO Maybe find a better solution for this?
|
||||
# The problem is IMO how star imports are priorized and that
|
||||
# there's no clear ordering.
|
||||
return NO_CONTEXTS
|
||||
|
||||
if import_names == ('os', 'path'):
|
||||
# This is a huge exception, we follow a nested import
|
||||
# ``os.path``, because it's a very important one in Python
|
||||
# that is being achieved by messing with ``sys.modules`` in
|
||||
# ``os``.
|
||||
if parent_module_context is None:
|
||||
parent_module_context, = evaluator.import_module(('os',))
|
||||
return parent_module_context.py__getattribute__('path')
|
||||
|
||||
from jedi.evaluate.imports import JediImportError
|
||||
try:
|
||||
context_set = func(
|
||||
evaluator,
|
||||
import_names,
|
||||
parent_module_context,
|
||||
sys_path
|
||||
)
|
||||
except JediImportError:
|
||||
if import_names == ('typing',):
|
||||
# TODO this is also quite ugly, please refactor.
|
||||
context_set = NO_CONTEXTS
|
||||
else:
|
||||
raise
|
||||
|
||||
import_name = import_names[-1]
|
||||
map_ = None
|
||||
if len(import_names) == 1:
|
||||
map_ = _cache_stub_file_map(evaluator.grammar.version_info)
|
||||
elif isinstance(parent_module_context, StubModuleContext):
|
||||
if not parent_module_context.stub_context.is_package():
|
||||
# Only if it's a package (= a folder) something can be
|
||||
# imported.
|
||||
return context_set
|
||||
path = parent_module_context.stub_context.py__path__()
|
||||
map_ = _merge_create_stub_map(path)
|
||||
|
||||
if map_ is not None:
|
||||
path = map_.get(import_name)
|
||||
if path is not None:
|
||||
try:
|
||||
stub_module_node = _load_stub(evaluator, path)
|
||||
except FileNotFoundError:
|
||||
# The file has since been removed after looking for it.
|
||||
# TODO maybe empty cache?
|
||||
pass
|
||||
else:
|
||||
if import_names == ('typing',):
|
||||
module_cls = TypingModuleWrapper
|
||||
else:
|
||||
module_cls = StubOnlyModuleContext
|
||||
stub_module_context = module_cls(
|
||||
context_set, evaluator, stub_module_node,
|
||||
path=path,
|
||||
string_names=import_names,
|
||||
# The code was loaded with latest_grammar, so use
|
||||
# that.
|
||||
code_lines=get_cached_code_lines(evaluator.latest_grammar, path),
|
||||
)
|
||||
modules = _merge_modules(context_set, stub_module_context)
|
||||
return ContextSet(modules)
|
||||
# If no stub is found, just return the default.
|
||||
return context_set
|
||||
return wrapper
|
||||
|
||||
|
||||
class StubName(NameWrapper):
|
||||
"""
|
||||
This name is only here to mix stub names with non-stub names. The idea is
|
||||
that the user can goto the actual name, but end up on the definition of the
|
||||
stub when inferring types.
|
||||
"""
|
||||
def __init__(self, parent_context, non_stub_name, stub_name):
|
||||
super(StubName, self).__init__(non_stub_name)
|
||||
self.parent_context = parent_context
|
||||
self._stub_name = stub_name
|
||||
|
||||
@memoize_method
|
||||
@iterator_to_context_set
|
||||
def infer(self):
|
||||
stub_contexts = self._stub_name.infer()
|
||||
if not stub_contexts:
|
||||
for c in self._wrapped_name.infer():
|
||||
yield c
|
||||
return
|
||||
|
||||
typ = self._wrapped_name.tree_name.parent.type
|
||||
if typ in ('classdef', 'funcdef'):
|
||||
actual_context, = self._wrapped_name.infer()
|
||||
for stub_context in stub_contexts:
|
||||
if isinstance(stub_context, MethodContext):
|
||||
assert isinstance(actual_context, MethodContext)
|
||||
cls = StubMethodContext
|
||||
elif isinstance(stub_context, FunctionContext):
|
||||
cls = StubFunctionContext
|
||||
elif isinstance(stub_context, StubOnlyClass):
|
||||
cls = StubClassContext
|
||||
else:
|
||||
yield stub_context
|
||||
continue
|
||||
yield cls.create_cached(
|
||||
actual_context.evaluator,
|
||||
self.parent_context,
|
||||
actual_context,
|
||||
stub_context,
|
||||
)
|
||||
else:
|
||||
for c in stub_contexts:
|
||||
yield c
|
||||
|
||||
|
||||
class CompiledStubName(NameWrapper):
|
||||
def __init__(self, parent_context, compiled_name, stub_name):
|
||||
super(CompiledStubName, self).__init__(stub_name)
|
||||
self.parent_context = parent_context
|
||||
self._compiled_name = compiled_name
|
||||
|
||||
@memoize_method
|
||||
@iterator_to_context_set
|
||||
def infer(self):
|
||||
compiled_contexts = self._compiled_name.infer()
|
||||
stub_contexts = self._wrapped_name.infer()
|
||||
|
||||
if not compiled_contexts:
|
||||
for c in stub_contexts:
|
||||
yield c
|
||||
|
||||
for actual_context in compiled_contexts:
|
||||
for stub_context in stub_contexts:
|
||||
if isinstance(stub_context, _CompiledStubContext):
|
||||
# It's already a stub context, e.g. bytes in Python 2
|
||||
# behaves this way.
|
||||
yield stub_context
|
||||
elif stub_context.is_class():
|
||||
assert not isinstance(stub_context, CompiledStubClass), \
|
||||
"%s and %s" % (self._wrapped_name, self._compiled_name)
|
||||
yield CompiledStubClass.create_cached(
|
||||
stub_context.evaluator, stub_context, actual_context)
|
||||
elif stub_context.is_function():
|
||||
yield CompiledStubFunction.create_cached(
|
||||
stub_context.evaluator, stub_context, actual_context)
|
||||
else:
|
||||
yield stub_context
|
||||
if not stub_contexts:
|
||||
yield actual_context
|
||||
|
||||
|
||||
class VersionInfo(ContextWrapper):
|
||||
pass
|
||||
|
||||
|
||||
class StubOnlyName(TreeNameDefinition):
|
||||
def infer(self):
|
||||
inferred = super(StubOnlyName, self).infer()
|
||||
if self.string_name == 'version_info' and self.get_root_context().py__name__() == 'sys':
|
||||
return [VersionInfo(c) for c in inferred]
|
||||
|
||||
return [
|
||||
StubOnlyClass.create_cached(c.evaluator, c) if isinstance(c, ClassContext) else c
|
||||
for c in inferred
|
||||
]
|
||||
|
||||
|
||||
class StubOnlyFilter(ParserTreeFilter):
|
||||
name_class = StubOnlyName
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._search_global = kwargs.pop('search_global') # Python 2 :/
|
||||
super(StubOnlyFilter, self).__init__(*args, **kwargs)
|
||||
|
||||
def _is_name_reachable(self, name):
|
||||
if not super(StubOnlyFilter, self)._is_name_reachable(name):
|
||||
return False
|
||||
|
||||
if not self._search_global:
|
||||
# Imports in stub files are only public if they have an "as"
|
||||
# export.
|
||||
definition = name.get_definition()
|
||||
if definition.type in ('import_from', 'import_name'):
|
||||
if name.parent.type not in ('import_as_name', 'dotted_as_name'):
|
||||
return False
|
||||
n = name.value
|
||||
if n.startswith('_') and not (n.startswith('__') and n.endswith('__')):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class StubFilter(AbstractFilter):
|
||||
"""
|
||||
Merging names from stubs and non-stubs.
|
||||
"""
|
||||
def __init__(self, parent_context, non_stub_filters, stub_filters, add_non_stubs):
|
||||
self._parent_context = parent_context
|
||||
self._non_stub_filters = non_stub_filters
|
||||
self._stub_filters = stub_filters
|
||||
self._add_non_stubs = add_non_stubs
|
||||
|
||||
def get(self, name):
|
||||
non_stub_names = self._get_names_from_filters(self._non_stub_filters, name)
|
||||
stub_names = self._get_names_from_filters(self._stub_filters, name)
|
||||
return self._merge_names(non_stub_names, stub_names)
|
||||
|
||||
def values(self):
|
||||
name_dict = {}
|
||||
for non_stub_filter in self._non_stub_filters:
|
||||
for name in non_stub_filter.values():
|
||||
name_dict.setdefault(name.string_name, []).append(name)
|
||||
|
||||
# Try to match the names of stubs with non-stubs. If there's no
|
||||
# match, just use the stub name. The user will be directed there
|
||||
# for all API accesses. Otherwise the user will be directed to the
|
||||
# non-stub positions (see StubName).
|
||||
for stub_filter in self._stub_filters:
|
||||
for stub_name in stub_filter.values():
|
||||
merged_names = self._merge_names(
|
||||
names=name_dict.get(stub_name.string_name),
|
||||
stub_names=[stub_name]
|
||||
)
|
||||
for merged_name in merged_names:
|
||||
yield merged_name
|
||||
|
||||
def _get_names_from_filters(self, filters, string_name):
|
||||
return [
|
||||
name
|
||||
for filter in filters
|
||||
for name in filter.get(string_name)
|
||||
]
|
||||
|
||||
@to_list
|
||||
def _merge_names(self, names, stub_names):
|
||||
if not stub_names:
|
||||
if self._add_non_stubs:
|
||||
return names
|
||||
return []
|
||||
if not names:
|
||||
if isinstance(self._stub_filters[0].context, TypingModuleWrapper):
|
||||
return [TypingModuleName(n) for n in stub_names]
|
||||
return stub_names
|
||||
|
||||
result = []
|
||||
# The names are contained in both filters.
|
||||
for name in names:
|
||||
for stub_name in stub_names:
|
||||
if isinstance(self._stub_filters[0].context, TypingModuleWrapper):
|
||||
stub_name = TypingModuleName(stub_name)
|
||||
|
||||
if isinstance(name, CompiledName):
|
||||
result.append(CompiledStubName(self._parent_context, name, stub_name))
|
||||
else:
|
||||
result.append(StubName(self._parent_context, name, stub_name))
|
||||
return result
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%s, %s)' % (
|
||||
self.__class__.__name__,
|
||||
self._non_stub_filters,
|
||||
self._stub_filters,
|
||||
)
|
||||
|
||||
|
||||
class _StubContextFilterMixin(object):
|
||||
def get_filters(self, search_global=False, until_position=None,
|
||||
origin_scope=None, **kwargs):
|
||||
filters = self._wrapped_context.get_filters(
|
||||
search_global, until_position, origin_scope, **kwargs
|
||||
)
|
||||
yield self.stub_context.get_stub_only_filter(
|
||||
parent_context=self,
|
||||
# Take the first filter, which is here to filter module contents
|
||||
# and wrap it.
|
||||
non_stub_filters=[next(filters)],
|
||||
search_global=search_global,
|
||||
until_position=until_position,
|
||||
origin_scope=origin_scope,
|
||||
)
|
||||
for f in filters:
|
||||
yield f
|
||||
|
||||
|
||||
class StubModuleContext(_StubContextFilterMixin, ModuleMixin, ContextWrapper):
|
||||
def __init__(self, context, stub_context):
|
||||
super(StubModuleContext, self).__init__(context)
|
||||
self.stub_context = stub_context
|
||||
|
||||
|
||||
class StubClassContext(_StubContextFilterMixin, ClassMixin, ContextWrapper):
|
||||
def __init__(self, parent_context, actual_context, stub_context):
|
||||
super(StubClassContext, self).__init__(actual_context)
|
||||
self.parent_context = parent_context
|
||||
self.stub_context = stub_context
|
||||
|
||||
def __getattribute__(self, name):
|
||||
if name in ('py__getitem__', 'py__simple_getitem__', 'py__bases__',
|
||||
'execute_annotation', 'list_type_vars', 'get_signatures'):
|
||||
# getitem is always done in the stub class.
|
||||
return getattr(self.stub_context, name)
|
||||
return super(StubClassContext, self).__getattribute__(name)
|
||||
|
||||
def define_generics(self, type_var_dict):
|
||||
if not type_var_dict:
|
||||
return self
|
||||
return self.stub_context.define_generics(type_var_dict)
|
||||
|
||||
|
||||
class StubFunctionContext(FunctionMixin, ContextWrapper):
|
||||
def __init__(self, parent_context, actual_context, stub_context):
|
||||
super(StubFunctionContext, self).__init__(actual_context)
|
||||
self.parent_context = parent_context
|
||||
self.stub_context = stub_context
|
||||
|
||||
def get_function_execution(self, arguments=None):
|
||||
return self.stub_context.get_function_execution(arguments)
|
||||
|
||||
def get_signatures(self):
|
||||
return self.stub_context.get_signatures()
|
||||
|
||||
|
||||
class StubMethodContext(StubFunctionContext):
|
||||
"""
|
||||
Both of the stub context and the actual context are a stub method.
|
||||
"""
|
||||
@safe_property
|
||||
def class_context(self):
|
||||
return StubClassContext.create_cached(
|
||||
self.evaluator,
|
||||
self.parent_context,
|
||||
actual_context=self._wrapped_context.class_context,
|
||||
stub_context=self.stub_context.class_context
|
||||
)
|
||||
|
||||
|
||||
class _StubOnlyContextMixin(object):
|
||||
_add_non_stubs_in_filter = False
|
||||
|
||||
def _get_stub_only_filters(self, **filter_kwargs):
|
||||
return [StubOnlyFilter(
|
||||
self.evaluator,
|
||||
context=self,
|
||||
**filter_kwargs
|
||||
)]
|
||||
|
||||
def get_stub_only_filter(self, parent_context, non_stub_filters, **filter_kwargs):
|
||||
# Here we remap the names from stubs to the actual module. This is
|
||||
# important if type inferences is needed in that module.
|
||||
return StubFilter(
|
||||
parent_context,
|
||||
non_stub_filters,
|
||||
self._get_stub_only_filters(**filter_kwargs),
|
||||
add_non_stubs=self._add_non_stubs_in_filter,
|
||||
)
|
||||
|
||||
def _get_base_filters(self, filters, search_global=False,
|
||||
until_position=None, origin_scope=None):
|
||||
next(filters) # Ignore the first filter and replace it with our own
|
||||
yield self.get_stub_only_filter(
|
||||
parent_context=self,
|
||||
non_stub_filters=list(self._get_first_non_stub_filters()),
|
||||
search_global=search_global,
|
||||
until_position=until_position,
|
||||
origin_scope=origin_scope,
|
||||
)
|
||||
|
||||
for f in filters:
|
||||
yield f
|
||||
|
||||
|
||||
class StubOnlyModuleContext(_StubOnlyContextMixin, ModuleContext):
|
||||
_add_non_stubs_in_filter = True
|
||||
|
||||
def __init__(self, non_stub_context_set, *args, **kwargs):
|
||||
super(StubOnlyModuleContext, self).__init__(*args, **kwargs)
|
||||
self.non_stub_context_set = non_stub_context_set
|
||||
|
||||
def _get_first_non_stub_filters(self):
|
||||
for context in self.non_stub_context_set:
|
||||
yield next(context.get_filters(search_global=False))
|
||||
|
||||
def _get_stub_only_filters(self, search_global, **filter_kwargs):
|
||||
stub_filters = super(StubOnlyModuleContext, self)._get_stub_only_filters(
|
||||
search_global=search_global, **filter_kwargs
|
||||
)
|
||||
stub_filters += self.iter_star_filters(search_global=search_global)
|
||||
return stub_filters
|
||||
|
||||
def get_filters(self, search_global=False, until_position=None,
|
||||
origin_scope=None, **kwargs):
|
||||
filters = super(StubOnlyModuleContext, self).get_filters(
|
||||
search_global, until_position, origin_scope, **kwargs
|
||||
)
|
||||
for f in self._get_base_filters(filters, search_global, until_position, origin_scope):
|
||||
yield f
|
||||
|
||||
|
||||
class StubOnlyClass(_StubOnlyContextMixin, ClassMixin, ContextWrapper):
|
||||
pass
|
||||
|
||||
|
||||
class _CompiledStubContext(ContextWrapper):
|
||||
def __init__(self, stub_context, compiled_context):
|
||||
super(_CompiledStubContext, self).__init__(stub_context)
|
||||
self._compiled_context = compiled_context
|
||||
|
||||
def py__doc__(self, include_call_signature=False):
|
||||
doc = self._compiled_context.py__doc__()
|
||||
if include_call_signature:
|
||||
call_sig = get_call_signature_for_any(self._wrapped_context.tree_node)
|
||||
if call_sig is not None:
|
||||
doc = call_sig + '\n\n' + doc
|
||||
return doc
|
||||
|
||||
|
||||
class CompiledStubFunction(_CompiledStubContext):
|
||||
pass
|
||||
|
||||
|
||||
class CompiledStubClass(_StubOnlyContextMixin, _CompiledStubContext, ClassMixin):
|
||||
def _get_first_non_stub_filters(self):
|
||||
yield next(self._compiled_context.get_filters(search_global=False))
|
||||
|
||||
def get_filters(self, search_global=False, until_position=None,
|
||||
origin_scope=None, **kwargs):
|
||||
filters = self._wrapped_context.get_filters(
|
||||
search_global, until_position, origin_scope, **kwargs
|
||||
)
|
||||
for f in self._get_base_filters(filters, search_global, until_position, origin_scope):
|
||||
yield f
|
||||
|
||||
|
||||
class TypingModuleWrapper(StubOnlyModuleContext):
|
||||
# TODO should use this instead of the isinstance check
|
||||
def get_filterss(self, *args, **kwargs):
|
||||
filters = super(TypingModuleWrapper, self).get_filters(*args, **kwargs)
|
||||
yield TypingModuleFilterWrapper(next(filters))
|
||||
for f in filters:
|
||||
yield f
|
||||
@@ -0,0 +1,689 @@
|
||||
"""
|
||||
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.
|
||||
|
||||
This file deals with all the typing.py cases.
|
||||
"""
|
||||
from jedi._compatibility import unicode, force_unicode
|
||||
from jedi import debug
|
||||
from jedi.evaluate.cache import evaluator_method_cache
|
||||
from jedi.evaluate.compiled import builtin_from_name
|
||||
from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS, Context, \
|
||||
iterator_to_context_set, HelperContextMixin, ContextWrapper
|
||||
from jedi.evaluate.lazy_context import LazyKnownContexts
|
||||
from jedi.evaluate.context.iterable import SequenceLiteralContext
|
||||
from jedi.evaluate.arguments import repack_with_argument_clinic
|
||||
from jedi.evaluate.utils import to_list
|
||||
from jedi.evaluate.filters import FilterWrapper, NameWrapper, \
|
||||
AbstractTreeName, AbstractNameDefinition, ContextName
|
||||
from jedi.evaluate.helpers import is_string
|
||||
from jedi.evaluate.context.klass import ClassMixin
|
||||
|
||||
_PROXY_CLASS_TYPES = 'Tuple Generic Protocol Callable Type'.split()
|
||||
_TYPE_ALIAS_TYPES = {
|
||||
'List': 'builtins.list',
|
||||
'Dict': 'builtins.dict',
|
||||
'Set': 'builtins.set',
|
||||
'FrozenSet': 'builtins.frozenset',
|
||||
'ChainMap': 'collections.ChainMap',
|
||||
'Counter': 'collections.Counter',
|
||||
'DefaultDict': 'collections.defaultdict',
|
||||
'Deque': 'collections.deque',
|
||||
}
|
||||
_PROXY_TYPES = 'Optional Union ClassVar'.split()
|
||||
|
||||
|
||||
class TypingName(AbstractTreeName):
|
||||
def __init__(self, context, other_name):
|
||||
super(TypingName, self).__init__(context.parent_context, other_name.tree_name)
|
||||
self._context = context
|
||||
|
||||
def infer(self):
|
||||
return ContextSet([self._context])
|
||||
|
||||
|
||||
class _BaseTypingContext(Context):
|
||||
def __init__(self, evaluator, parent_context, tree_name):
|
||||
super(_BaseTypingContext, self).__init__(evaluator, parent_context)
|
||||
self._tree_name = tree_name
|
||||
|
||||
@property
|
||||
def tree_node(self):
|
||||
return self._tree_name
|
||||
|
||||
def get_filters(self, *args, **kwargs):
|
||||
# TODO this is obviously wrong.
|
||||
class EmptyFilter():
|
||||
def get(self, name):
|
||||
return []
|
||||
|
||||
def values(self):
|
||||
return []
|
||||
|
||||
yield EmptyFilter()
|
||||
|
||||
def py__class__(self):
|
||||
# TODO this is obviously not correct, but at least gives us a class if
|
||||
# we have none. Some of these objects don't really have a base class in
|
||||
# typeshed.
|
||||
return builtin_from_name(self.evaluator, u'object')
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return ContextName(self, self._tree_name)
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%s)' % (self.__class__.__name__, self._tree_name.value)
|
||||
|
||||
|
||||
class TypingModuleName(NameWrapper):
|
||||
def infer(self):
|
||||
return ContextSet(self._remap())
|
||||
|
||||
def _remap(self):
|
||||
name = self.string_name
|
||||
evaluator = self.parent_context.evaluator
|
||||
try:
|
||||
actual = _TYPE_ALIAS_TYPES[name]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
yield TypeAlias.create_cached(evaluator, self.parent_context, self.tree_name, actual)
|
||||
return
|
||||
|
||||
if name in _PROXY_CLASS_TYPES:
|
||||
yield TypingClassContext.create_cached(evaluator, self.parent_context, self.tree_name)
|
||||
elif name in _PROXY_TYPES:
|
||||
yield TypingContext.create_cached(evaluator, self.parent_context, self.tree_name)
|
||||
elif name == 'runtime':
|
||||
# We don't want anything here, not sure what this function is
|
||||
# supposed to do, since it just appears in the stubs and shouldn't
|
||||
# have any effects there (because it's never executed).
|
||||
return
|
||||
elif name == 'TypeVar':
|
||||
yield TypeVarClass.create_cached(evaluator, self.parent_context, self.tree_name)
|
||||
elif name == 'Any':
|
||||
yield Any.create_cached(evaluator, self.parent_context, self.tree_name)
|
||||
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.
|
||||
yield builtin_from_name(evaluator, u'True')
|
||||
elif name == 'overload':
|
||||
yield OverloadFunction.create_cached(evaluator, self.parent_context, self.tree_name)
|
||||
elif name == 'cast':
|
||||
# TODO implement cast
|
||||
for c in self._wrapped_name.infer(): # Fuck my life Python 2
|
||||
yield c
|
||||
elif name == 'TypedDict':
|
||||
# TODO doesn't even exist in typeshed/typing.py, yet. But will be
|
||||
# added soon.
|
||||
pass
|
||||
elif name in ('no_type_check', 'no_type_check_decorator'):
|
||||
# This is not necessary, as long as we are not doing type checking.
|
||||
for c in self._wrapped_name.infer(): # Fuck my life Python 2
|
||||
yield c
|
||||
else:
|
||||
# Everything else shouldn't be relevant for type checking.
|
||||
for c in self._wrapped_name.infer(): # Fuck my life Python 2
|
||||
yield c
|
||||
|
||||
|
||||
class TypingModuleFilterWrapper(FilterWrapper):
|
||||
name_wrapper_class = TypingModuleName
|
||||
|
||||
|
||||
class _WithIndexBase(_BaseTypingContext):
|
||||
def __init__(self, evaluator, parent_context, name, index_context, context_of_index):
|
||||
super(_WithIndexBase, self).__init__(evaluator, parent_context, name)
|
||||
self._index_context = index_context
|
||||
self._context_of_index = context_of_index
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s: %s[%s]>' % (
|
||||
self.__class__.__name__,
|
||||
self._tree_name.value,
|
||||
self._index_context,
|
||||
)
|
||||
|
||||
|
||||
class TypingContextWithIndex(_WithIndexBase):
|
||||
def execute_annotation(self):
|
||||
string_name = self._tree_name.value
|
||||
|
||||
if string_name == 'Union':
|
||||
# This is kind of a special case, because we have Unions (in Jedi
|
||||
# ContextSets).
|
||||
return self.gather_annotation_classes().execute_annotation()
|
||||
elif string_name == 'Optional':
|
||||
# Optional is basically just saying it's either None or the actual
|
||||
# type.
|
||||
return self.gather_annotation_classes().execute_annotation() \
|
||||
| ContextSet([builtin_from_name(self.evaluator, u'None')])
|
||||
elif string_name == 'Type':
|
||||
# The type is actually already given in the index_context
|
||||
return ContextSet([self._index_context])
|
||||
elif string_name == 'ClassVar':
|
||||
# For now don't do anything here, ClassVars are always used.
|
||||
return self._index_context.execute_annotation()
|
||||
|
||||
cls = globals()[string_name]
|
||||
return ContextSet([cls(
|
||||
self.evaluator,
|
||||
self.parent_context,
|
||||
self._tree_name,
|
||||
self._index_context,
|
||||
self._context_of_index
|
||||
)])
|
||||
|
||||
def gather_annotation_classes(self):
|
||||
return ContextSet.from_sets(
|
||||
_iter_over_arguments(self._index_context, self._context_of_index)
|
||||
)
|
||||
|
||||
|
||||
class TypingContext(_BaseTypingContext):
|
||||
index_class = TypingContextWithIndex
|
||||
py__simple_getitem__ = None
|
||||
|
||||
def py__getitem__(self, index_context_set, contextualized_node):
|
||||
return ContextSet(
|
||||
self.index_class.create_cached(
|
||||
self.evaluator,
|
||||
self.parent_context,
|
||||
self._tree_name,
|
||||
index_context,
|
||||
context_of_index=contextualized_node.context)
|
||||
for index_context in index_context_set
|
||||
)
|
||||
|
||||
|
||||
class _TypingClassMixin(object):
|
||||
def py__bases__(self):
|
||||
return [LazyKnownContexts(
|
||||
self.evaluator.builtins_module.py__getattribute__('object')
|
||||
)]
|
||||
|
||||
|
||||
class TypingClassContextWithIndex(_TypingClassMixin, TypingContextWithIndex, ClassMixin):
|
||||
pass
|
||||
|
||||
|
||||
class TypingClassContext(_TypingClassMixin, TypingContext, ClassMixin):
|
||||
index_class = TypingClassContextWithIndex
|
||||
|
||||
|
||||
def _iter_over_arguments(maybe_tuple_context, defining_context):
|
||||
def iterate():
|
||||
if isinstance(maybe_tuple_context, SequenceLiteralContext):
|
||||
for lazy_context in maybe_tuple_context.py__iter__(contextualized_node=None):
|
||||
yield lazy_context.infer()
|
||||
else:
|
||||
yield ContextSet([maybe_tuple_context])
|
||||
|
||||
def resolve_forward_references(context_set):
|
||||
for context in context_set:
|
||||
if is_string(context):
|
||||
from jedi.evaluate.pep0484 import _get_forward_reference_node
|
||||
node = _get_forward_reference_node(defining_context, context.get_safe_value())
|
||||
if node is not None:
|
||||
for c in defining_context.eval_node(node):
|
||||
yield c
|
||||
else:
|
||||
yield context
|
||||
|
||||
for context_set in iterate():
|
||||
yield ContextSet(resolve_forward_references(context_set))
|
||||
|
||||
|
||||
class TypeAlias(HelperContextMixin):
|
||||
def __init__(self, evaluator, parent_context, origin_tree_name, actual):
|
||||
self.evaluator = evaluator
|
||||
self.parent_context = parent_context
|
||||
self._origin_tree_name = origin_tree_name
|
||||
self._actual = actual # e.g. builtins.list
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return ContextName(self, self._origin_tree_name)
|
||||
|
||||
def py__name__(self):
|
||||
return self.name.string_name
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._get_type_alias_class(), name)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s: %s>' % (self.__class__.__name__, self._actual)
|
||||
|
||||
@evaluator_method_cache()
|
||||
def _get_type_alias_class(self):
|
||||
module_name, class_name = self._actual.split('.')
|
||||
if self.evaluator.environment.version_info.major == 2 and module_name == 'builtins':
|
||||
module_name = '__builtin__'
|
||||
|
||||
# TODO use evaluator.import_module?
|
||||
from jedi.evaluate.imports import Importer
|
||||
module, = Importer(
|
||||
self.evaluator, [module_name], self.evaluator.builtins_module
|
||||
).follow()
|
||||
classes = module.py__getattribute__(class_name)
|
||||
# There should only be one, because it's code that we control.
|
||||
assert len(classes) == 1, classes
|
||||
cls = next(iter(classes))
|
||||
return cls
|
||||
|
||||
|
||||
class _ContainerBase(_WithIndexBase):
|
||||
def _get_getitem_contexts(self, index):
|
||||
args = _iter_over_arguments(self._index_context, self._context_of_index)
|
||||
for i, contexts in enumerate(args):
|
||||
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).execute_annotation()
|
||||
|
||||
|
||||
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):
|
||||
entries = self._index_context.get_tree_entries()
|
||||
if len(entries) == 2 and entries[1] == '...':
|
||||
return True
|
||||
return False
|
||||
|
||||
def py__simple_getitem__(self, index):
|
||||
if self._is_homogenous():
|
||||
return self._get_getitem_contexts(0).execute_annotation()
|
||||
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__iter__(self, contextualized_node=None):
|
||||
if self._is_homogenous():
|
||||
while True:
|
||||
yield LazyKnownContexts(self._get_getitem_contexts(0).execute_annotation())
|
||||
else:
|
||||
if isinstance(self._index_context, SequenceLiteralContext):
|
||||
for i in range(self._index_context.py__len__()):
|
||||
yield LazyKnownContexts(self._get_getitem_contexts(i).execute_annotation())
|
||||
|
||||
def py__getitem__(self, index_context_set, contextualized_node):
|
||||
if self._is_homogenous():
|
||||
return self._get_getitem_contexts(0).execute_annotation()
|
||||
|
||||
return ContextSet.from_sets(
|
||||
_iter_over_arguments(self._index_context, self._context_of_index)
|
||||
).execute_annotation()
|
||||
|
||||
|
||||
class Generic(_ContainerBase):
|
||||
pass
|
||||
|
||||
|
||||
class Protocol(_ContainerBase):
|
||||
pass
|
||||
|
||||
|
||||
class Any(_BaseTypingContext):
|
||||
def execute_annotation(self):
|
||||
debug.warning('Used Any - returned no results')
|
||||
return NO_CONTEXTS
|
||||
|
||||
|
||||
class TypeVarClass(_BaseTypingContext):
|
||||
def py__call__(self, arguments):
|
||||
unpacked = arguments.unpack()
|
||||
|
||||
key, lazy_context = next(unpacked, (None, None))
|
||||
var_name = self._find_string_name(lazy_context)
|
||||
# The name must be given, otherwise it's useless.
|
||||
if var_name is None or key is not None:
|
||||
debug.warning('Found a variable without a name %s', arguments)
|
||||
return NO_CONTEXTS
|
||||
|
||||
return ContextSet([TypeVar.create_cached(
|
||||
self.evaluator,
|
||||
self.parent_context,
|
||||
self._tree_name,
|
||||
var_name,
|
||||
unpacked
|
||||
)])
|
||||
|
||||
def _find_string_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))
|
||||
try:
|
||||
method = name_context.get_safe_value
|
||||
except AttributeError:
|
||||
return None
|
||||
else:
|
||||
safe_value = method(default=None)
|
||||
if self.evaluator.environment.version_info.major == 2:
|
||||
if isinstance(safe_value, bytes):
|
||||
return force_unicode(safe_value)
|
||||
if isinstance(safe_value, (str, unicode)):
|
||||
return safe_value
|
||||
return None
|
||||
|
||||
|
||||
class TypeVar(_BaseTypingContext):
|
||||
def __init__(self, evaluator, parent_context, tree_name, var_name, unpacked_args):
|
||||
super(TypeVar, self).__init__(evaluator, parent_context, tree_name)
|
||||
self._var_name = var_name
|
||||
|
||||
self._constraints_lazy_contexts = []
|
||||
self._bound_lazy_context = None
|
||||
self._covariant_lazy_context = None
|
||||
self._contravariant_lazy_context = None
|
||||
for key, lazy_context in unpacked_args:
|
||||
if key is None:
|
||||
self._constraints_lazy_contexts.append(lazy_context)
|
||||
else:
|
||||
if key == 'bound':
|
||||
self._bound_lazy_context = lazy_context
|
||||
elif key == 'covariant':
|
||||
self._covariant_lazy_context = lazy_context
|
||||
elif key == 'contravariant':
|
||||
self._contra_variant_lazy_context = lazy_context
|
||||
else:
|
||||
debug.warning('Invalid TypeVar param name %s', key)
|
||||
|
||||
def py__name__(self):
|
||||
return self._var_name
|
||||
|
||||
def get_filters(self, *args, **kwargs):
|
||||
return iter([])
|
||||
|
||||
def _get_classes(self):
|
||||
if self._bound_lazy_context is not None:
|
||||
return self._bound_lazy_context.infer()
|
||||
if self._constraints_lazy_contexts:
|
||||
return ContextSet.from_sets(
|
||||
l.infer() for l in self._constraints_lazy_contexts
|
||||
)
|
||||
debug.warning('Tried to infer the TypeVar %s without a given type', self._var_name)
|
||||
return NO_CONTEXTS
|
||||
|
||||
def is_same_class(self, other):
|
||||
# Everything can match an undefined type var.
|
||||
return True
|
||||
|
||||
@property
|
||||
def constraints(self):
|
||||
return ContextSet.from_sets(
|
||||
lazy.infer() for lazy in self._constraints_lazy_contexts
|
||||
)
|
||||
|
||||
def execute_annotation(self):
|
||||
return self._get_classes().execute_annotation()
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s: %s>' % (self.__class__.__name__, self.py__name__())
|
||||
|
||||
|
||||
class OverloadFunction(_BaseTypingContext):
|
||||
@repack_with_argument_clinic('func, /')
|
||||
def py__call__(self, func_context_set):
|
||||
# Just pass arguments through.
|
||||
return func_context_set
|
||||
|
||||
|
||||
class BoundTypeVarName(AbstractNameDefinition):
|
||||
"""
|
||||
This type var was bound to a certain type, e.g. int.
|
||||
"""
|
||||
def __init__(self, type_var, context_set):
|
||||
self._type_var = type_var
|
||||
self.parent_context = type_var.parent_context
|
||||
self._context_set = context_set
|
||||
|
||||
def infer(self):
|
||||
def iter_():
|
||||
for context in self._context_set:
|
||||
# Replace any with the constraints if they are there.
|
||||
if isinstance(context, Any):
|
||||
for constraint in self._type_var.constraints:
|
||||
yield constraint
|
||||
else:
|
||||
yield context
|
||||
return ContextSet(iter_())
|
||||
|
||||
def py__name__(self):
|
||||
return self._type_var.py__name__()
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %s -> %s>' % (self.__class__.__name__, self.py__name__(), self._context_set)
|
||||
|
||||
|
||||
class TypeVarFilter(object):
|
||||
"""
|
||||
A filter for all given variables in a class.
|
||||
|
||||
A = TypeVar('A')
|
||||
B = TypeVar('B')
|
||||
class Foo(Mapping[A, B]):
|
||||
...
|
||||
|
||||
In this example we would have two type vars given: A and B
|
||||
"""
|
||||
def __init__(self, given_types, type_vars):
|
||||
self._given_types = given_types
|
||||
self._type_vars = type_vars
|
||||
|
||||
def get(self, name):
|
||||
for i, type_var in enumerate(self._type_vars):
|
||||
if type_var.py__name__() == name:
|
||||
try:
|
||||
return [BoundTypeVarName(type_var, self._given_types[i])]
|
||||
except IndexError:
|
||||
return [type_var.name]
|
||||
return []
|
||||
|
||||
def values(self):
|
||||
# The values are not relevant. If it's not searched exactly, the type
|
||||
# vars are just global and should be looked up as that.
|
||||
return []
|
||||
|
||||
|
||||
class AbstractAnnotatedClass(ClassMixin, ContextWrapper):
|
||||
def get_type_var_filter(self):
|
||||
return TypeVarFilter(self.get_given_types(), self.list_type_vars())
|
||||
|
||||
def _create_class_filter(self, cls, origin_scope, is_instance):
|
||||
filter_ = super(AbstractAnnotatedClass, self)._create_class_filter(
|
||||
cls, origin_scope, is_instance
|
||||
)
|
||||
try:
|
||||
stub_context = cls.stub_context
|
||||
except AttributeError:
|
||||
return filter_
|
||||
else:
|
||||
return stub_context.get_stub_only_filter(
|
||||
# Take the first filter, which is here to filter module contents
|
||||
# and wrap it.
|
||||
self.parent_context,
|
||||
[filter_],
|
||||
search_global=False,
|
||||
origin_scope=origin_scope,
|
||||
)
|
||||
|
||||
def get_filters(self, search_global=False, *args, **kwargs):
|
||||
filters = super(AbstractAnnotatedClass, self).get_filters(
|
||||
search_global,
|
||||
*args, **kwargs
|
||||
)
|
||||
for f in filters:
|
||||
yield f
|
||||
|
||||
if search_global:
|
||||
# The type vars can only be looked up if it's a global search and
|
||||
# not a direct lookup on the class.
|
||||
yield self.get_type_var_filter()
|
||||
|
||||
def is_same_class(self, other):
|
||||
if not isinstance(other, AbstractAnnotatedClass):
|
||||
return False
|
||||
|
||||
if self.tree_node != other.tree_node:
|
||||
# TODO not sure if this is nice.
|
||||
return False
|
||||
given_params1 = self.get_given_types()
|
||||
given_params2 = other.get_given_types()
|
||||
|
||||
if len(given_params1) != len(given_params2):
|
||||
# If the amount of type vars doesn't match, the class doesn't
|
||||
# match.
|
||||
return False
|
||||
|
||||
# Now compare generics
|
||||
return all(
|
||||
any(
|
||||
# TODO why is this ordering the correct one?
|
||||
cls2.is_same_class(cls1)
|
||||
for cls1 in class_set1
|
||||
for cls2 in class_set2
|
||||
) for class_set1, class_set2 in zip(given_params1, given_params2)
|
||||
)
|
||||
|
||||
def py__call__(self, arguments):
|
||||
instance, = super(AbstractAnnotatedClass, self).py__call__(arguments)
|
||||
return ContextSet([InstanceWrapper(instance)])
|
||||
|
||||
def get_given_types(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def define_generics(self, type_var_dict):
|
||||
changed = False
|
||||
new_generics = []
|
||||
for generic_set in self.get_given_types():
|
||||
contexts = NO_CONTEXTS
|
||||
for generic in generic_set:
|
||||
if isinstance(generic, AbstractAnnotatedClass):
|
||||
new_generic = generic.define_generics(type_var_dict)
|
||||
contexts |= ContextSet([new_generic])
|
||||
if new_generic != generic:
|
||||
changed = True
|
||||
else:
|
||||
if isinstance(generic, TypeVar):
|
||||
try:
|
||||
contexts |= type_var_dict[generic.py__name__()]
|
||||
changed = True
|
||||
except KeyError:
|
||||
contexts |= ContextSet([generic])
|
||||
else:
|
||||
contexts |= ContextSet([generic])
|
||||
new_generics.append(contexts)
|
||||
|
||||
if not changed:
|
||||
# There might not be any type vars that change. In that case just
|
||||
# return itself, because it does not make sense to potentially lose
|
||||
# cached results.
|
||||
return self
|
||||
|
||||
return AnnotatedSubClass(
|
||||
self._wrapped_context,
|
||||
given_types=tuple(new_generics)
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s: %s%s>' % (
|
||||
self.__class__.__name__,
|
||||
self._wrapped_context,
|
||||
list(self.get_given_types()),
|
||||
)
|
||||
|
||||
@to_list
|
||||
def py__bases__(self):
|
||||
for base in self._wrapped_context.py__bases__():
|
||||
yield LazyAnnotatedBaseClass(self, base)
|
||||
|
||||
|
||||
class AnnotatedClass(AbstractAnnotatedClass):
|
||||
def __init__(self, class_context, index_context, context_of_index):
|
||||
super(AnnotatedClass, self).__init__(class_context)
|
||||
self._index_context = index_context
|
||||
self._context_of_index = context_of_index
|
||||
|
||||
@evaluator_method_cache()
|
||||
def get_given_types(self):
|
||||
return list(_iter_over_arguments(self._index_context, self._context_of_index))
|
||||
|
||||
|
||||
class AnnotatedSubClass(AbstractAnnotatedClass):
|
||||
def __init__(self, class_context, given_types):
|
||||
super(AnnotatedSubClass, self).__init__(class_context)
|
||||
self._given_types = given_types
|
||||
|
||||
def get_given_types(self):
|
||||
return self._given_types
|
||||
|
||||
|
||||
class LazyAnnotatedBaseClass(object):
|
||||
def __init__(self, class_context, lazy_base_class):
|
||||
self._class_context = class_context
|
||||
self._lazy_base_class = lazy_base_class
|
||||
|
||||
@iterator_to_context_set
|
||||
def infer(self):
|
||||
for base in self._lazy_base_class.infer():
|
||||
if isinstance(base, AbstractAnnotatedClass):
|
||||
# Here we have to recalculate the given types.
|
||||
yield AnnotatedSubClass.create_cached(
|
||||
base.evaluator,
|
||||
base._wrapped_context,
|
||||
tuple(self._remap_type_vars(base)),
|
||||
)
|
||||
else:
|
||||
yield base
|
||||
|
||||
def _remap_type_vars(self, base):
|
||||
filter = self._class_context.get_type_var_filter()
|
||||
for type_var_set in base.get_given_types():
|
||||
new = NO_CONTEXTS
|
||||
for type_var in type_var_set:
|
||||
if isinstance(type_var, TypeVar):
|
||||
names = filter.get(type_var.py__name__())
|
||||
new |= ContextSet.from_sets(
|
||||
name.infer() for name in names
|
||||
)
|
||||
else:
|
||||
# Mostly will be type vars, except if in some cases
|
||||
# a concrete type will already be there. In that
|
||||
# case just add it to the context set.
|
||||
new |= ContextSet([type_var])
|
||||
yield new
|
||||
|
||||
|
||||
class InstanceWrapper(ContextWrapper):
|
||||
def py__stop_iteration_returns(self):
|
||||
for cls in self._wrapped_context.class_context.py__mro__():
|
||||
if cls.py__name__() == 'Generator':
|
||||
given_types = cls.get_given_types()
|
||||
try:
|
||||
return given_types[2].execute_annotation()
|
||||
except IndexError:
|
||||
pass
|
||||
elif cls.py__name__() == 'Iterator':
|
||||
return ContextSet([builtin_from_name(self.evaluator, u'None')])
|
||||
return self._wrapped_context.py__stop_iteration_returns()
|
||||
Reference in New Issue
Block a user