From 2fb04db0abfd7f222f9288f5550727fce1d943ac Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Wed, 21 Aug 2019 23:08:42 +0200 Subject: [PATCH] Fix the weird py__path__ behavior --- jedi/inference/compiled/access.py | 8 ++++++- jedi/inference/compiled/mixed.py | 2 +- jedi/inference/compiled/value.py | 9 +++++-- jedi/inference/gradual/typeshed.py | 7 +----- jedi/inference/imports.py | 38 ++++++++++++++---------------- jedi/inference/value/module.py | 35 +++++++++------------------ jedi/inference/value/namespace.py | 4 ++++ 7 files changed, 49 insertions(+), 54 deletions(-) diff --git a/jedi/inference/compiled/access.py b/jedi/inference/compiled/access.py index 8d948ffa..75cf29d3 100644 --- a/jedi/inference/compiled/access.py +++ b/jedi/inference/compiled/access.py @@ -273,7 +273,13 @@ class DirectObjectAccess(object): return [self._create_access_path(base) for base in self._obj.__bases__] 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 def get_repr(self): diff --git a/jedi/inference/compiled/mixed.py b/jedi/inference/compiled/mixed.py index 8a923d7e..92bcb804 100644 --- a/jedi/inference/compiled/mixed.py +++ b/jedi/inference/compiled/mixed.py @@ -277,7 +277,7 @@ def _create(inference_state, access_handle, parent_context, *args): file_io=file_io, string_names=string_names, code_lines=code_lines, - is_package=hasattr(compiled_object, 'py__path__'), + is_package=compiled_object.is_package, ).as_context() if name is not None: inference_state.module_cache.add(string_names, ValueSet([module_context])) diff --git a/jedi/inference/compiled/value.py b/jedi/inference/compiled/value.py index b4eaf7bd..ffe71e5a 100644 --- a/jedi/inference/compiled/value.py +++ b/jedi/inference/compiled/value.py @@ -83,9 +83,14 @@ class CompiledObject(Value): for access in self.access_handle.py__bases__() ) - @CheckAttribute() 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 def string_names(self): diff --git a/jedi/inference/gradual/typeshed.py b/jedi/inference/gradual/typeshed.py index f172c94f..f6712c9e 100644 --- a/jedi/inference/gradual/typeshed.py +++ b/jedi/inference/gradual/typeshed.py @@ -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. if not python_value_set: if parent_module_value is not None: - try: - method = parent_module_value.py__path__ - except AttributeError: - check_path = [] - else: - check_path = method() + check_path = parent_module_value.py__path__() or [] # In case import_names names_for_path = (import_names[-1],) else: diff --git a/jedi/inference/imports.py b/jedi/inference/imports.py index 136d6a37..e37fbf95 100644 --- a/jedi/inference/imports.py +++ b/jedi/inference/imports.py @@ -400,28 +400,26 @@ def import_module(inference_state, import_names, parent_module_value, sys_path): if is_pkg is None: return NO_VALUES else: - try: - method = parent_module_value.py__path__ - except AttributeError: - # The module is not a package. + paths = parent_module_value.py__path__() + if paths is None: + # The module might not be a package. 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: - paths = method() - 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 + return NO_VALUES if isinstance(file_io_or_ns, ImplicitNSInfo): from jedi.inference.value.namespace import ImplicitNamespaceValue diff --git a/jedi/inference/value/module.py b/jedi/inference/value/module.py index 3768523a..5f1581d0 100644 --- a/jedi/inference/value/module.py +++ b/jedi/inference/value/module.py @@ -83,12 +83,8 @@ class SubModuleDictMixin(object): package). """ names = {} - try: - method = self.py__path__ - except AttributeError: - pass - else: - mods = iter_module_names(self.inference_state, method()) + if self.is_package: + mods = iter_module_names(self.inference_state, self.py__path__()) for name in mods: # It's obviously a relative import to the current module. names[name] = SubModuleName(self, name) @@ -233,7 +229,15 @@ class ModuleValue(ModuleMixin, TreeValue): return self.string_names 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. first_few_lines = ''.join(self.code_lines[:50]) # 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. 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): return ModuleContext(self) diff --git a/jedi/inference/value/namespace.py b/jedi/inference/value/namespace.py index cddf2abe..81fc2305 100644 --- a/jedi/inference/value/namespace.py +++ b/jedi/inference/value/namespace.py @@ -61,6 +61,10 @@ class ImplicitNamespaceValue(Value, SubModuleDictMixin): def is_stub(self): return False + @property + def is_package(self): + return True + def as_context(self): return NamespaceContext(self)