1
0
forked from VimPlug/jedi

Fix os path resolving issues

This commit is contained in:
Dave Halter
2019-08-21 00:22:34 +02:00
parent 14fc5ed289
commit 85f8f2a764
8 changed files with 53 additions and 48 deletions

View File

@@ -12,9 +12,7 @@ from jedi import debug
from jedi.inference.utils import unite from jedi.inference.utils import unite
from jedi.cache import memoize_method from jedi.cache import memoize_method
from jedi.inference import imports from jedi.inference import imports
from jedi.inference import compiled
from jedi.inference.imports import ImportName from jedi.inference.imports import ImportName
from jedi.inference.value import FunctionExecutionContext
from jedi.inference.gradual.typeshed import StubModuleValue from jedi.inference.gradual.typeshed import StubModuleValue
from jedi.inference.gradual.conversion import convert_names, convert_values from jedi.inference.gradual.conversion import convert_names, convert_values
from jedi.inference.base_value import ValueSet from jedi.inference.base_value import ValueSet
@@ -25,14 +23,14 @@ def _sort_names_by_start_pos(names):
return sorted(names, key=lambda s: s.start_pos or (0, 0)) return sorted(names, key=lambda s: s.start_pos or (0, 0))
def defined_names(inference_state, value): def defined_names(inference_state, context):
""" """
List sub-definitions (e.g., methods in class). List sub-definitions (e.g., methods in class).
:type scope: Scope :type scope: Scope
:rtype: list of Definition :rtype: list of Definition
""" """
filter = next(value.get_filters()) filter = next(context.get_filters())
names = [name for name in filter.values()] names = [name for name in filter.values()]
return [Definition(inference_state, n) for n in _sort_names_by_start_pos(names)] return [Definition(inference_state, n) for n in _sort_names_by_start_pos(names)]
@@ -71,7 +69,7 @@ class BaseDefinition(object):
self.is_keyword = isinstance(self._name, KeywordName) self.is_keyword = isinstance(self._name, KeywordName)
@memoize_method @memoize_method
def _get_module(self): def _get_module_context(self):
# This can take a while to complete, because in the worst case of # This can take a while to complete, because in the worst case of
# imports (consider `import a` completions), we need to load all # imports (consider `import a` completions), we need to load all
# modules starting with a first. # modules starting with a first.
@@ -80,11 +78,11 @@ class BaseDefinition(object):
@property @property
def module_path(self): def module_path(self):
"""Shows the file path of a module. e.g. ``/usr/lib/python2.7/os.py``""" """Shows the file path of a module. e.g. ``/usr/lib/python2.7/os.py``"""
module = self._get_module() module = self._get_module_context()
if module.is_stub() or not module.is_compiled(): if module.is_stub() or not module.is_compiled():
# Compiled modules should not return a module path even if they # Compiled modules should not return a module path even if they
# have one. # have one.
return self._get_module().py__file__() return self._get_module_context().py__file__()
return None return None
@@ -183,14 +181,14 @@ class BaseDefinition(object):
>>> print(d.module_name) # doctest: +ELLIPSIS >>> print(d.module_name) # doctest: +ELLIPSIS
json json
""" """
return self._get_module().py__name__() return self._get_module_context().py__name__()
def in_builtin_module(self): def in_builtin_module(self):
"""Whether this is a builtin module.""" """Whether this is a builtin module."""
if isinstance(self._get_module(), StubModuleValue): value = self._get_module_context().get_value()
return any(isinstance(value, compiled.CompiledObject) if isinstance(value, StubModuleValue):
for value in self._get_module().non_stub_value_set) return any(v.is_compiled() for v in value.non_stub_value_set)
return isinstance(self._get_module(), compiled.CompiledObject) return value.is_compiled()
@property @property
def line(self): def line(self):
@@ -360,13 +358,12 @@ class BaseDefinition(object):
if not self._name.is_value_name: if not self._name.is_value_name:
return None return None
value = self._name.parent_context context = self._name.parent_context
if value is None: if context is None:
return None return None
if isinstance(value, FunctionExecutionContext): # TODO private access!
value = value.function_value return Definition(self._inference_state, context._value.name)
return Definition(self._inference_state, value.name)
def __repr__(self): def __repr__(self):
return "<%s %sname=%r, description=%r>" % ( return "<%s %sname=%r, description=%r>" % (
@@ -588,7 +585,7 @@ class Definition(BaseDefinition):
""" """
defs = self._name.infer() defs = self._name.infer()
return sorted( return sorted(
unite(defined_names(self._inference_state, d) for d in defs), unite(defined_names(self._inference_state, d.as_context()) for d in defs),
key=lambda s: s._name.start_pos or (0, 0) key=lambda s: s._name.start_pos or (0, 0)
) )

View File

@@ -109,6 +109,7 @@ class Completion:
self._like_name, self._call_signatures_callback, self._like_name, self._call_signatures_callback,
self._code_lines, self._original_position self._code_lines, self._original_position
)) ))
return completions
if completions: if completions:
return completions return completions

