Skip to content

Commit 4558fee

Browse files
authored
Fix legacy flags, deprecate them and remove --index-url (#2731)
Resolves #2690
1 parent 3641a47 commit 4558fee

File tree

7 files changed

+84
-23
lines changed

7 files changed

+84
-23
lines changed

docs/changelog/2690.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix legacy CLI flags ``--pre``, ``--force-deps``, ``--sitepackages`` and ``--alwayscopy`` not working, and mark them
2+
as deprecated - by :user:`gaborbernat`.

docs/faq.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ tox 4 - CLI arguments changed
107107
no longer the case.
108108
- Running ``--showconfig```or ``--help-ini`` with the ``-v`` flag will add interleaved debugging information, whereas
109109
tox 3 added additional lines at the start. If you want to generate valid ini files you must not use the ``-v`` flag.
110+
- The ``--index-url`` is now removed, use ``PIP_INDEX_URL`` in :ref:`set_env` instead.
110111

111112
tox 4 - packaging changes
112113
+++++++++++++++++++++++++

src/tox/session/cmd/legacy.py

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
from __future__ import annotations
22

33
from pathlib import Path
4+
from typing import cast
45

5-
from tox.config.cli.parser import DEFAULT_VERBOSITY, ToxParser
6+
from packaging.requirements import InvalidRequirement, Requirement
7+
8+
from tox.config.cli.parser import DEFAULT_VERBOSITY, Parsed, ToxParser
9+
from tox.config.loader.memory import MemoryLoader
10+
from tox.config.set_env import SetEnv
611
from tox.plugin import impl
712
from tox.session.cmd.run.common import env_run_create_flags
813
from tox.session.cmd.run.parallel import OFF_VALUE, parallel_flags, run_parallel
914
from tox.session.cmd.run.sequential import run_sequential
1015
from tox.session.state import State
16+
from tox.tox_env.python.pip.req_file import PythonDeps
1117

12-
from ..env_select import CliEnv, register_env_select_flags
18+
from ..env_select import CliEnv, EnvSelector, register_env_select_flags
1319
from .devenv import devenv
1420
from .list_env import list_env
1521
from .show_config import show_config
@@ -54,16 +60,8 @@ def tox_add_option(parser: ToxParser) -> None:
5460
our.add_argument(
5561
"--pre",
5662
action="store_true",
57-
help="install pre-releases and development versions of dependencies. This will pass the --pre option to"
58-
"install_command (pip by default).",
59-
)
60-
our.add_argument(
61-
"-i",
62-
"--index-url",
63-
action="append",
64-
default=[],
65-
metavar="url",
66-
help="set indexserver url (if URL is of form name=url set the url for the 'name' indexserver, specifically)",
63+
help="deprecated use PIP_PRE in set_env instead - install pre-releases and development versions of"
64+
"dependencies; this will set PIP_PRE=1 environment variable",
6765
)
6866
our.add_argument(
6967
"--force-dep",
@@ -72,17 +70,18 @@ def tox_add_option(parser: ToxParser) -> None:
7270
default=[],
7371
help="Forces a certain version of one of the dependencies when configuring the virtual environment. REQ "
7472
"Examples 'pytest<6.1' or 'django>=2.2'.",
73+
type=Requirement,
7574
)
7675
our.add_argument(
7776
"--sitepackages",
7877
action="store_true",
79-
help="override sitepackages setting to True in all envs",
78+
help="deprecated use VIRTUALENV_SYSTEM_SITE_PACKAGES=1, override sitepackages setting to True in all envs",
8079
dest="site_packages",
8180
)
8281
our.add_argument(
8382
"--alwayscopy",
8483
action="store_true",
85-
help="override always copy setting to True in all envs",
84+
help="deprecated use VIRTUALENV_ALWAYS_COPY=1, override always copy setting to True in all envs",
8685
dest="always_copy",
8786
)
8887

@@ -92,16 +91,53 @@ def legacy(state: State) -> int:
9291
if option.show_config:
9392
option.list_keys_only = []
9493
option.show_core = not bool(option.env)
95-
return show_config(state)
9694
if option.list_envs or option.list_envs_all:
9795
state.envs.on_empty_fallback_py = False
9896
option.list_no_description = option.verbosity <= DEFAULT_VERBOSITY
9997
option.list_default_only = not option.list_envs_all
10098
option.show_core = False
99+
100+
_handle_legacy_only_flags(option, state.envs)
101+
102+
if option.show_config:
103+
return show_config(state)
104+
if option.list_envs or option.list_envs_all:
101105
return list_env(state)
102106
if option.devenv_path:
103107
option.devenv_path = Path(option.devenv_path)
104108
return devenv(state)
105109
if option.parallel != 0: # only 0 means sequential
106110
return run_parallel(state)
107111
return run_sequential(state)
112+
113+
114+
def _handle_legacy_only_flags(option: Parsed, envs: EnvSelector) -> None:
115+
override = {}
116+
if getattr(option, "site_packages", False):
117+
override["system_site_packages"] = True
118+
if getattr(option, "always_copy", False):
119+
override["always_copy"] = True
120+
set_env = {}
121+
if getattr(option, "pre", False):
122+
set_env["PIP_PRE"] = "1"
123+
forced = {j.name: j for j in getattr(option, "force_dep", [])}
124+
if override or set_env or forced:
125+
for env in envs.iter(only_active=True, package=False):
126+
env_conf = envs[env].conf
127+
if override:
128+
env_conf.loaders.insert(0, MemoryLoader(**override))
129+
if set_env:
130+
cast(SetEnv, env_conf["set_env"]).update(set_env, override=True)
131+
if forced:
132+
to_force = forced.copy()
133+
deps = cast(PythonDeps, env_conf["deps"])
134+
as_root_args = deps.as_root_args
135+
for at, entry in enumerate(as_root_args):
136+
try:
137+
req = Requirement(entry)
138+
except InvalidRequirement:
139+
continue
140+
if req.name in to_force:
141+
as_root_args[at] = str(to_force[req.name])
142+
del to_force[req.name]
143+
as_root_args.extend(str(v) for v in to_force.values())

src/tox/tox_env/python/virtual_env/api.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,9 @@ def virtualenv_env_vars(self) -> dict[str, str]:
112112
base_python: list[str] = self.conf["base_python"]
113113
if "VIRTUALENV_NO_PERIODIC_UPDATE" not in env:
114114
env["VIRTUALENV_NO_PERIODIC_UPDATE"] = "True"
115-
site = getattr(self.options, "site_packages", False) or self.conf["system_site_packages"]
116115
env["VIRTUALENV_CLEAR"] = "False"
117-
env["VIRTUALENV_SYSTEM_SITE_PACKAGES"] = str(site)
118-
env["VIRTUALENV_COPIES"] = str(getattr(self.options, "always_copy", False) or self.conf["always_copy"])
116+
env["VIRTUALENV_SYSTEM_SITE_PACKAGES"] = str(self.conf["system_site_packages"])
117+
env["VIRTUALENV_COPIES"] = str(self.conf["always_copy"])
119118
env["VIRTUALENV_DOWNLOAD"] = str(self.conf["download"])
120119
env["VIRTUALENV_PYTHON"] = "\n".join(base_python)
121120
if hasattr(self.options, "discover"):

tests/config/cli/test_cli_env_var.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ def test_verbose_no_test() -> None:
5959
"parallel_live": False,
6060
"parallel_no_spinner": False,
6161
"pre": False,
62-
"index_url": [],
6362
"factors": [],
6463
"labels": [],
6564
"skip_env": "",
@@ -93,7 +92,6 @@ def test_env_var_exhaustive_parallel_values(
9392
"env": CliEnv(["py37", "py36"]),
9493
"force_dep": [],
9594
"hash_seed": "noset",
96-
"index_url": [],
9795
"install_pkg": None,
9896
"no_provision": False,
9997
"list_envs": False,

tests/session/cmd/test_legacy.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,17 @@ def test_legacy_list_env_with_empty_or_missing_env_list(tox_project: ToxProjectC
5353
outcome = tox_project({"tox.ini": configuration}).run("le", "-l")
5454

5555
outcome.assert_success()
56-
outcome.assert_out_err("", "")
56+
assert not outcome.err
57+
assert not outcome.out
5758

5859

5960
def test_legacy_list_env_with_no_tox_file(tox_project: ToxProjectCreator) -> None:
6061
project = tox_project({})
6162
outcome = project.run("le", "-l")
62-
6363
outcome.assert_success()
6464
out = f"ROOT: No tox.ini or setup.cfg or pyproject.toml found, assuming empty tox.ini at {project.path}\n"
65-
outcome.assert_out_err(out, "")
65+
assert not outcome.err
66+
assert outcome.out == out
6667

6768

6869
@pytest.mark.parametrize("verbose", range(3))
@@ -106,3 +107,26 @@ def test_legacy_run_sequential(tox_project: ToxProjectCreator, mocker: MockerFix
106107
def test_legacy_help(tox_project: ToxProjectCreator) -> None:
107108
outcome = tox_project({"tox.ini": ""}).run("le", "-h")
108109
outcome.assert_success()
110+
111+
112+
def test_legacy_cli_flags(tox_project: ToxProjectCreator, mocker: MockerFixture) -> None:
113+
session = mocker.MagicMock()
114+
session.creator.interpreter.system_executable = "I"
115+
virtualenv_session = mocker.patch("tox.tox_env.python.virtual_env.api.session_via_cli", return_value=session)
116+
ini = "[testenv]\ndeps = p>6\n c\n -rr.txt\npackage=skip\nset_env = PIP_PRE = 0"
117+
proj = tox_project({"tox.ini": ini})
118+
(proj.path / "r.txt").write_text("d")
119+
execute_calls = proj.patch_execute(lambda r: 0 if "install" in r.run_id else None)
120+
args = ["--pre", "--force-dep", "p<1", "--force-dep", "b>2", "--sitepackages", "--alwayscopy"]
121+
result = proj.run("le", "-e", "py", *args)
122+
result.assert_success()
123+
124+
calls = [(i[0][0].conf.name, i[0][3].run_id, i[0][3].cmd[5:]) for i in execute_calls.call_args_list]
125+
assert calls[0] == ("py", "install_deps", ["c", "p<1", "-r", "r.txt", "b>2"])
126+
for call in execute_calls.call_args_list:
127+
if call[0][0].name == "py":
128+
assert call[0][3].env["PIP_PRE"] == "1"
129+
assert len(virtualenv_session.call_args_list) == 1
130+
v_env = virtualenv_session.call_args_list[0][1]["env"]
131+
assert v_env["VIRTUALENV_SYSTEM_SITE_PACKAGES"] == "True"
132+
assert v_env["VIRTUALENV_COPIES"] == "True"

tests/util/test_spinner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def test_spinner_atty(capfd: CaptureFixture, monkeypatch: MonkeyPatch) -> None:
7272

7373

7474
@time_machine.travel("2012-01-14", tick=False)
75+
@pytest.mark.flaky(max_runs=3, min_passes=1)
7576
def test_spinner_report(capfd: CaptureFixture, monkeypatch: MonkeyPatch) -> None:
7677
monkeypatch.setattr(sys.stdout, "isatty", lambda: False)
7778
with spinner.Spinner(refresh_rate=100) as spin:

0 commit comments

Comments
 (0)