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:
return []
module = self._parser.module()
module = self._evaluator.wrap(self._parser.module())
names, level, _, _ = helpers.check_error_statements(module, self._pos)
if names:
names = [str(n) for n in names]

View File

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

View File

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

View File

@@ -760,6 +760,12 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module, Wrapper)):
return name
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)
def py__package__(self):

View File

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

View File

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