1
0
forked from VimPlug/jedi

Use sys path mostly from project and move some sys path stuff around.

This commit is contained in:
Dave Halter
2017-10-05 10:06:28 +02:00
parent 383f749026
commit 51d2ffb078
7 changed files with 77 additions and 67 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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()