diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index b77d35c9..bc2083bb 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -21,6 +21,7 @@ from jedi.parser_utils import get_executable_nodes from jedi import debug from jedi import settings from jedi import cache +from jedi.file_io import KnownContentFileIO from jedi.api import classes from jedi.api import interpreter from jedi.api import helpers @@ -38,8 +39,7 @@ from jedi.evaluate.syntax_tree import tree_name_to_contexts from jedi.evaluate.context import ModuleContext from jedi.evaluate.base_context import ContextSet from jedi.evaluate.context.iterable import unpack_tuple_to_dict -from jedi.evaluate.gradual.conversion import try_stubs_to_actual_context_set, \ - try_stub_to_actual_names +from jedi.evaluate.gradual.conversion import try_stub_to_actual_names from jedi.evaluate.gradual.utils import load_proper_stub_module # Jedi uses lots and lots of recursion. By setting this a little bit higher, we @@ -164,11 +164,12 @@ class Script(object): names = import_names is_package = is_p + file_io = KnownContentFileIO(cast_path(self.path), self._code) if self.path is not None and self.path.endswith('.pyi'): # We are in a stub file. Try to load the stub properly. stub_module = load_proper_stub_module( self._evaluator, - cast_path(self.path), + file_io, names, self._module_node ) @@ -179,14 +180,11 @@ class Script(object): names = ('__main__',) module = ModuleContext( - self._evaluator, self._module_node, cast_path(self.path), + self._evaluator, self._module_node, file_io, string_names=names, code_lines=self._code_lines, is_package=is_package, ) - #module, = try_to_merge_with_stub( - # self._evaluator, None, module.string_names, ContextSet([module]) - #) if names[0] not in ('builtins', '__builtin__', 'typing'): # These modules are essential for Jedi, so don't overwrite them. self._evaluator.module_cache.add(names, ContextSet([module])) @@ -469,7 +467,7 @@ class Interpreter(Script): self._evaluator, self._module_node, self.namespaces, - path=self.path, + file_io=KnownContentFileIO(self.path, self._code), code_lines=self._code_lines, ) diff --git a/jedi/api/interpreter.py b/jedi/api/interpreter.py index 56bb3586..d7eb50ce 100644 --- a/jedi/api/interpreter.py +++ b/jedi/api/interpreter.py @@ -24,14 +24,14 @@ class MixedModuleContext(Context): # TODO use ContextWrapper! type = 'mixed_module' - def __init__(self, evaluator, tree_module, namespaces, path, code_lines): + def __init__(self, evaluator, tree_module, namespaces, file_io, code_lines): self.evaluator = evaluator self._namespaces = namespaces self._namespace_objects = [NamespaceObject(n) for n in namespaces] self._module_context = ModuleContext( evaluator, tree_module, - path=path, + file_io=file_io, string_names=('__main__',), code_lines=code_lines ) diff --git a/jedi/evaluate/compiled/mixed.py b/jedi/evaluate/compiled/mixed.py index 3a2aea7e..d9041122 100644 --- a/jedi/evaluate/compiled/mixed.py +++ b/jedi/evaluate/compiled/mixed.py @@ -11,6 +11,7 @@ from jedi import settings from jedi.evaluate import compiled from jedi.cache import underscore_memoization from jedi.evaluate import imports +from jedi.file_io import FileIO from jedi.evaluate.base_context import Context, ContextSet from jedi.evaluate.context import ModuleContext from jedi.evaluate.cache import evaluator_function_cache @@ -140,6 +141,7 @@ def _find_syntax_node_name(evaluator, access_handle): # The path might not exist or be e.g. . return None + file_io = FileIO(path) module_node = _load_module(evaluator, path) if inspect.ismodule(python_object): @@ -147,7 +149,7 @@ def _find_syntax_node_name(evaluator, access_handle): # a way to write a module in a module in Python (and also __name__ can # be something like ``email.utils``). code_lines = get_cached_code_lines(evaluator.grammar, path) - return module_node, module_node, path, code_lines + return module_node, module_node, file_io, code_lines try: name_str = python_object.__name__ @@ -186,7 +188,7 @@ def _find_syntax_node_name(evaluator, access_handle): # completions at some points but will lead to mostly correct type # inference, because people tend to define a public name in a module only # once. - return module_node, names[-1].parent, path, code_lines + return module_node, names[-1].parent, file_io, code_lines @compiled_objects_cache('mixed_cache') @@ -198,7 +200,7 @@ def _create(evaluator, access_handle, parent_context, *args): if result is None: return compiled_object - module_node, tree_node, path, code_lines = result + module_node, tree_node, file_io, code_lines = result if parent_context.tree_node.get_root_node() == module_node: module_context = parent_context.get_root_context() @@ -208,7 +210,7 @@ def _create(evaluator, access_handle, parent_context, *args): string_names = tuple(name.split('.')) module_context = ModuleContext( evaluator, module_node, - path=path, + file_io=file_io, string_names=string_names, code_lines=code_lines, is_package=hasattr(compiled_object, 'py__path__'), diff --git a/jedi/evaluate/context/module.py b/jedi/evaluate/context/module.py index 051e2ccc..197c8efa 100644 --- a/jedi/evaluate/context/module.py +++ b/jedi/evaluate/context/module.py @@ -173,13 +173,17 @@ class ModuleContext(ModuleMixin, TreeContext): api_type = u'module' parent_context = None - def __init__(self, evaluator, module_node, path, string_names, code_lines, is_package=False): + def __init__(self, evaluator, module_node, file_io, string_names, code_lines, is_package=False): super(ModuleContext, self).__init__( evaluator, parent_context=None, tree_node=module_node ) - self._path = path + self._file_io = file_io + if self._file_io is None: + self._path = None + else: + self._path = file_io.path self.string_names = string_names self.code_lines = code_lines self.is_package = is_package diff --git a/jedi/evaluate/gradual/typeshed.py b/jedi/evaluate/gradual/typeshed.py index ee3a3079..4019e149 100644 --- a/jedi/evaluate/gradual/typeshed.py +++ b/jedi/evaluate/gradual/typeshed.py @@ -4,7 +4,6 @@ import re from jedi.file_io import FileIO from jedi._compatibility import FileNotFoundError, cast_path from jedi.parser_utils import get_cached_code_lines -from jedi.evaluate.cache import evaluator_function_cache from jedi.evaluate.base_context import ContextSet from jedi.evaluate.gradual.stub_context import TypingModuleWrapper, StubModuleContext @@ -62,11 +61,6 @@ def _get_typeshed_directories(version_info): yield os.path.join(base, check_version) -@evaluator_function_cache() -def _load_stub(evaluator, path): - return evaluator.parse(file_io=FileIO(path), cache=True, use_latest_grammar=True) - - _version_cache = {} @@ -149,7 +143,12 @@ def _try_to_load_stub(evaluator, import_names, actual_context_set, # foo-stubs for p in sys_path: init = os.path.join(p, *import_names) + '-stubs' + os.path.sep + '__init__.pyi' - m = _try_to_load_stub_from_file(evaluator, actual_context_set, init, import_names) + m = _try_to_load_stub_from_file( + evaluator, + actual_context_set, + file_io=FileIO(init), + import_names=import_names, + ) if m is not None: return m @@ -172,8 +171,8 @@ def _try_to_load_stub(evaluator, import_names, actual_context_set, evaluator, actual_context_set, # The file path should end with .pyi - file_path, - import_names + file_io=FileIO(file_path), + import_names=import_names, ) if m is not None: return m @@ -202,8 +201,8 @@ def _try_to_load_stub(evaluator, import_names, actual_context_set, m = _try_to_load_stub_from_file( evaluator, actual_context_set, - os.path.join(p, *names_for_path) + '.pyi', - import_names, + file_io=FileIO(os.path.join(p, *names_for_path) + '.pyi'), + import_names=import_names, ) if m is not None: return m @@ -229,35 +228,44 @@ def _load_from_typeshed(evaluator, actual_context_set, parent_module_context, im if map_ is not None: path = map_.get(import_name) if path is not None: - return _try_to_load_stub_from_file(evaluator, actual_context_set, path, import_names) + return _try_to_load_stub_from_file( + evaluator, + actual_context_set, + file_io=FileIO(path), + import_names=import_names, + ) -def _try_to_load_stub_from_file(evaluator, actual_context_set, path, import_names): +def _try_to_load_stub_from_file(evaluator, actual_context_set, file_io, import_names): try: - stub_module_node = _load_stub(evaluator, path) + stub_module_node = evaluator.parse( + file_io=file_io, + cache=True, + use_latest_grammar=True + ) except (OSError, IOError): # IOError is Python 2 only # The file that you're looking for doesn't exist (anymore). return None else: return create_stub_module( - evaluator, actual_context_set, stub_module_node, path, + evaluator, actual_context_set, stub_module_node, file_io, import_names ) -def create_stub_module(evaluator, actual_context_set, stub_module_node, path, import_names): +def create_stub_module(evaluator, actual_context_set, stub_module_node, file_io, import_names): if import_names == ('typing',): module_cls = TypingModuleWrapper else: module_cls = StubModuleContext - file_name = os.path.basename(path) + file_name = os.path.basename(file_io.path) stub_module_context = module_cls( actual_context_set, evaluator, stub_module_node, - path=path, + file_io=file_io, string_names=import_names, # The code was loaded with latest_grammar, so use # that. - code_lines=get_cached_code_lines(evaluator.latest_grammar, path), + code_lines=get_cached_code_lines(evaluator.latest_grammar, file_io.path), is_package=file_name == '__init__.pyi', ) return stub_module_context diff --git a/jedi/evaluate/gradual/utils.py b/jedi/evaluate/gradual/utils.py index 7de63252..6bc60e69 100644 --- a/jedi/evaluate/gradual/utils.py +++ b/jedi/evaluate/gradual/utils.py @@ -3,11 +3,12 @@ import os from jedi.evaluate.gradual.typeshed import TYPESHED_PATH, create_stub_module -def load_proper_stub_module(evaluator, path, import_names, module_node): +def load_proper_stub_module(evaluator, file_io, import_names, module_node): """ This function is given a random .pyi file and should return the proper module. """ + path = file_io.path assert path.endswith('.pyi') if path.startswith(TYPESHED_PATH): # /foo/stdlib/3/os/__init__.pyi -> stdlib/3/os/__init__ @@ -24,7 +25,7 @@ def load_proper_stub_module(evaluator, path, import_names, module_node): return None stub = create_stub_module( - evaluator, actual_context_set, module_node, path, import_names + evaluator, actual_context_set, module_node, file_io, import_names ) evaluator.stub_module_cache[import_names] = stub return stub diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index 4c6b9f13..5fec9415 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -464,7 +464,7 @@ def _load_python_module(evaluator, file_io, sys_path=None, from jedi.evaluate.context import ModuleContext return ModuleContext( evaluator, module_node, - path=file_io.path, + file_io=file_io, string_names=import_names, code_lines=get_cached_code_lines(evaluator.grammar, file_io.path), is_package=is_package, diff --git a/jedi/evaluate/sys_path.py b/jedi/evaluate/sys_path.py index 4cbef176..8dea434b 100644 --- a/jedi/evaluate/sys_path.py +++ b/jedi/evaluate/sys_path.py @@ -6,6 +6,7 @@ from jedi.evaluate.base_context import ContextualizedNode from jedi.evaluate.helpers import is_string from jedi.common.utils import traverse_parents from jedi.parser_utils import get_cached_code_lines +from jedi.file_io import FileIO from jedi import settings from jedi import debug @@ -140,9 +141,10 @@ def discover_buildout_paths(evaluator, script_path): def _get_paths_from_buildout_script(evaluator, buildout_script_path): + file_io = FileIO(buildout_script_path) try: module_node = evaluator.parse( - path=buildout_script_path, + file_io=file_io, cache=True, cache_path=settings.cache_directory ) @@ -152,7 +154,7 @@ def _get_paths_from_buildout_script(evaluator, buildout_script_path): from jedi.evaluate.context import ModuleContext module = ModuleContext( - evaluator, module_node, buildout_script_path, + evaluator, module_node, file_io, string_names=None, code_lines=get_cached_code_lines(evaluator.grammar, buildout_script_path), ) diff --git a/jedi/plugins/stdlib.py b/jedi/plugins/stdlib.py index 6612733c..03674b81 100644 --- a/jedi/plugins/stdlib.py +++ b/jedi/plugins/stdlib.py @@ -437,7 +437,7 @@ def collections_namedtuple(obj, arguments): generated_class = next(module.iter_classdefs()) parent_context = ModuleContext( evaluator, module, - path=None, + file_io=None, string_names=None, code_lines=parso.split_lines(code, keepends=True), )