mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-07 22:44:27 +08:00
Better support for searching python environments
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user