Finished changing the import logic. The sys.path calculations within Jedi are clearer now.

This commit is contained in:
Dave Halter
2015-04-25 22:45:08 +02:00
parent d038fba9df
commit 06d134a7c1
7 changed files with 32 additions and 34 deletions

View File

@@ -284,7 +284,7 @@ class Script(object):
if eval_stmt is None: if eval_stmt is None:
return [] return []
module = self._parser.module() module = self._evaluator.wrap(self._parser.module())
names, level, _, _ = helpers.check_error_statements(module, self._pos) names, level, _, _ = helpers.check_error_statements(module, self._pos)
if names: if names:
names = [str(n) for n in names] names = [str(n) for n in names]

View File

@@ -74,6 +74,9 @@ class CompiledObject(Base):
def py__bool__(self): def py__bool__(self):
return bool(self.obj) return bool(self.obj)
def py__file__(self):
return self.obj.__file__
def is_class(self): def is_class(self):
return inspect.isclass(self.obj) return inspect.isclass(self.obj)

View File

@@ -23,7 +23,7 @@ from jedi import debug
from jedi import cache from jedi import cache
from jedi.parser import fast from jedi.parser import fast
from jedi.parser import tree as pr from jedi.parser import tree as pr
from jedi.evaluate.sys_path import get_sys_path, sys_path_with_modifications from jedi.evaluate import sys_path
from jedi.evaluate import helpers from jedi.evaluate import helpers
from jedi import settings from jedi import settings
from jedi.common import source_to_unicode from jedi.common import source_to_unicode
@@ -213,9 +213,11 @@ class _Importer(object):
self._evaluator = evaluator self._evaluator = evaluator
self.level = level self.level = level
self.module = module self.module = module
path = module.path try:
# TODO abspath self.file_path = module.py__file__()
self.file_path = os.path.dirname(path) if path is not None else None except AttributeError:
# Can be None for certain compiled modules like 'builtins'.
self.file_path = None
if level: if level:
base = module.py__package__().split('.') base = module.py__package__().split('.')
@@ -235,15 +237,13 @@ class _Importer(object):
else: else:
_add_error(self._evaluator, import_path[-1]) _add_error(self._evaluator, import_path[-1])
import_path = [] import_path = []
# TODO add import error.
# TODO add import error. debug.warning('Attempted relative import beyond top-level package.')
debug.warning('Attempted relative import beyond top-level package.')
else: else:
# Here we basically rewrite the level to 0. # Here we basically rewrite the level to 0.
import_path = tuple(base) + import_path import_path = tuple(base) + import_path
self.import_path = import_path self.import_path = import_path
@property @property
def str_import_path(self): def str_import_path(self):
"""Returns the import path as pure strings instead of `Name`.""" """Returns the import path as pure strings instead of `Name`."""
@@ -258,31 +258,20 @@ class _Importer(object):
@memoize_default() @memoize_default()
def sys_path_with_modifications(self): def sys_path_with_modifications(self):
in_path = [] in_path = []
sys_path_mod = list(sys_path_with_modifications(self._evaluator, self.module)) sys_path_mod = list(sys_path.sys_path_with_modifications(self._evaluator, self.module))
if self.file_path is not None: if self.file_path is not None:
# If you edit e.g. gunicorn, there will be imports like this: # If you edit e.g. gunicorn, there will be imports like this:
# `from gunicorn import something`. But gunicorn is not in the # `from gunicorn import something`. But gunicorn is not in the
# sys.path. Therefore look if gunicorn is a parent directory, #56. # sys.path. Therefore look if gunicorn is a parent directory, #56.
if self.import_path: # TODO is this check really needed? if self.import_path: # TODO is this check really needed?
parts = self.file_path.split(os.path.sep) for path in sys_path.traverse_parents(self.file_path):
for i, p in enumerate(parts): if os.path.basename(path) == self.str_import_path[0]:
if p == unicode(self.import_path[0]): in_path.append(os.path.dirname(path))
new = os.path.sep.join(parts[:i])
in_path.append(new)
if not self.module.has_explicit_absolute_import: # Since we know nothing about the call location of the sys.path,
# If the module explicitly asks for absolute imports, # it's a possibility that the current directory is the origin of
# there's probably a bogus local one. # the Python execution.
sys_path_mod.insert(0, self.file_path) sys_path_mod.insert(0, os.path.dirname(self.file_path))
# First the sys path is searched normally and if that doesn't
# succeed, try to search the parent directories, because sometimes
# Jedi doesn't recognize sys.path modifications (like py.test
# stuff).
old_path, temp_path = self.file_path, os.path.dirname(self.file_path)
while old_path != temp_path:
sys_path_mod.append(temp_path)
old_path, temp_path = temp_path, os.path.dirname(temp_path)
return in_path + sys_path_mod return in_path + sys_path_mod

View File

@@ -760,6 +760,12 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module, Wrapper)):
return name return name
def py__file__(self): def py__file__(self):
"""
In contrast to Python's __file__ can be None.
"""
if self._module.path is None:
return None
return os.path.abspath(self._module.path) return os.path.abspath(self._module.path)
def py__package__(self): def py__package__(self):

View File

@@ -188,7 +188,7 @@ def _get_paths_from_buildout_script(evaluator, buildout_script):
yield path yield path
def _traverse_parents(path): def traverse_parents(path):
while True: while True:
new = os.path.dirname(path) new = os.path.dirname(path)
if new == path: if new == path:
@@ -198,7 +198,7 @@ def _traverse_parents(path):
def _get_parent_dir_with_file(path, filename): def _get_parent_dir_with_file(path, filename):
for parent in _traverse_parents(path): for parent in traverse_parents(path):
if os.path.isfile(os.path.join(parent, filename)): if os.path.isfile(os.path.join(parent, filename)):
return parent return parent
return None return None
@@ -208,7 +208,7 @@ def _detect_django_path(module_path):
""" Detects the path of the very well known Django library (if used) """ """ Detects the path of the very well known Django library (if used) """
result = [] result = []
for parent in _traverse_parents(module_path): for parent in traverse_parents(module_path):
with common.ignored(IOError): with common.ignored(IOError):
with open(parent + os.path.sep + 'manage.py'): with open(parent + os.path.sep + 'manage.py'):
debug.dbg('Found django path: %s', module_path) debug.dbg('Found django path: %s', module_path)

View File

@@ -1,6 +1,6 @@
import not_in_sys_path from not_in_sys_path import not_in_sys_path
import not_in_sys_path_package from not_in_sys_path import not_in_sys_path_package
from not_in_sys_path_package import module from not_in_sys_path.not_in_sys_path_package import module
not_in_sys_path.value not_in_sys_path.value
not_in_sys_path_package.value not_in_sys_path_package.value