mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-07 06:24:27 +08:00
Fix the weird py__path__ behavior
This commit is contained in:
@@ -273,7 +273,13 @@ class DirectObjectAccess(object):
|
|||||||
return [self._create_access_path(base) for base in self._obj.__bases__]
|
return [self._create_access_path(base) for base in self._obj.__bases__]
|
||||||
|
|
||||||
def py__path__(self):
|
def py__path__(self):
|
||||||
return self._obj.__path__
|
paths = getattr(self._obj, '__path__', None)
|
||||||
|
# Avoid some weird hacks that would just fail, because they cannot be
|
||||||
|
# used by pickle.
|
||||||
|
if not isinstance(paths, list) \
|
||||||
|
or not all(isinstance(p, (bytes, unicode)) for p in paths):
|
||||||
|
return None
|
||||||
|
return paths
|
||||||
|
|
||||||
@_force_unicode_decorator
|
@_force_unicode_decorator
|
||||||
def get_repr(self):
|
def get_repr(self):
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ def _create(inference_state, access_handle, parent_context, *args):
|
|||||||
file_io=file_io,
|
file_io=file_io,
|
||||||
string_names=string_names,
|
string_names=string_names,
|
||||||
code_lines=code_lines,
|
code_lines=code_lines,
|
||||||
is_package=hasattr(compiled_object, 'py__path__'),
|
is_package=compiled_object.is_package,
|
||||||
).as_context()
|
).as_context()
|
||||||
if name is not None:
|
if name is not None:
|
||||||
inference_state.module_cache.add(string_names, ValueSet([module_context]))
|
inference_state.module_cache.add(string_names, ValueSet([module_context]))
|
||||||
|
|||||||
@@ -83,9 +83,14 @@ class CompiledObject(Value):
|
|||||||
for access in self.access_handle.py__bases__()
|
for access in self.access_handle.py__bases__()
|
||||||
)
|
)
|
||||||
|
|
||||||
@CheckAttribute()
|
|
||||||
def py__path__(self):
|
def py__path__(self):
|
||||||
return map(cast_path, self.access_handle.py__path__())
|
paths = self.access_handle.py__path__()
|
||||||
|
if paths is None:
|
||||||
|
return None
|
||||||
|
return map(cast_path, paths)
|
||||||
|
|
||||||
|
def is_package(self):
|
||||||
|
return self.py__path__() is not None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def string_names(self):
|
def string_names(self):
|
||||||
|
|||||||
@@ -206,12 +206,7 @@ def _try_to_load_stub(inference_state, import_names, python_value_set,
|
|||||||
# 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_value is not None:
|
if parent_module_value is not None:
|
||||||
try:
|
check_path = parent_module_value.py__path__() or []
|
||||||
method = parent_module_value.py__path__
|
|
||||||
except AttributeError:
|
|
||||||
check_path = []
|
|
||||||
else:
|
|
||||||
check_path = method()
|
|
||||||
# In case import_names
|
# In case import_names
|
||||||
names_for_path = (import_names[-1],)
|
names_for_path = (import_names[-1],)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -400,28 +400,26 @@ def import_module(inference_state, import_names, parent_module_value, sys_path):
|
|||||||
if is_pkg is None:
|
if is_pkg is None:
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
else:
|
else:
|
||||||
try:
|
paths = parent_module_value.py__path__()
|
||||||
method = parent_module_value.py__path__
|
if paths is None:
|
||||||
except AttributeError:
|
# The module might not be a package.
|
||||||
# The module is not a package.
|
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
|
|
||||||
|
for path in paths:
|
||||||
|
# At the moment we are only using one path. So this is
|
||||||
|
# not important to be correct.
|
||||||
|
if not isinstance(path, list):
|
||||||
|
path = [path]
|
||||||
|
file_io_or_ns, is_pkg = inference_state.compiled_subprocess.get_module_info(
|
||||||
|
string=import_names[-1],
|
||||||
|
path=path,
|
||||||
|
full_name=module_name,
|
||||||
|
is_global_search=False,
|
||||||
|
)
|
||||||
|
if is_pkg is not None:
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
paths = method()
|
return NO_VALUES
|
||||||
for path in paths:
|
|
||||||
# At the moment we are only using one path. So this is
|
|
||||||
# not important to be correct.
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
file_io_or_ns, is_pkg = inference_state.compiled_subprocess.get_module_info(
|
|
||||||
string=import_names[-1],
|
|
||||||
path=path,
|
|
||||||
full_name=module_name,
|
|
||||||
is_global_search=False,
|
|
||||||
)
|
|
||||||
if is_pkg is not None:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
return NO_VALUES
|
|
||||||
|
|
||||||
if isinstance(file_io_or_ns, ImplicitNSInfo):
|
if isinstance(file_io_or_ns, ImplicitNSInfo):
|
||||||
from jedi.inference.value.namespace import ImplicitNamespaceValue
|
from jedi.inference.value.namespace import ImplicitNamespaceValue
|
||||||
|
|||||||
@@ -83,12 +83,8 @@ class SubModuleDictMixin(object):
|
|||||||
package).
|
package).
|
||||||
"""
|
"""
|
||||||
names = {}
|
names = {}
|
||||||
try:
|
if self.is_package:
|
||||||
method = self.py__path__
|
mods = iter_module_names(self.inference_state, self.py__path__())
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
mods = iter_module_names(self.inference_state, method())
|
|
||||||
for name in mods:
|
for name in mods:
|
||||||
# It's obviously a relative import to the current module.
|
# It's obviously a relative import to the current module.
|
||||||
names[name] = SubModuleName(self, name)
|
names[name] = SubModuleName(self, name)
|
||||||
@@ -233,7 +229,15 @@ class ModuleValue(ModuleMixin, TreeValue):
|
|||||||
return self.string_names
|
return self.string_names
|
||||||
return self.string_names[:-1]
|
return self.string_names[:-1]
|
||||||
|
|
||||||
def _py__path__(self):
|
def py__path__(self):
|
||||||
|
"""
|
||||||
|
In case of a package, this returns Python's __path__ attribute, which
|
||||||
|
is a list of paths (strings).
|
||||||
|
Returns None if the module is not a package.
|
||||||
|
"""
|
||||||
|
if not self.is_package:
|
||||||
|
return None
|
||||||
|
|
||||||
# A namespace package is typically auto generated and ~10 lines long.
|
# A namespace package is typically auto generated and ~10 lines long.
|
||||||
first_few_lines = ''.join(self.code_lines[:50])
|
first_few_lines = ''.join(self.code_lines[:50])
|
||||||
# these are strings that need to be used for namespace packages,
|
# these are strings that need to be used for namespace packages,
|
||||||
@@ -258,23 +262,6 @@ class ModuleValue(ModuleMixin, TreeValue):
|
|||||||
assert file is not None # Shouldn't be a package in the first place.
|
assert file is not None # Shouldn't be a package in the first place.
|
||||||
return [os.path.dirname(file)]
|
return [os.path.dirname(file)]
|
||||||
|
|
||||||
@property
|
|
||||||
def py__path__(self):
|
|
||||||
"""
|
|
||||||
Not seen here, since it's a property. The callback actually uses a
|
|
||||||
variable, so use it like::
|
|
||||||
|
|
||||||
foo.py__path__(sys_path)
|
|
||||||
|
|
||||||
In case of a package, this returns Python's __path__ attribute, which
|
|
||||||
is a list of paths (strings).
|
|
||||||
Raises an AttributeError if the module is not a package.
|
|
||||||
"""
|
|
||||||
if self.is_package:
|
|
||||||
return self._py__path__
|
|
||||||
else:
|
|
||||||
raise AttributeError('Only packages have __path__ attributes.')
|
|
||||||
|
|
||||||
def _as_context(self):
|
def _as_context(self):
|
||||||
return ModuleContext(self)
|
return ModuleContext(self)
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ class ImplicitNamespaceValue(Value, SubModuleDictMixin):
|
|||||||
def is_stub(self):
|
def is_stub(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_package(self):
|
||||||
|
return True
|
||||||
|
|
||||||
def as_context(self):
|
def as_context(self):
|
||||||
return NamespaceContext(self)
|
return NamespaceContext(self)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user