View File

@@ -111,11 +111,11 @@ class InferenceState(object):
self.reset_recursion_limitations() self.reset_recursion_limitations()
self.allow_different_encoding = True self.allow_different_encoding = True
def import_module(self, import_names, parent_module_context=None, def import_module(self, import_names, parent_module_value=None,
sys_path=None, prefer_stubs=True): sys_path=None, prefer_stubs=True):
if sys_path is None: if sys_path is None:
sys_path = self.get_sys_path() sys_path = self.get_sys_path()
return imports.import_module(self, import_names, parent_module_context, return imports.import_module(self, import_names, parent_module_value,
sys_path, prefer_stubs=prefer_stubs) sys_path, prefer_stubs=prefer_stubs)
@staticmethod @staticmethod

View File

@@ -103,6 +103,10 @@ class ModuleContext(AbstractContext):
def py__package__(self): def py__package__(self):
return self._value.py__package__ return self._value.py__package__
@property
def is_package(self):
return self._value.is_package
def get_filters(self, until_position=None, origin_scope=None): def get_filters(self, until_position=None, origin_scope=None):
filters = self._value.get_filters(origin_scope) filters = self._value.get_filters(origin_scope)
# Skip the first filter and replace it. # Skip the first filter and replace it.

View File

@@ -91,7 +91,7 @@ def _load_stub_module(module):
module.inference_state, module.inference_state,
import_names=module.string_names, import_names=module.string_names,
python_value_set=ValueSet([module]), python_value_set=ValueSet([module]),
parent_module_context=None, parent_module_value=None,
sys_path=module.inference_state.get_sys_path(), sys_path=module.inference_state.get_sys_path(),
) )

View File

