forked from tox-dev/tox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
test_interpreters.py
234 lines (193 loc) · 7.69 KB
/
test_interpreters.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
from __future__ import unicode_literals
import os
import platform
import stat
import subprocess
import sys
import py
import pytest
import tox
from tox import reporter
from tox.config import get_plugin_manager
from tox.interpreters import (
ExecFailed,
InterpreterInfo,
Interpreters,
NoInterpreterInfo,
run_and_get_interpreter_info,
tox_get_python_executable,
)
from tox.reporter import Verbosity
@pytest.fixture(name="interpreters")
def create_interpreters_instance():
pm = get_plugin_manager()
return Interpreters(hook=pm.hook)
@pytest.mark.skipif(tox.INFO.IS_PYPY, reason="testing cpython interpreter discovery")
def test_tox_get_python_executable(mocker):
class envconfig:
basepython = sys.executable
envname = "pyxx"
config = mocker.MagicMock()
config.return_value.option.return_value.discover = []
def get_exe(name):
envconfig.basepython = name
p = tox_get_python_executable(envconfig)
assert p
return str(p)
def assert_version_in_output(exe, version):
out = subprocess.check_output((exe, "-V"), stderr=subprocess.STDOUT)
assert version in out.decode()
p = tox_get_python_executable(envconfig)
assert p == py.path.local(sys.executable)
for major, minor in [(2, 7), (3, 5), (3, 6), (3, 7), (3, 8)]:
name = "python{}.{}".format(major, minor)
if tox.INFO.IS_WIN:
pydir = "python{}{}".format(major, minor)
x = py.path.local(r"c:\{}".format(pydir))
if not x.check():
continue
else:
if not py.path.local.sysfind(name) or subprocess.call((name, "-c", "")):
continue
exe = get_exe(name)
assert_version_in_output(exe, "{}.{}".format(major, minor))
has_py_exe = py.path.local.sysfind("py") is not None
for major in (2, 3):
name = "python{}".format(major)
if has_py_exe:
error_code = subprocess.call(("py", "-{}".format(major), "-c", ""))
if error_code:
continue
elif not py.path.local.sysfind(name):
continue
exe = get_exe(name)
assert_version_in_output(exe, str(major))
@pytest.mark.skipif("sys.platform == 'win32'", reason="symlink execution unreliable on Windows")
def test_find_alias_on_path(monkeypatch, tmp_path, mocker):
reporter.update_default_reporter(Verbosity.DEFAULT, Verbosity.DEBUG)
magic = tmp_path / "magic{}".format(os.path.splitext(sys.executable)[1])
os.symlink(sys.executable, str(magic))
monkeypatch.setenv(
str("PATH"),
os.pathsep.join([str(tmp_path)] + os.environ.get(str("PATH"), "").split(os.pathsep)),
)
class envconfig:
basepython = "magic"
envname = "pyxx"
config = mocker.MagicMock()
config.return_value.option.return_value.discover = []
detected = py.path.local.sysfind("magic")
assert detected
t = tox_get_python_executable(envconfig).lower()
assert t == str(magic).lower()
def test_run_and_get_interpreter_info():
name = os.path.basename(sys.executable)
info = run_and_get_interpreter_info(name, sys.executable)
assert info.version_info == tuple(sys.version_info)
assert info.implementation == platform.python_implementation()
assert info.executable == sys.executable
class TestInterpreters:
def test_get_executable(self, interpreters, mocker):
class envconfig:
basepython = sys.executable
envname = "pyxx"
config = mocker.MagicMock()
config.return_value.option.return_value.discover = []
x = interpreters.get_executable(envconfig)
assert x == sys.executable
info = interpreters.get_info(envconfig)
assert info.version_info == tuple(sys.version_info)
assert info.executable == sys.executable
assert isinstance(info, InterpreterInfo)
def test_get_executable_no_exist(self, interpreters, mocker):
class envconfig:
basepython = "1lkj23"
envname = "pyxx"
config = mocker.MagicMock()
config.return_value.option.return_value.discover = []
assert not interpreters.get_executable(envconfig)
info = interpreters.get_info(envconfig)
assert not info.version_info
assert info.name == "1lkj23"
assert not info.executable
assert isinstance(info, NoInterpreterInfo)
@pytest.mark.skipif("sys.platform == 'win32'", reason="Uses a unix only wrapper")
def test_get_info_uses_hook_path(self, tmp_path):
magic = tmp_path / "magic{}".format(os.path.splitext(sys.executable)[1])
wrapper = (
"#!{executable}\n"
"import subprocess\n"
"import sys\n"
'sys.exit(subprocess.call(["{executable}"] + sys.argv[1:]))\n'
).format(executable=sys.executable)
magic.write_text(wrapper)
magic.chmod(magic.stat().st_mode | stat.S_IEXEC)
class MockHook:
def tox_get_python_executable(self, envconfig):
return str(magic)
class envconfig:
basepython = sys.executable
envname = "magicpy"
# Check that the wrapper is working first.
# If it isn't, the default is to return the passed path anyway.
subprocess.check_call([str(magic), "--help"])
interpreters = Interpreters(hook=MockHook())
info = interpreters.get_info(envconfig)
assert info.executable == str(magic)
def test_get_sitepackagesdir_error(self, interpreters, mocker):
class envconfig:
basepython = sys.executable
envname = "123"
config = mocker.MagicMock()
config.return_value.option.return_value.discover = []
info = interpreters.get_info(envconfig)
s = interpreters.get_sitepackagesdir(info, "")
assert s
def test_exec_failed():
x = ExecFailed("my-executable", "my-source", "my-out", "my-err")
assert isinstance(x, Exception)
assert x.executable == "my-executable"
assert x.source == "my-source"
assert x.out == "my-out"
assert x.err == "my-err"
class TestInterpreterInfo:
@staticmethod
def info(
implementation="CPython",
executable="my-executable",
version_info="my-version-info",
sysplatform="my-sys-platform",
):
return InterpreterInfo(
implementation, executable, version_info, sysplatform, True, "/", None
)
def test_data(self):
x = self.info("larry", "moe", "shemp", "curly")
assert x.implementation == "larry"
assert x.executable == "moe"
assert x.version_info == "shemp"
assert x.sysplatform == "curly"
def test_str(self):
x = self.info(executable="foo", version_info="bar")
assert str(x) == "<executable at foo, version_info bar>"
class TestNoInterpreterInfo:
def test_default_data(self):
x = NoInterpreterInfo("foo")
assert x.name == "foo"
assert x.executable is None
assert x.version_info is None
assert x.out is None
assert x.err == "not found"
def test_set_data(self):
x = NoInterpreterInfo("migraine", executable="my-executable", out="my-out", err="my-err")
assert x.name == "migraine"
assert x.executable == "my-executable"
assert x.version_info is None
assert x.out == "my-out"
assert x.err == "my-err"
def test_str_without_executable(self):
x = NoInterpreterInfo("coconut")
assert str(x) == "<executable not found for: coconut>"
def test_str_with_executable(self):
x = NoInterpreterInfo("coconut", executable="bang/em/together")
assert str(x) == "<executable at bang/em/together, not runnable>"