Better support for searching python environments

This commit is contained in:
Dave Halter
2017-12-15 12:19:52 +01:00
parent c3efde3bfa
commit 3cd5fa3c20
4 changed files with 54 additions and 17 deletions

View File

@@ -41,3 +41,4 @@ __version__ = '0.11.0'
from jedi.api import Script, Interpreter, set_debug_function, \ from jedi.api import Script, Interpreter, set_debug_function, \
preload_module, names preload_module, names
from jedi import settings from jedi import settings
from jedi.api.virtualenv import find_virtualenvs, create_environment

View File

@@ -121,7 +121,7 @@ class Script(object):
compiled_subprocess = None compiled_subprocess = None
else: else:
if environment is None: if environment is None:
environment = DefaultEnvironment(self.path) environment = DefaultEnvironment()
compiled_subprocess = environment.get_subprocess() compiled_subprocess = environment.get_subprocess()
self._evaluator = Evaluator(self._grammar, project, compiled_subprocess) self._evaluator = Evaluator(self._grammar, project, compiled_subprocess)
project.add_script_path(self.path) project.add_script_path(self.path)

View File

@@ -3,6 +3,9 @@ import re
import sys import sys
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from collections import namedtuple from collections import namedtuple
# When dropping Python 2.7 support we should consider switching to
# `shutil.which`.
from distutils.spawn import find_executable
from jedi.evaluate.project import Project from jedi.evaluate.project import Project
from jedi.cache import memoize_method from jedi.cache import memoize_method
@@ -10,19 +13,21 @@ from jedi.evaluate.compiled.subprocess import get_subprocess
_VersionInfo = namedtuple('VersionInfo', 'major minor micro') _VersionInfo = namedtuple('VersionInfo', 'major minor micro')
_SUPPORTED_PYTHONS = ['2.7', '3.3', '3.4', '3.5', '3.6']
class NoVirtualEnv(Exception):
class InvalidPythonEnvironment(Exception):
pass pass
class Environment(object): class Environment(object):
def __init__(self, path, executable): def __init__(self, path, executable):
self._path = path self._base_path = path
self._executable = executable self._executable = executable
self.version_info = _get_version(self._executable) self.version_info = _get_version(self._executable)
def __repr__(self): def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, self._path) return '<%s: %s>' % (self.__class__.__name__, self._base_path)
def get_project(self): def get_project(self):
return Project(self.get_sys_path()) return Project(self.get_sys_path())
@@ -41,10 +46,8 @@ class Environment(object):
class DefaultEnvironment(Environment): class DefaultEnvironment(Environment):
def __init__(self, script_path): def __init__(self):
# TODO make this usable super(DefaultEnvironment, self).__init__(sys.prefix, sys.executable)
path = script_path
super(DefaultEnvironment, self).__init__(path, sys.executable)
def find_virtualenvs(paths=None): def find_virtualenvs(paths=None):
@@ -55,10 +58,35 @@ def find_virtualenvs(paths=None):
executable = _get_executable_path(path) executable = _get_executable_path(path)
try: try:
yield Environment(path, executable) yield Environment(path, executable)
except NoVirtualEnv: except InvalidPythonEnvironment:
pass pass
def find_python_environments():
"""
Ignores virtualenvs and returns the different Python versions.
"""
current_version = '%s.%s' % (sys.version_info.major, sys.version_info.minor)
for version_string in _SUPPORTED_PYTHONS:
if version_string == current_version:
yield DefaultEnvironment()
else:
exe = find_executable('python' + version_string)
if exe is not None:
path = os.path.dirname(os.path.dirname(exe))
try:
yield Environment(path, exe)
except InvalidPythonEnvironment:
pass
def create_environment(path):
"""
Make it possible to create
"""
return Environment(path, _get_executable_path(path))
def _get_executable_path(path): def _get_executable_path(path):
""" """
Returns None if it's not actually a virtual env. Returns None if it's not actually a virtual env.
@@ -77,15 +105,15 @@ def _get_version(executable):
stdout, stderr = process.communicate() stdout, stderr = process.communicate()
retcode = process.poll() retcode = process.poll()
if retcode: if retcode:
raise NoVirtualEnv() raise InvalidPythonEnvironment()
except OSError: except OSError:
raise NoVirtualEnv() raise InvalidPythonEnvironment()
# Until Python 3.4 wthe version string is part of stderr, after that # Until Python 3.4 wthe version string is part of stderr, after that
# stdout. # stdout.
output = stdout + stderr output = stdout + stderr
match = re.match(br'Python (\d+)\.(\d+)\.(\d+)', output) match = re.match(br'Python (\d+)\.(\d+)\.(\d+)', output)
if match is None: if match is None:
raise NoVirtualEnv() raise InvalidPythonEnvironment()
return _VersionInfo(*match.groups()) return _VersionInfo(*match.groups())

View File

@@ -2,23 +2,31 @@ import pytest
import jedi import jedi
from jedi._compatibility import py_version from jedi._compatibility import py_version
from jedi.api.virtualenv import Environment, DefaultEnvironment, NoVirtualEnv from jedi.api.virtualenv import Environment, DefaultEnvironment, \
InvalidPythonEnvironment, find_python_environments
def test_sys_path(): def test_sys_path():
assert DefaultEnvironment('/foo').get_sys_path() assert DefaultEnvironment().get_sys_path()
def test_find_python_environments():
envs = list(find_python_environments())
assert len(envs)
for env in envs:
assert env.version_info
assert env.get_sys_path()
@pytest.mark.parametrize( @pytest.mark.parametrize(
'version', 'version',
# TODO add '2.6', '2.7', ['2.7', '3.3', '3.4', '3.5', '3.6', '3.7']
['3.3', '3.4', '3.5', '3.6', '3.7']
) )
def test_versions(version): def test_versions(version):
executable = 'python' + version executable = 'python' + version
try: try:
env = Environment('some path', executable) env = Environment('some path', executable)
except NoVirtualEnv: except InvalidPythonEnvironment:
if int(version.replace('.', '')) == py_version: if int(version.replace('.', '')) == py_version:
# At least the current version has to work # At least the current version has to work
raise raise