@@ -7,7 +7,7 @@ from jedi._compatibility import FileNotFoundError, cast_path
from jedi.parser_utils import get_cached_code_lines from jedi.parser_utils import get_cached_code_lines
from jedi.inference.base_value import ValueSet, NO_VALUES from jedi.inference.base_value import ValueSet, NO_VALUES
from jedi.inference.gradual.stub_value import TypingModuleWrapper, StubModuleValue from jedi.inference.gradual.stub_value import TypingModuleWrapper, StubModuleValue
from jedi.inference.context import ModuleContext from jedi.inference.value import ModuleValue
_jedi_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) _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') TYPESHED_PATH = os.path.join(_jedi_path, 'third_party', 'typeshed')
@@ -90,27 +90,30 @@ def _cache_stub_file_map(version_info):
def import_module_decorator(func): def import_module_decorator(func):
@wraps(func) @wraps(func)
def wrapper(inference_state, import_names, parent_module_context, sys_path, prefer_stubs): def wrapper(inference_state, import_names, parent_module_value, sys_path, prefer_stubs):
try: try:
python_value_set = inference_state.module_cache.get(import_names) python_value_set = inference_state.module_cache.get(import_names)
except KeyError: except KeyError:
if parent_module_context is not None and parent_module_context.is_stub(): if parent_module_value is not None and parent_module_value.is_stub():
parent_module_contexts = parent_module_context.non_stub_value_set parent_module_values = parent_module_value.non_stub_value_set
else: else:
parent_module_contexts = [parent_module_context] parent_module_values = [parent_module_value]
if import_names == ('os', 'path'): if import_names == ('os', 'path'):
# This is a huge exception, we follow a nested import # This is a huge exception, we follow a nested import
# ``os.path``, because it's a very important one in Python # ``os.path``, because it's a very important one in Python
# that is being achieved by messing with ``sys.modules`` in # that is being achieved by messing with ``sys.modules`` in
# ``os``. # ``os``.
python_parent = next(iter(parent_module_contexts)) python_parent = next(iter(parent_module_values))
if python_parent is None: if python_parent is None:
python_parent, = inference_state.import_module(('os',), prefer_stubs=False) python_parent, = inference_state.import_module(('os',), prefer_stubs=False)
python_value_set = python_parent.py__getattribute__('path') python_value_set = ValueSet.from_sets(
func(inference_state, (n,), None, sys_path,)
for n in ['posixpath', 'ntpath', 'macpath', 'os2emxpath']
)
else: else:
python_value_set = ValueSet.from_sets( python_value_set = ValueSet.from_sets(
func(inference_state, import_names, p, sys_path,) func(inference_state, import_names, p, sys_path,)
for p in parent_module_contexts for p in parent_module_values
) )
inference_state.module_cache.add(import_names, python_value_set) inference_state.module_cache.add(import_names, python_value_set)
@@ -118,7 +121,7 @@ def import_module_decorator(func):
return python_value_set return python_value_set
stub = _try_to_load_stub_cached(inference_state, import_names, python_value_set, stub = _try_to_load_stub_cached(inference_state, import_names, python_value_set,
parent_module_context, sys_path) parent_module_value, sys_path)
if stub is not None: if stub is not None:
return ValueSet([stub]) return ValueSet([stub])
return python_value_set return python_value_set
@@ -141,18 +144,18 @@ def _try_to_load_stub_cached(inference_state, import_names, *args, **kwargs):
def _try_to_load_stub(inference_state, import_names, python_value_set, def _try_to_load_stub(inference_state, import_names, python_value_set,
parent_module_context, sys_path): parent_module_value, sys_path):
""" """
Trying to load a stub for a set of import_names. Trying to load a stub for a set of import_names.
This is modelled to work like "PEP 561 -- Distributing and Packaging Type This is modelled to work like "PEP 561 -- Distributing and Packaging Type
Information", see https://www.python.org/dev/peps/pep-0561. Information", see https://www.python.org/dev/peps/pep-0561.
""" """
if parent_module_context is None and len(import_names) > 1: if parent_module_value is None and len(import_names) > 1:
try: try:
parent_module_context = _try_to_load_stub_cached( parent_module_value = _try_to_load_stub_cached(
inference_state, import_names[:-1], NO_VALUES, inference_state, import_names[:-1], NO_VALUES,
parent_module_context=None, sys_path=sys_path) parent_module_value=None, sys_path=sys_path)
except KeyError: except KeyError:
pass pass
@@ -196,15 +199,15 @@ def _try_to_load_stub(inference_state, import_names, python_value_set,
return m return m
# 3. Try to load typeshed # 3. Try to load typeshed
m = _load_from_typeshed(inference_state, python_value_set, parent_module_context, import_names) m = _load_from_typeshed(inference_state, python_value_set, parent_module_value, import_names)
if m is not None: if m is not None:
return m return m
# 4. Try to load pyi file somewhere if python_value_set was not defined. # 4. Try to load pyi file somewhere if python_value_set was not defined.
if not python_value_set: if not python_value_set:
if parent_module_context is not None: if parent_module_value is not None:
try: try:
method = parent_module_context.py__path__ method = parent_module_value.py__path__
except AttributeError: except AttributeError:
check_path = [] check_path = []
else: else:
@@ -230,18 +233,18 @@ def _try_to_load_stub(inference_state, import_names, python_value_set,
return None return None
def _load_from_typeshed(inference_state, python_value_set, parent_module_context, import_names): def _load_from_typeshed(inference_state, python_value_set, parent_module_value, import_names):
import_name = import_names[-1] import_name = import_names[-1]
map_ = None map_ = None
if len(import_names) == 1: if len(import_names) == 1:
map_ = _cache_stub_file_map(inference_state.grammar.version_info) map_ = _cache_stub_file_map(inference_state.grammar.version_info)
import_name = _IMPORT_MAP.get(import_name, import_name) import_name = _IMPORT_MAP.get(import_name, import_name)
elif isinstance(parent_module_context, ModuleContext): elif isinstance(parent_module_value, ModuleValue):
if not parent_module_context.is_package: if not parent_module_value.is_package:
# Only if it's a package (= a folder) something can be # Only if it's a package (= a folder) something can be
# imported. # imported.
return None return None
path = parent_module_context.py__path__() path = parent_module_value.py__path__()
map_ = _merge_create_stub_map(path) map_ = _merge_create_stub_map(path)
if map_ is not None: if map_ is not None:

View File

@@ -295,9 +295,9 @@ class Importer(object):
value_set = ValueSet.from_sets([ value_set = ValueSet.from_sets([
self._inference_state.import_module( self._inference_state.import_module(
import_names[:i+1], import_names[:i+1],
parent_module_context, parent_module_value,
sys_path sys_path
) for parent_module_context in value_set ) for parent_module_value in value_set
]) ])
if not value_set: if not value_set:
message = 'No module named ' + '.'.join(import_names) message = 'No module named ' + '.'.join(import_names)
@@ -377,7 +377,7 @@ class Importer(object):
@plugin_manager.decorate() @plugin_manager.decorate()
@import_module_decorator @import_module_decorator
def import_module(inference_state, import_names, parent_module_context, sys_path): def import_module(inference_state, import_names, parent_module_value, sys_path):
""" """
This method is very similar to importlib's `_gcd_import`. This method is very similar to importlib's `_gcd_import`.
""" """
@@ -388,7 +388,7 @@ def import_module(inference_state, import_names, parent_module_context, sys_path
return ValueSet([module]) return ValueSet([module])
module_name = '.'.join(import_names) module_name = '.'.join(import_names)
if parent_module_context is None: if parent_module_value is None:
# Override the sys.path. It works only good that way. # Override the sys.path. It works only good that way.
# Injecting the path directly into `find_module` did not work. # Injecting the path directly into `find_module` did not work.
file_io_or_ns, is_pkg = inference_state.compiled_subprocess.get_module_info( file_io_or_ns, is_pkg = inference_state.compiled_subprocess.get_module_info(
@@ -401,7 +401,7 @@ def import_module(inference_state, import_names, parent_module_context, sys_path
return NO_VALUES return NO_VALUES
else: else:
try: try:
method = parent_module_context.py__path__ method = parent_module_value.py__path__
except AttributeError: except AttributeError:
# The module is not a package. # The module is not a package.
return NO_VALUES return NO_VALUES
@@ -441,7 +441,7 @@ def import_module(inference_state, import_names, parent_module_context, sys_path
is_package=is_pkg, is_package=is_pkg,
) )
if parent_module_context is None: if parent_module_value is None:
debug.dbg('global search_module %s: %s', import_names[-1], module) debug.dbg('global search_module %s: %s', import_names[-1], module)
else: else:
debug.dbg('search_module %s in paths %s: %s', module_name, paths, module) debug.dbg('search_module %s in paths %s: %s', module_name, paths, module)

View File

@@ -15,7 +15,7 @@ def test_base_auto_import_modules(auto_import_json, Script):
loads, = Script('import json; json.loads').goto_definitions() loads, = Script('import json; json.loads').goto_definitions()
assert isinstance(loads._name, ValueName) assert isinstance(loads._name, ValueName)
value, = loads._name.infer() value, = loads._name.infer()
assert isinstance(value.parent_context, StubModuleValue) assert isinstance(value.parent_context._value, StubModuleValue)
def test_auto_import_modules_imports(auto_import_json, Script): def test_auto_import_modules_imports(auto_import_json, Script):