forked from VimPlug/jedi
Refactor Jedi so we use stub modules as much as possible
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import os
|
||||
|
||||
from jedi.cache import memoize_method
|
||||
from jedi.parser_utils import get_call_signature_for_any
|
||||
from jedi.evaluate.utils import safe_property
|
||||
@@ -10,7 +12,8 @@ from jedi.evaluate.filters import ParserTreeFilter, \
|
||||
NameWrapper, AbstractFilter, TreeNameDefinition
|
||||
from jedi.evaluate.compiled.context import CompiledName
|
||||
from jedi.evaluate.utils import to_list
|
||||
from jedi.evaluate.gradual.typing import TypingModuleFilterWrapper, TypingModuleName
|
||||
from jedi.evaluate.gradual.typing import TypingModuleFilterWrapper, \
|
||||
TypingModuleName, AnnotatedClass
|
||||
|
||||
|
||||
class _StubContextFilterMixin(object):
|
||||
@@ -113,13 +116,13 @@ class _StubOnlyContextMixin(object):
|
||||
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_contexts=self.get_stub_contexts(),
|
||||
non_stub_filters=list(self._get_first_non_stub_filters()),
|
||||
stub_only_filters = self._get_stub_only_filters(
|
||||
search_global=search_global,
|
||||
until_position=until_position,
|
||||
origin_scope=origin_scope,
|
||||
)
|
||||
for f in stub_only_filters:
|
||||
yield f
|
||||
|
||||
for f in filters:
|
||||
yield f
|
||||
@@ -169,6 +172,15 @@ class StubOnlyModuleContext(_StubOnlyContextMixin, ModuleContext):
|
||||
for f in self._get_base_filters(filters, search_global, until_position, origin_scope):
|
||||
yield f
|
||||
|
||||
def _iter_modules(self, path):
|
||||
dirs = os.listdir(path)
|
||||
for name in dirs:
|
||||
if os.path.isdir(os.path.join(path, name)):
|
||||
yield (None, name, True)
|
||||
if name.endswith('.pyi'):
|
||||
yield (None, name[:-4], True)
|
||||
return []
|
||||
|
||||
|
||||
class StubOnlyClass(_StubOnlyContextMixin, ClassMixin, ContextWrapper):
|
||||
pass
|
||||
@@ -209,8 +221,7 @@ class CompiledStubClass(_StubOnlyContextMixin, _CompiledStubContext, ClassMixin)
|
||||
|
||||
|
||||
class TypingModuleWrapper(StubOnlyModuleContext):
|
||||
# TODO should use this instead of the isinstance check
|
||||
def get_filterss(self, *args, **kwargs):
|
||||
def get_filters(self, *args, **kwargs):
|
||||
filters = super(TypingModuleWrapper, self).get_filters(*args, **kwargs)
|
||||
yield TypingModuleFilterWrapper(next(filters))
|
||||
for f in filters:
|
||||
@@ -334,6 +345,35 @@ def stubify(parent_context, context):
|
||||
return with_stub_context_if_possible(context)
|
||||
|
||||
|
||||
def _load_or_get_stub_module(evaluator, names):
|
||||
return evaluator.stub_module_cache.get(names)
|
||||
|
||||
|
||||
def load_stubs(context):
|
||||
root_context = context.get_root_context()
|
||||
stub_module = _load_or_get_stub_module(
|
||||
context.evaluator,
|
||||
root_context.string_names
|
||||
)
|
||||
if stub_module is None:
|
||||
return NO_CONTEXTS
|
||||
|
||||
qualified_names = context.get_qualified_names()
|
||||
if qualified_names is None:
|
||||
return NO_CONTEXTS
|
||||
|
||||
stub_contexts = ContextSet([stub_module])
|
||||
for name in qualified_names:
|
||||
stub_contexts = stub_contexts.py__getattribute__(name)
|
||||
|
||||
if isinstance(context, AnnotatedClass):
|
||||
return ContextSet([
|
||||
context.annotate_other_class(c) if c.is_class() else c
|
||||
for c in stub_contexts
|
||||
])
|
||||
return stub_contexts
|
||||
|
||||
|
||||
class CompiledStubName(NameWrapper):
|
||||
def __init__(self, parent_context, compiled_name, stub_name):
|
||||
super(CompiledStubName, self).__init__(stub_name)
|
||||
|
||||
@@ -6,7 +6,6 @@ from jedi._compatibility import FileNotFoundError
|
||||
from jedi.parser_utils import get_cached_code_lines
|
||||
from jedi.evaluate.cache import evaluator_function_cache
|
||||
from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS
|
||||
from jedi.evaluate.context import ModuleContext
|
||||
from jedi.evaluate.gradual.stub_context import StubModuleContext, \
|
||||
TypingModuleWrapper, StubOnlyModuleContext
|
||||
|
||||
@@ -89,13 +88,7 @@ def _cache_stub_file_map(version_info):
|
||||
|
||||
|
||||
def import_module_decorator(func):
|
||||
def wrapper(evaluator, import_names, parent_module_context, sys_path, load_stub=True):
|
||||
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
|
||||
|
||||
def wrapper(evaluator, import_names, parent_module_context, sys_path):
|
||||
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
|
||||
@@ -103,41 +96,33 @@ def import_module_decorator(func):
|
||||
# ``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(
|
||||
actual_context_set = parent_module_context.py__getattribute__('path')
|
||||
else:
|
||||
actual_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
|
||||
stub = _try_to_load_stub(evaluator, actual_context_set, parent_module_context, import_names)
|
||||
if stub is not None:
|
||||
return ContextSet(stub)
|
||||
return actual_context_set
|
||||
|
||||
if not load_stub:
|
||||
return context_set
|
||||
return try_to_merge_with_stub(evaluator, parent_module_context,
|
||||
import_names, context_set)
|
||||
return wrapper
|
||||
|
||||
|
||||
def try_to_merge_with_stub(evaluator, parent_module_context, import_names, actual_context_set):
|
||||
def _try_to_load_stub(evaluator, actual_context_set, parent_module_context, import_names):
|
||||
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:
|
||||
elif isinstance(parent_module_context, StubOnlyModuleContext):
|
||||
if not parent_module_context.is_package:
|
||||
# Only if it's a package (= a folder) something can be
|
||||
# imported.
|
||||
return actual_context_set
|
||||
path = parent_module_context.stub_context.py__path__()
|
||||
return None
|
||||
path = parent_module_context.py__path__()
|
||||
map_ = _merge_create_stub_map(path)
|
||||
|
||||
if map_ is not None:
|
||||
@@ -150,10 +135,14 @@ def try_to_merge_with_stub(evaluator, parent_module_context, import_names, actua
|
||||
# TODO maybe empty cache?
|
||||
pass
|
||||
else:
|
||||
return create_stub_module(evaluator, actual_context_set,
|
||||
stub_module_node, path, import_names)
|
||||
return create_stub_module(
|
||||
evaluator, actual_context_set, stub_module_node, path,
|
||||
import_names
|
||||
)
|
||||
evaluator.stub_module_cache[import_names] = None
|
||||
# If no stub is found, just return the default.
|
||||
return actual_context_set
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def create_stub_module(evaluator, actual_context_set, stub_module_node, path, import_names):
|
||||
@@ -171,4 +160,5 @@ def create_stub_module(evaluator, actual_context_set, stub_module_node, path, im
|
||||
code_lines=get_cached_code_lines(evaluator.latest_grammar, path),
|
||||
is_package=file_name == '__init__.pyi',
|
||||
)
|
||||
return stub_module_context.get_stub_contexts()
|
||||
evaluator.stub_module_cache[import_names] = stub_module_context
|
||||
return [stub_module_context]
|
||||
|
||||
@@ -625,6 +625,9 @@ class AnnotatedClass(AbstractAnnotatedClass):
|
||||
def get_given_types(self):
|
||||
return list(_iter_over_arguments(self._index_context, self._context_of_index))
|
||||
|
||||
def annotate_other_class(self, cls):
|
||||
return AnnotatedClass(cls, self._index_context, self._context_of_index)
|
||||
|
||||
|
||||
class AnnotatedSubClass(AbstractAnnotatedClass):
|
||||
def __init__(self, class_context, given_types):
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import os
|
||||
|
||||
from jedi.evaluate.imports import JediImportError
|
||||
from jedi.evaluate.gradual.typeshed import TYPESHED_PATH, create_stub_module
|
||||
|
||||
|
||||
@@ -20,9 +19,8 @@ def load_proper_stub_module(evaluator, path, import_names, module_node):
|
||||
import_names = import_names[:-1]
|
||||
|
||||
if import_names is not None:
|
||||
try:
|
||||
actual_context_set = evaluator.import_module(import_names, load_stub=False)
|
||||
except JediImportError as e:
|
||||
actual_context_set = evaluator.import_module(import_names)
|
||||
if not actual_context_set:
|
||||
return None
|
||||
|
||||
context_set = create_stub_module(
|
||||
|
||||
Reference in New Issue
Block a user