forked from VimPlug/jedi
Use sys path mostly from project and move some sys path stuff around.
This commit is contained in:
@@ -111,7 +111,9 @@ class Script(object):
|
|||||||
|
|
||||||
# Load the Python grammar of the current interpreter.
|
# Load the Python grammar of the current interpreter.
|
||||||
self._grammar = parso.load_grammar()
|
self._grammar = parso.load_grammar()
|
||||||
self._evaluator = Evaluator(self._grammar, Project(sys_path=sys_path))
|
project = Project(sys_path=sys_path)
|
||||||
|
self._evaluator = Evaluator(self._grammar, project)
|
||||||
|
project.add_script_path(self.path)
|
||||||
debug.speed('init')
|
debug.speed('init')
|
||||||
|
|
||||||
@cache.memoize_method
|
@cache.memoize_method
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ there are global variables, which are holding the cache information. Some of
|
|||||||
these variables are being cleaned after every API usage.
|
these variables are being cleaned after every API usage.
|
||||||
"""
|
"""
|
||||||
import time
|
import time
|
||||||
import inspect
|
|
||||||
|
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from parso.cache import parser_cache
|
from parso.cache import parser_cache
|
||||||
@@ -46,8 +45,6 @@ def underscore_memoization(func):
|
|||||||
return getattr(self, name)
|
return getattr(self, name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
result = func(self)
|
result = func(self)
|
||||||
if inspect.isgenerator(result):
|
|
||||||
result = list(result)
|
|
||||||
setattr(self, name, result)
|
setattr(self, name, result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ only *evaluates* what needs to be *evaluated*. All the statements and modules
|
|||||||
that are not used are just being ignored.
|
that are not used are just being ignored.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import copy
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from parso.python import tree
|
from parso.python import tree
|
||||||
@@ -100,8 +99,8 @@ class Evaluator(object):
|
|||||||
self.dynamic_params_depth = 0
|
self.dynamic_params_depth = 0
|
||||||
self.is_analysis = False
|
self.is_analysis = False
|
||||||
self.python_version = sys.version_info[:2]
|
self.python_version = sys.version_info[:2]
|
||||||
|
|
||||||
self.project = project
|
self.project = project
|
||||||
|
project.add_evaluator(self)
|
||||||
|
|
||||||
self.reset_recursion_limitations()
|
self.reset_recursion_limitations()
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from jedi.evaluate.sys_path import get_venv_path
|
from jedi.evaluate.sys_path import get_venv_path, detect_additional_paths
|
||||||
|
from jedi.cache import underscore_memoization
|
||||||
|
|
||||||
|
|
||||||
class Project(object):
|
class Project(object):
|
||||||
@@ -14,11 +15,24 @@ class Project(object):
|
|||||||
if sys_path is None:
|
if sys_path is None:
|
||||||
sys_path = sys.path
|
sys_path = sys.path
|
||||||
|
|
||||||
sys_path = list(sys_path)
|
base_sys_path = list(sys_path)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sys_path.remove('')
|
base_sys_path.remove('')
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.sys_path = sys_path
|
self._base_sys_path = base_sys_path
|
||||||
|
|
||||||
|
def add_script_path(self, script_path):
|
||||||
|
self._script_path = script_path
|
||||||
|
|
||||||
|
def add_evaluator(self, evaluator):
|
||||||
|
self._evaluator = evaluator
|
||||||
|
|
||||||
|
@property
|
||||||
|
@underscore_memoization
|
||||||
|
def sys_path(self):
|
||||||
|
if self._script_path is None:
|
||||||
|
return self._base_sys_path
|
||||||
|
|
||||||
|
return self._base_sys_path + detect_additional_paths(self._evaluator, self._script_path)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ from jedi.evaluate.site import addsitedir
|
|||||||
|
|
||||||
from jedi._compatibility import exec_function, unicode
|
from jedi._compatibility import exec_function, unicode
|
||||||
from jedi.evaluate.cache import evaluator_function_cache
|
from jedi.evaluate.cache import evaluator_function_cache
|
||||||
from jedi.evaluate.compiled import CompiledObject
|
|
||||||
from jedi.evaluate.base_context import ContextualizedNode
|
from jedi.evaluate.base_context import ContextualizedNode
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
@@ -80,12 +79,24 @@ def _execute_code(module_path, code):
|
|||||||
try:
|
try:
|
||||||
res = variables['result']
|
res = variables['result']
|
||||||
if isinstance(res, str):
|
if isinstance(res, str):
|
||||||
return [os.path.abspath(res)]
|
return _abs_path(module_path, res)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
return []
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _abs_path(module_path, path):
|
||||||
|
if os.path.isabs(path):
|
||||||
|
return path
|
||||||
|
|
||||||
|
if module_path is None:
|
||||||
|
# In this case we have no idea where we actually are in the file
|
||||||
|
# system.
|
||||||
|
return None
|
||||||
|
|
||||||
|
base_dir = os.path.dirname(module_path)
|
||||||
|
return os.path.abspath(os.path.join(base_dir, path))
|
||||||
|
|
||||||
def _paths_from_assignment(module_context, expr_stmt):
|
def _paths_from_assignment(module_context, expr_stmt):
|
||||||
"""
|
"""
|
||||||
Extracts the assigned strings from an assignment that looks as follows::
|
Extracts the assigned strings from an assignment that looks as follows::
|
||||||
@@ -125,7 +136,9 @@ def _paths_from_assignment(module_context, expr_stmt):
|
|||||||
for lazy_context in cn.infer().iterate(cn):
|
for lazy_context in cn.infer().iterate(cn):
|
||||||
for context in lazy_context.infer():
|
for context in lazy_context.infer():
|
||||||
if is_string(context):
|
if is_string(context):
|
||||||
yield context.obj
|
abs_path = _abs_path(module_context.py__file__(), context.obj)
|
||||||
|
if abs_path is not None:
|
||||||
|
yield abs_path
|
||||||
|
|
||||||
|
|
||||||
def _paths_from_list_modifications(module_path, trailer1, trailer2):
|
def _paths_from_list_modifications(module_path, trailer1, trailer2):
|
||||||
@@ -143,10 +156,11 @@ def _paths_from_list_modifications(module_path, trailer1, trailer2):
|
|||||||
arg = trailer2.children[1]
|
arg = trailer2.children[1]
|
||||||
if name == 'insert' and len(arg.children) in (3, 4): # Possible trailing comma.
|
if name == 'insert' and len(arg.children) in (3, 4): # Possible trailing comma.
|
||||||
arg = arg.children[2]
|
arg = arg.children[2]
|
||||||
return _execute_code(module_path, arg.get_code())
|
path = _execute_code(module_path, arg.get_code())
|
||||||
|
return [] if path is None else [path]
|
||||||
|
|
||||||
|
|
||||||
def _check_module(module_context):
|
def check_sys_path_modifications(module_context):
|
||||||
"""
|
"""
|
||||||
Detect sys.path modifications within module.
|
Detect sys.path modifications within module.
|
||||||
"""
|
"""
|
||||||
@@ -161,10 +175,10 @@ def _check_module(module_context):
|
|||||||
if n.type == 'name' and n.value == 'path':
|
if n.type == 'name' and n.value == 'path':
|
||||||
yield name, power
|
yield name, power
|
||||||
|
|
||||||
sys_path = list(module_context.evaluator.project.sys_path) # copy
|
if module_context.tree_node is None:
|
||||||
if isinstance(module_context, CompiledObject):
|
return []
|
||||||
return sys_path
|
|
||||||
|
|
||||||
|
added = []
|
||||||
try:
|
try:
|
||||||
possible_names = module_context.tree_node.get_used_names()['path']
|
possible_names = module_context.tree_node.get_used_names()['path']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -173,39 +187,30 @@ def _check_module(module_context):
|
|||||||
for name, power in get_sys_path_powers(possible_names):
|
for name, power in get_sys_path_powers(possible_names):
|
||||||
expr_stmt = power.parent
|
expr_stmt = power.parent
|
||||||
if len(power.children) >= 4:
|
if len(power.children) >= 4:
|
||||||
sys_path.extend(
|
added.extend(
|
||||||
_paths_from_list_modifications(
|
_paths_from_list_modifications(
|
||||||
module_context.py__file__(), *power.children[2:4]
|
module_context.py__file__(), *power.children[2:4]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif expr_stmt is not None and expr_stmt.type == 'expr_stmt':
|
elif expr_stmt is not None and expr_stmt.type == 'expr_stmt':
|
||||||
sys_path.extend(_paths_from_assignment(module_context, expr_stmt))
|
added.extend(_paths_from_assignment(module_context, expr_stmt))
|
||||||
return sys_path
|
return added
|
||||||
|
|
||||||
|
|
||||||
@evaluator_function_cache(default=[])
|
@evaluator_function_cache(default=[])
|
||||||
def sys_path_with_modifications(evaluator, module_context):
|
def sys_path_with_modifications(evaluator, module_context):
|
||||||
path = module_context.py__file__()
|
return evaluator.project.sys_path + check_sys_path_modifications(module_context)
|
||||||
if path is None:
|
|
||||||
# Support for modules without a path is bad, therefore return the
|
|
||||||
# normal path.
|
|
||||||
return evaluator.project.sys_path
|
|
||||||
|
|
||||||
curdir = os.path.abspath(os.curdir)
|
|
||||||
#TODO why do we need a chdir?
|
|
||||||
with ignored(OSError):
|
|
||||||
os.chdir(os.path.dirname(path))
|
|
||||||
|
|
||||||
|
def detect_additional_paths(evaluator, script_path):
|
||||||
|
django_paths = _detect_django_path(script_path)
|
||||||
buildout_script_paths = set()
|
buildout_script_paths = set()
|
||||||
|
|
||||||
result = _check_module(module_context)
|
for buildout_script_path in _get_buildout_script_paths(script_path):
|
||||||
result += _detect_django_path(path)
|
|
||||||
for buildout_script_path in _get_buildout_script_paths(path):
|
|
||||||
for path in _get_paths_from_buildout_script(evaluator, buildout_script_path):
|
for path in _get_paths_from_buildout_script(evaluator, buildout_script_path):
|
||||||
buildout_script_paths.add(path)
|
buildout_script_paths.add(path)
|
||||||
# cleanup, back to old directory
|
|
||||||
os.chdir(curdir)
|
return django_paths + list(buildout_script_paths)
|
||||||
return list(result) + list(buildout_script_paths)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_paths_from_buildout_script(evaluator, buildout_script_path):
|
def _get_paths_from_buildout_script(evaluator, buildout_script_path):
|
||||||
@@ -220,7 +225,8 @@ def _get_paths_from_buildout_script(evaluator, buildout_script_path):
|
|||||||
return
|
return
|
||||||
|
|
||||||
from jedi.evaluate.context import ModuleContext
|
from jedi.evaluate.context import ModuleContext
|
||||||
for path in _check_module(ModuleContext(evaluator, module_node, buildout_script_path)):
|
module = ModuleContext(evaluator, module_node, buildout_script_path)
|
||||||
|
for path in check_sys_path_modifications(module):
|
||||||
yield path
|
yield path
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,17 @@
|
|||||||
import os
|
import os
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
import parso
|
from jedi import Script
|
||||||
|
|
||||||
from jedi._compatibility import u
|
|
||||||
from jedi.evaluate.sys_path import (_get_parent_dir_with_file,
|
from jedi.evaluate.sys_path import (_get_parent_dir_with_file,
|
||||||
_get_buildout_script_paths,
|
_get_buildout_script_paths,
|
||||||
sys_path_with_modifications,
|
check_sys_path_modifications)
|
||||||
_check_module)
|
|
||||||
from jedi.evaluate import Evaluator
|
|
||||||
from jedi.evaluate.project import Project
|
|
||||||
from jedi.evaluate.context.module import ModuleContext
|
|
||||||
|
|
||||||
from ..helpers import cwd_at
|
from ..helpers import cwd_at
|
||||||
|
|
||||||
|
|
||||||
def check_module_test(code):
|
def check_module_test(code):
|
||||||
grammar = parso.load_grammar()
|
module_context = Script(code)._get_module()
|
||||||
e = Evaluator(grammar, Project())
|
return check_sys_path_modifications(module_context)
|
||||||
module_context = ModuleContext(e, parso.parse(code), path=None)
|
|
||||||
return _check_module(module_context)
|
|
||||||
|
|
||||||
|
|
||||||
@cwd_at('test/test_evaluate/buildout_project/src/proj_name')
|
@cwd_at('test/test_evaluate/buildout_project/src/proj_name')
|
||||||
@@ -40,25 +32,27 @@ def test_buildout_detection():
|
|||||||
|
|
||||||
|
|
||||||
def test_append_on_non_sys_path():
|
def test_append_on_non_sys_path():
|
||||||
code = dedent(u("""
|
code = dedent("""
|
||||||
class Dummy(object):
|
class Dummy(object):
|
||||||
path = []
|
path = []
|
||||||
|
|
||||||
d = Dummy()
|
d = Dummy()
|
||||||
d.path.append('foo')"""))
|
d.path.append('foo')"""
|
||||||
|
)
|
||||||
|
|
||||||
paths = check_module_test(code)
|
paths = check_module_test(code)
|
||||||
assert len(paths) > 0
|
assert not paths
|
||||||
assert 'foo' not in paths
|
assert 'foo' not in paths
|
||||||
|
|
||||||
|
|
||||||
def test_path_from_invalid_sys_path_assignment():
|
def test_path_from_invalid_sys_path_assignment():
|
||||||
code = dedent(u("""
|
code = dedent("""
|
||||||
import sys
|
import sys
|
||||||
sys.path = 'invalid'"""))
|
sys.path = 'invalid'"""
|
||||||
|
)
|
||||||
|
|
||||||
paths = check_module_test(code)
|
paths = check_module_test(code)
|
||||||
assert len(paths) > 0
|
assert not paths
|
||||||
assert 'invalid' not in paths
|
assert 'invalid' not in paths
|
||||||
|
|
||||||
|
|
||||||
@@ -69,15 +63,12 @@ def test_sys_path_with_modifications():
|
|||||||
""")
|
""")
|
||||||
|
|
||||||
path = os.path.abspath(os.path.join(os.curdir, 'module_name.py'))
|
path = os.path.abspath(os.path.join(os.curdir, 'module_name.py'))
|
||||||
grammar = parso.load_grammar()
|
paths = Script(code, path=path)._evaluator.project.sys_path
|
||||||
module_node = parso.parse(code, path=path)
|
|
||||||
module_context = ModuleContext(Evaluator(grammar, Project()), module_node, path=path)
|
|
||||||
paths = sys_path_with_modifications(module_context.evaluator, module_context)
|
|
||||||
assert '/tmp/.buildout/eggs/important_package.egg' in paths
|
assert '/tmp/.buildout/eggs/important_package.egg' in paths
|
||||||
|
|
||||||
|
|
||||||
def test_path_from_sys_path_assignment():
|
def test_path_from_sys_path_assignment():
|
||||||
code = dedent(u("""
|
code = dedent("""
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@@ -91,7 +82,8 @@ def test_path_from_sys_path_assignment():
|
|||||||
import important_package
|
import important_package
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(important_package.main())"""))
|
sys.exit(important_package.main())"""
|
||||||
|
)
|
||||||
|
|
||||||
paths = check_module_test(code)
|
paths = check_module_test(code)
|
||||||
assert 1 not in paths
|
assert 1 not in paths
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ from jedi import Script
|
|||||||
|
|
||||||
def test_paths_from_assignment():
|
def test_paths_from_assignment():
|
||||||
def paths(src):
|
def paths(src):
|
||||||
script = Script(src)
|
script = Script(src, path='/foo/bar.py')
|
||||||
expr_stmt = script._get_module_node().children[0]
|
expr_stmt = script._get_module_node().children[0]
|
||||||
return set(sys_path._paths_from_assignment(script._get_module(), expr_stmt))
|
return set(sys_path._paths_from_assignment(script._get_module(), expr_stmt))
|
||||||
|
|
||||||
assert paths('sys.path[0:0] = ["a"]') == set(['a'])
|
assert paths('sys.path[0:0] = ["a"]') == set(['/foo/a'])
|
||||||
assert paths('sys.path = ["b", 1, x + 3, y, "c"]') == set(['b', 'c'])
|
assert paths('sys.path = ["b", 1, x + 3, y, "c"]') == set(['/foo/b', '/foo/c'])
|
||||||
assert paths('sys.path = a = ["a"]') == set(['a'])
|
assert paths('sys.path = a = ["a"]') == set(['/foo/a'])
|
||||||
|
|
||||||
# Fail for complicated examples.
|
# Fail for complicated examples.
|
||||||
assert paths('sys.path, other = ["a"], 2') == set()
|
assert paths('sys.path, other = ["a"], 2') == set()
|
||||||
|
|||||||
Reference in New Issue
Block a user