Skip to content

Commit e2ae954

Browse files
authored
fix non-canonical names don't work with provisioning (#1360)
1 parent 2d2b2ae commit e2ae954

File tree

6 files changed

+109
-10
lines changed

6 files changed

+109
-10
lines changed

.pre-commit-config.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@ repos:
66
args: [--safe]
77
language_version: python3.7
88
- repo: https://github.com/asottile/blacken-docs
9-
rev: v0.5.0
9+
rev: v1.1.0
1010
hooks:
1111
- id: blacken-docs
1212
additional_dependencies: [black==19.3b0]
1313
language_version: python3.7
1414
- repo: https://github.com/asottile/seed-isort-config
15-
rev: v1.9.0
15+
rev: v1.9.1
1616
hooks:
1717
- id: seed-isort-config
1818
args: [--application-directories, "src:."]
1919
- repo: https://github.com/pre-commit/mirrors-isort
20-
rev: v4.3.20
20+
rev: v4.3.21
2121
hooks:
2222
- id: isort
2323
- repo: https://github.com/pre-commit/pre-commit-hooks
@@ -31,7 +31,7 @@ repos:
3131
additional_dependencies: ["flake8-bugbear == 19.3.0"]
3232
language_version: python3.7
3333
- repo: https://github.com/asottile/pyupgrade
34-
rev: v1.17.1
34+
rev: v1.19.0
3535
hooks:
3636
- id: pyupgrade
3737
- repo: https://github.com/pre-commit/pygrep-hooks

docs/changelog/1359.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
non canonical names within :conf:`requires` cause infinite provisioning loop - by :user:`gaborbernat`

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ console_scripts =
5959
testing =
6060
freezegun >= 0.3.11, <1
6161
pathlib2 >= 2.3.3, <3
62-
pytest >= 3.0.0, <5
62+
pytest >= 4.0.0, <6
6363
pytest-cov >= 2.5.1, <3
6464
pytest-mock >= 1.10.0, <2
6565
pytest-xdist >= 1.22.2, <2

src/tox/config/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,7 @@ def line_of_default_to_zero(section, name=None):
10271027
config.setupdir = reader.getpath("setupdir", "{toxinidir}")
10281028
config.logdir = config.toxworkdir.join("log")
10291029
within_parallel = PARALLEL_ENV_VAR_KEY in os.environ
1030-
if not within_parallel:
1030+
if not within_parallel and not WITHIN_PROVISION:
10311031
ensure_empty_dir(config.logdir)
10321032

10331033
# determine indexserver dictionary
@@ -1165,7 +1165,7 @@ def ensure_requires_satisfied(config, requires, min_version):
11651165
if package_name not in exists:
11661166
deps.append(DepConfig(require, None))
11671167
exists.add(package_name)
1168-
dist = importlib_metadata.distribution(package_name)
1168+
dist = importlib_metadata.distribution(package.name)
11691169
if not package.specifier.contains(dist.version, prereleases=True):
11701170
raise MissingDependency(package)
11711171
except requirements.InvalidRequirement as exception:
@@ -1176,6 +1176,9 @@ def ensure_requires_satisfied(config, requires, min_version):
11761176
missing_requirements.append(str(requirements.Requirement(require)))
11771177
if failed_to_parse:
11781178
raise tox.exception.BadRequirement()
1179+
if WITHIN_PROVISION and missing_requirements:
1180+
msg = "break infinite loop provisioning within {} missing {}"
1181+
raise tox.exception.Error(msg.format(sys.executable, missing_requirements))
11791182
config.run_provision = bool(len(missing_requirements))
11801183
return deps
11811184

tests/unit/session/test_provision.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1+
from __future__ import absolute_import, unicode_literals
2+
3+
import os
14
import shutil
25
import subprocess
36
import sys
47

58
import py
69
import pytest
10+
from pathlib2 import Path
11+
from six.moves.urllib.parse import urljoin
12+
from six.moves.urllib.request import pathname2url
713

814
from tox.exception import BadRequirement, MissingRequirement
915

@@ -141,3 +147,92 @@ def test_provision_cli_args_not_ignored_if_provision_false(cmd, initproj):
141147
initproj("test-0.1", {"tox.ini": "[tox]"})
142148
result = cmd("-a", "--option", "b")
143149
result.assert_fail(is_run_test_env=False)
150+
151+
152+
@pytest.fixture(scope="session")
153+
def wheel(tmp_path_factory):
154+
"""create a wheel for a project"""
155+
state = {"at": 0}
156+
157+
def _wheel(path):
158+
state["at"] += 1
159+
dest_path = tmp_path_factory.mktemp("wheel-{}-".format(state["at"]))
160+
env = os.environ.copy()
161+
try:
162+
subprocess.check_output(
163+
[
164+
sys.executable,
165+
"-m",
166+
"pip",
167+
"wheel",
168+
"-w",
169+
str(dest_path),
170+
"--no-deps",
171+
str(path),
172+
],
173+
universal_newlines=True,
174+
stderr=subprocess.STDOUT,
175+
env=env,
176+
)
177+
except subprocess.CalledProcessError as exception:
178+
assert not exception.returncode, exception.output
179+
180+
wheels = list(dest_path.glob("*.whl"))
181+
assert len(wheels) == 1
182+
wheel = wheels[0]
183+
return wheel
184+
185+
return _wheel
186+
187+
188+
THIS_PROJECT_ROOT = Path(__file__).resolve().parents[3]
189+
190+
191+
@pytest.fixture(scope="session")
192+
def tox_wheel(wheel):
193+
return wheel(THIS_PROJECT_ROOT)
194+
195+
196+
@pytest.fixture(scope="session")
197+
def magic_non_canonical_wheel(wheel, tmp_path_factory):
198+
magic_proj = tmp_path_factory.mktemp("magic")
199+
(magic_proj / "setup.py").write_text(
200+
"from setuptools import setup\nsetup(name='com.magic.this-is-fun')"
201+
)
202+
return wheel(magic_proj)
203+
204+
205+
def test_provision_non_canonical_dep(
206+
cmd, initproj, monkeypatch, tox_wheel, magic_non_canonical_wheel
207+
):
208+
initproj(
209+
"w-0.1",
210+
{
211+
"tox.ini": """
212+
[tox]
213+
envlist = py
214+
requires =
215+
com.magic.this-is-fun
216+
tox == {}
217+
[testenv:.tox]
218+
passenv = *
219+
""".format(
220+
tox_wheel.name.split("-")[1]
221+
)
222+
},
223+
)
224+
find_links = " ".join(
225+
space_path2url(d) for d in (tox_wheel.parent, magic_non_canonical_wheel.parent)
226+
)
227+
228+
monkeypatch.setenv(str("PIP_FIND_LINKS"), str(find_links))
229+
230+
result = cmd("-a", "-v", "-v")
231+
result.assert_success(is_run_test_env=False)
232+
233+
234+
def space_path2url(path):
235+
at_path = str(path)
236+
if " " not in at_path:
237+
return at_path
238+
return urljoin("file:", pathname2url(os.path.abspath(at_path)))

tox.ini

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,9 @@ exclude_lines =
117117
118118
[coverage:paths]
119119
source = src/tox
120-
.tox/*/lib/python*/site-packages/tox
121-
.tox/pypy*/site-packages/tox
122-
.tox\*\Lib\site-packages\tox
120+
*/.tox/*/lib/python*/site-packages/tox
121+
*/.tox/pypy*/site-packages/tox
122+
*/.tox\*\Lib\site-packages\tox
123123
*/src/tox
124124
*\src\tox
125125

0 commit comments

Comments
 (0)