Make it possible to connect to a subprocess to get the sys path

This commit is contained in:
Dave Halter
2017-11-14 18:25:37 +01:00
parent 46b81dfa6d
commit 96149d2e6a
7 changed files with 51 additions and 52 deletions

View File

@@ -24,6 +24,7 @@ from jedi.api import classes
from jedi.api import interpreter
from jedi.api import helpers
from jedi.api.completion import Completion
from jedi.api.virtualenv import DefaultEnvironment
from jedi.evaluate import Evaluator
from jedi.evaluate import imports
from jedi.evaluate import usages
@@ -117,22 +118,15 @@ class Script(object):
project = Project(sys_path=sys_path)
if isinstance(self, Interpreter):
# It's not possible to use a subprocess for the interpreter.
self._compiled_subprocess = None
compiled_subprocess = None
else:
if environment is None:
executable = sys.executable
else:
executable = environment.executable
self._compiled_subprocess = get_subprocess(executable)
self._evaluator = Evaluator(self._grammar, project, self._compiled_subprocess)
environment = DefaultEnvironment()
compiled_subprocess = environment.get_subprocess()
self._evaluator = Evaluator(self._grammar, project, compiled_subprocess)
project.add_script_path(self.path)
debug.speed('init')
def __del__(self):
if self._compiled_subprocess is not None:
self._compiled_subprocess.delete_evaluator(evaluator)
self._evaluator.cleanup_evaluator
@cache.memoize_method
def _get_module_node(self):
return self._grammar.parse(

View File

@@ -1,5 +1,6 @@
import os
import re
import sys
import sysconfig
from subprocess import CalledProcessError
from collections import namedtuple
@@ -7,7 +8,7 @@ from collections import namedtuple
from jedi._compatibility import check_output
from jedi.evaluate.project import Project
from jedi.cache import memoize_method
from jedi.evaluate.compiled.subprocess import get_subprocess
_VersionInfo = namedtuple('VersionInfo', 'major minor micro')
@@ -28,6 +29,10 @@ class Environment(object):
def get_project(self):
return Project(self.get_sys_path())
def get_subprocess(self):
return get_subprocess(self._executable)
@memoize_method
def get_sys_path(self):
vars = {
'base': self._path
@@ -38,35 +43,15 @@ class Environment(object):
# on how the Python version was compiled (ENV variables).
# If you omit -S when starting Python (normal case), additionally
# site.py gets executed.
write
# venv
['', '/usr/lib/python3.3', '/usr/lib/python3.3/plat-x86_64-linux-gnu',
'/usr/lib/python3.3/lib-dynload',
'/home/dave/source/python/virtenv/venv3.3/lib/python3.3/site-packages']
['/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu',
'/usr/lib/python3.4/lib-dynload',
'/home/dave/source/stuff_cloudscale/rgw-metrics/venv/lib/python3.4/site-packages']
['', '/usr/lib/python35.zip', '/usr/lib/python3.5',
'/usr/lib/python3.5/plat-x86_64-linux-gnu',
'/usr/lib/python3.5/lib-dynload',
'/home/dave/source/python/virtenv/venv3.5/lib/python3.5/site-packages']
return self.get_subprocess().get_sys_path()
{'purelib': '{base}/local/lib/python{py_version_short}/dist-packages',
'stdlib': '{base}/lib/python{py_version_short}',
'scripts': '{base}/local/bin',
'platinclude': '{platbase}/local/include/python{py_version_short}',
'include': '{base}/local/include/python{py_version_short}',
'data': '{base}/local',
'platstdlib': '{platbase}/lib/python{py_version_short}',
'platlib': '{platbase}/local/lib/python{py_version_short}/dist-packages'}
return [] + additional_paths
def _get_long_running_process(self):
return process
class DefaultEnvironment(Environment):
def __init__(self, script_path):
# TODO make this usable
path = script_path
super(DefaultEnvironment, self).__init__(path, sys.executable)
def find_virtualenvs(paths=None):
@@ -95,11 +80,11 @@ def _get_executable_path(path):
def _get_version(executable):
try:
output = check_output(executable, '--version')
output = check_output([executable, '--version'])
except (CalledProcessError, OSError):
raise NoVirtualEnv()
match = re.match(r'Python (\d+)\.(\d+)\.(\d+)', output)
match = re.match(rb'Python (\d+)\.(\d+)\.(\d+)', output)
if match is None:
raise NoVirtualEnv()

View File

@@ -14,7 +14,7 @@ import pickle
from functools import partial
from jedi.cache import memoize_method
from jedi.evaluate.compiled.subprocess import commands
from jedi.evaluate.compiled.subprocess import functions
_PICKLE_PROTOCOL = 2
@@ -29,6 +29,11 @@ def get_subprocess(executable):
return sub
def _get_function(evaluator, name):
function = getattr(functions, name)
return partial(function, evaluator)
class EvaluatorSameProcess(object):
"""
Basically just an easy access to functions.py. It has the same API
@@ -38,9 +43,8 @@ class EvaluatorSameProcess(object):
def __init__(self, evaluator):
self._evaluator = evaluator
def __getattr__(self):
function = getattr(commands, name)
return partial(function, self._evaluator)
def __getattr__(self, name):
return _get_function(self._evaluator, name)
class EvaluatorSubprocess(object):
@@ -50,11 +54,10 @@ class EvaluatorSubprocess(object):
self._compiled_subprocess = compiled_subprocess
def __getattr__(self, name):
function = getattr(commands, name)
return partial(function, self._evaluator_weakref())
return _get_function(self._evaluator_weakref(), name)
def __del__(self):
self.delete_evaluator(self._evaluator_weakref()
self.delete_evaluator(self._evaluator_weakref())
class _Subprocess(object):
@@ -93,6 +96,9 @@ class _CompiledSubprocess(_Subprocess):
assert callable(function)
return self._send(id(evaluator), function, args, kwargs)
def get_sys_path(self):
return self._send(None, functions.get_sys_path, (), {})
def delete_evaluator(self, evaluator_id):
# With an argument - the evaluator gets deleted.
self._send(evaluator_id, None)
@@ -102,7 +108,7 @@ class Listener():
def __init__(self):
self._evaluators = {}
def _run(self, evaluator_id, function, args, kwargs):
def _get_evaluator(self, function, evaluator_id):
from jedi.evaluate import Evaluator
if function is None:
@@ -116,7 +122,13 @@ class Listener():
except KeyError:
evaluator = Evaluator(None, None)
self.evaluators[evaluator_id] = evaluator
return evaluator
def _run(self, evaluator_id, function, args, kwargs):
if evaluator_id is None:
return function(*args, **kwargs)
else:
evaluator = self._get_evaluator(evaluator_id)
return function(evaluator, *args, **kwargs)
def listen(self):

View File

@@ -1,3 +1,3 @@
from jedi.evaluate.compiled import subprocess
subprocess.listen()
subprocess.Listener().listen()

View File

@@ -0,0 +1,5 @@
import sys
def get_sys_path():
return sys.path

View File

@@ -3,12 +3,10 @@ import sys
from jedi.evaluate.sys_path import get_venv_path, detect_additional_paths
from jedi.cache import memoize_method
from jedi.evaluate.compiled.subprocess import CompiledSubProcess
class Project(object):
def __init__(self, sys_path=None):
self._compiled_subprocess = CompiledSubProcess()
if sys_path is not None:
self._sys_path = sys_path

View File

@@ -0,0 +1,5 @@
from jedi.api.virtualenv import Environment, DefaultEnvironment
def test_sys_path():
assert DefaultEnvironment('/foo').get_sys_path()