mirror of
https://github.com/davidhalter/jedi.git
synced 2026-05-20 07:19:40 +08:00
Fix relative imports outside of the proper paths
This commit is contained in:
@@ -82,7 +82,8 @@ class Script(object):
|
|||||||
:type sys_path: Environment
|
:type sys_path: Environment
|
||||||
"""
|
"""
|
||||||
def __init__(self, source=None, line=None, column=None, path=None,
|
def __init__(self, source=None, line=None, column=None, path=None,
|
||||||
encoding='utf-8', sys_path=None, environment=None):
|
encoding='utf-8', sys_path=None, environment=None,
|
||||||
|
_project=None):
|
||||||
self._orig_path = path
|
self._orig_path = path
|
||||||
# An empty path (also empty string) should always result in no path.
|
# An empty path (also empty string) should always result in no path.
|
||||||
self.path = os.path.abspath(path) if path else None
|
self.path = os.path.abspath(path) if path else None
|
||||||
@@ -98,10 +99,12 @@ class Script(object):
|
|||||||
if sys_path is not None and not is_py3:
|
if sys_path is not None and not is_py3:
|
||||||
sys_path = list(map(force_unicode, sys_path))
|
sys_path = list(map(force_unicode, sys_path))
|
||||||
|
|
||||||
# Load the Python grammar of the current interpreter.
|
project = _project
|
||||||
project = get_default_project(
|
if project is None:
|
||||||
os.path.dirname(self.path)if path else os.getcwd()
|
# Load the Python grammar of the current interpreter.
|
||||||
)
|
project = get_default_project(
|
||||||
|
os.path.dirname(self.path)if path else os.getcwd()
|
||||||
|
)
|
||||||
# TODO deprecate and remove sys_path from the Script API.
|
# TODO deprecate and remove sys_path from the Script API.
|
||||||
if sys_path is not None:
|
if sys_path is not None:
|
||||||
project._sys_path = sys_path
|
project._sys_path = sys_path
|
||||||
|
|||||||
+14
-14
@@ -211,7 +211,7 @@ def _level_to_base_import_path(project_path, directory, level):
|
|||||||
# import path for it.
|
# import path for it.
|
||||||
while True:
|
while True:
|
||||||
if d == project_path:
|
if d == project_path:
|
||||||
return level_import_paths, None
|
return level_import_paths, d
|
||||||
dir_name = os.path.basename(d)
|
dir_name = os.path.basename(d)
|
||||||
if dir_name:
|
if dir_name:
|
||||||
level_import_paths.insert(0, dir_name)
|
level_import_paths.insert(0, dir_name)
|
||||||
@@ -280,18 +280,19 @@ class Importer(object):
|
|||||||
base_import_path, base_directory = _level_to_base_import_path(
|
base_import_path, base_directory = _level_to_base_import_path(
|
||||||
self._evaluator.project._path, directory, level,
|
self._evaluator.project._path, directory, level,
|
||||||
)
|
)
|
||||||
|
if base_directory is None:
|
||||||
|
# Everything is lost, the relative import does point
|
||||||
|
# somewhere out of the filesystem.
|
||||||
|
self._inference_possible = False
|
||||||
|
else:
|
||||||
|
self._fixed_sys_path = [base_directory]
|
||||||
|
|
||||||
if base_import_path is None:
|
if base_import_path is None:
|
||||||
if import_path:
|
if import_path:
|
||||||
_add_error(
|
_add_error(
|
||||||
module_context, import_path[0],
|
module_context, import_path[0],
|
||||||
message='Attempted relative import beyond top-level package.'
|
message='Attempted relative import beyond top-level package.'
|
||||||
)
|
)
|
||||||
if base_directory is None:
|
|
||||||
# Everything is lost, the relative import does point
|
|
||||||
# somewhere out of the filesystem.
|
|
||||||
self._inference_possible = False
|
|
||||||
else:
|
|
||||||
self._fixed_sys_path = [base_directory]
|
|
||||||
else:
|
else:
|
||||||
import_path = base_import_path + import_path
|
import_path = base_import_path + import_path
|
||||||
self.import_path = import_path
|
self.import_path = import_path
|
||||||
@@ -375,6 +376,9 @@ class Importer(object):
|
|||||||
:param only_modules: Indicates wheter it's possible to import a
|
:param only_modules: Indicates wheter it's possible to import a
|
||||||
definition that is not defined in a module.
|
definition that is not defined in a module.
|
||||||
"""
|
"""
|
||||||
|
if not self._inference_possible:
|
||||||
|
return []
|
||||||
|
|
||||||
names = []
|
names = []
|
||||||
if self.import_path:
|
if self.import_path:
|
||||||
# flask
|
# flask
|
||||||
@@ -416,15 +420,11 @@ class Importer(object):
|
|||||||
for filter in context.get_filters(search_global=False):
|
for filter in context.get_filters(search_global=False):
|
||||||
names += filter.values()
|
names += filter.values()
|
||||||
else:
|
else:
|
||||||
# Empty import path=completion after import
|
|
||||||
if self.level:
|
if self.level:
|
||||||
if self.file_path is not None:
|
# We only get here if the level cannot be properly calculated.
|
||||||
path = self.file_path
|
names += self._get_module_names(self._fixed_sys_path)
|
||||||
for i in range(self.level):
|
|
||||||
path = os.path.dirname(path)
|
|
||||||
raise 1
|
|
||||||
names += self._get_module_names([path])
|
|
||||||
else:
|
else:
|
||||||
|
# This is just the list of global imports.
|
||||||
names += self._get_module_names()
|
names += self._get_module_names()
|
||||||
return names
|
return names
|
||||||
|
|
||||||
|
|||||||
@@ -280,20 +280,38 @@ def test_get_modules_containing_name(evaluator, path, goal):
|
|||||||
@pytest.mark.parametrize('empty_sys_path', (False, True))
|
@pytest.mark.parametrize('empty_sys_path', (False, True))
|
||||||
def test_relative_imports_with_multiple_similar_directories(Script, path, empty_sys_path):
|
def test_relative_imports_with_multiple_similar_directories(Script, path, empty_sys_path):
|
||||||
dir = get_example_dir('issue1209')
|
dir = get_example_dir('issue1209')
|
||||||
|
if empty_sys_path:
|
||||||
|
project = Project(dir, sys_path=(), smart_sys_path=False)
|
||||||
|
else:
|
||||||
|
project = Project(dir)
|
||||||
script = Script(
|
script = Script(
|
||||||
"from . ",
|
"from . ",
|
||||||
path=os.path.join(dir, path),
|
path=os.path.join(dir, path),
|
||||||
|
_project=project,
|
||||||
)
|
)
|
||||||
# TODO pass this project to the script as a param once that's possible.
|
|
||||||
if empty_sys_path:
|
|
||||||
script._evaluator.project = Project(dir, smart_sys_path=False)
|
|
||||||
else:
|
|
||||||
script._evaluator.project = Project(dir)
|
|
||||||
name, import_ = script.completions()
|
name, import_ = script.completions()
|
||||||
assert import_.name == 'import'
|
assert import_.name == 'import'
|
||||||
assert name.name == 'api_test1'
|
assert name.name == 'api_test1'
|
||||||
|
|
||||||
|
|
||||||
|
def test_relative_imports_x(Script):
|
||||||
|
dir = get_example_dir('issue1209')
|
||||||
|
project = Project(dir, sys_path=[], smart_sys_path=False)
|
||||||
|
script = Script(
|
||||||
|
"from ...",
|
||||||
|
path=os.path.join(dir, 'api/whatever/test_this.py'),
|
||||||
|
_project=project,
|
||||||
|
)
|
||||||
|
assert [c.name for c in script.completions()] == ['api', 'import', 'whatever']
|
||||||
|
|
||||||
|
script = Script(
|
||||||
|
"from " + '.' * 100,
|
||||||
|
path=os.path.join(dir, 'api/whatever/test_this.py'),
|
||||||
|
_project=project,
|
||||||
|
)
|
||||||
|
assert [c.name for c in script.completions()] == ['import']
|
||||||
|
|
||||||
|
|
||||||
@cwd_at('test/examples/issue1209/api/whatever/')
|
@cwd_at('test/examples/issue1209/api/whatever/')
|
||||||
def test_relative_imports_without_path(Script):
|
def test_relative_imports_without_path(Script):
|
||||||
script = Script("from . ")
|
script = Script("from . ")
|
||||||
@@ -322,12 +340,12 @@ def test_relative_import_out_of_file_system(Script):
|
|||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'level, directory, project_path, result', [
|
'level, directory, project_path, result', [
|
||||||
(1, '/a/b/c', '/a', (['b', 'c'], None)),
|
(1, '/a/b/c', '/a', (['b', 'c'], '/a')),
|
||||||
(2, '/a/b/c', '/a', (['b'], None)),
|
(2, '/a/b/c', '/a', (['b'], '/a')),
|
||||||
(3, '/a/b/c', '/a', ([], None)),
|
(3, '/a/b/c', '/a', ([], '/a')),
|
||||||
(4, '/a/b/c', '/a', (None, '/')),
|
(4, '/a/b/c', '/a', (None, '/')),
|
||||||
(5, '/a/b/c', '/a', (None, None)),
|
(5, '/a/b/c', '/a', (None, None)),
|
||||||
(1, '/', '/', ([], None)),
|
(1, '/', '/', ([], '/')),
|
||||||
(2, '/', '/', (None, None)),
|
(2, '/', '/', (None, None)),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user