Skip to content

Commit 53bf191

Browse files
authored
tests: add conda marker (#969)
* tests: add conda marker * Update tests/conftest.py --------- Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
1 parent c6541cd commit 53bf191

4 files changed

Lines changed: 44 additions & 18 deletions

File tree

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ filterwarnings = [ "error" ]
144144
log_cli_level = "INFO"
145145
testpaths = [ "tests" ]
146146
pythonpath = [ ".github/" ]
147+
markers = [
148+
"conda: test requires conda",
149+
]
147150

148151
[tool.coverage]
149152
run.branch = true

tests/conftest.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@
1414
from __future__ import annotations
1515

1616
import re
17+
import shutil
18+
import subprocess
1719
from pathlib import Path
1820
from string import Template
19-
from typing import Callable
21+
from typing import Any, Callable
2022

2123
import pytest
2224

25+
HAS_CONDA = shutil.which("conda") is not None
26+
2327

2428
@pytest.fixture(autouse=True)
2529
def reset_color_envvars(monkeypatch: pytest.MonkeyPatch) -> None:
@@ -58,3 +62,33 @@ def generate_noxfile(**option_mapping: str | bool) -> str:
5862
return str(path)
5963

6064
return generate_noxfile
65+
66+
67+
# This fixture will be automatically used unless the test has the 'conda' marker
68+
@pytest.fixture
69+
def prevent_conda(monkeypatch: pytest.MonkeyPatch) -> None:
70+
def blocked_popen(*args: Any, **kwargs: Any) -> Any:
71+
cmd = args[0][0] if isinstance(args[0], list) else args[0]
72+
msg = "Use of 'conda' command is blocked in tests without @pytest.mark.conda"
73+
if "conda" in cmd or "mamba" in cmd:
74+
raise RuntimeError(msg)
75+
return original_popen(*args, **kwargs)
76+
77+
original_popen = subprocess.Popen
78+
monkeypatch.setattr(subprocess, "Popen", blocked_popen)
79+
80+
81+
def pytest_collection_modifyitems(items: list[pytest.Item]) -> None:
82+
for item in items:
83+
if "make_conda" in getattr(item, "fixturenames", ()):
84+
item.add_marker("conda")
85+
if "conda" in item.keywords:
86+
item.add_marker(
87+
pytest.mark.skipif(not HAS_CONDA, reason="Missing conda command.")
88+
)
89+
90+
91+
# Protection to make sure every conda-using test requests it
92+
def pytest_runtest_setup(item: pytest.Item) -> None:
93+
if not any(mark.name == "conda" for mark in item.iter_markers()):
94+
item.add_marker(pytest.mark.usefixtures("prevent_conda"))

tests/test_sessions.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,6 @@
4040
from nox import _options
4141
from nox.logger import logger
4242

43-
HAS_CONDA = shutil.which("conda") is not None
44-
has_conda = pytest.mark.skipif(not HAS_CONDA, reason="Missing conda command.")
45-
4643
DIR = Path(__file__).parent.resolve()
4744

4845

@@ -1225,7 +1222,7 @@ def test__create_venv(self, create: mock.Mock) -> None:
12251222
"nox.virtualenv.CondaEnv.create",
12261223
"conda",
12271224
nox.virtualenv.CondaEnv,
1228-
marks=has_conda,
1225+
marks=pytest.mark.conda,
12291226
),
12301227
],
12311228
)

tests/test_virtualenv.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,11 @@
3939
from nox.virtualenv import CondaEnv, ProcessEnv, VirtualEnv
4040

4141
IS_WINDOWS = sys.platform.startswith("win")
42-
HAS_CONDA = shutil.which("conda") is not None
4342
HAS_UV = shutil.which("uv") is not None
4443
RAISE_ERROR = "RAISE_ERROR"
4544
VIRTUALENV_VERSION = metadata.version("virtualenv")
4645

4746
has_uv = pytest.mark.skipif(not HAS_UV, reason="Missing uv command.")
48-
has_conda = pytest.mark.skipif(not HAS_CONDA, reason="Missing conda command.")
4947

5048

5149
class TextProcessResult(NamedTuple):
@@ -171,7 +169,6 @@ def test_condaenv_constructor_explicit(
171169
assert venv.reuse_existing is True
172170

173171

174-
@has_conda
175172
def test_condaenv_create(make_conda: Callable[..., tuple[CondaEnv, Path]]) -> None:
176173
venv, dir_ = make_conda()
177174
venv.create()
@@ -200,7 +197,6 @@ def test_condaenv_create(make_conda: Callable[..., tuple[CondaEnv, Path]]) -> No
200197
assert venv._reused
201198

202199

203-
@has_conda
204200
def test_condaenv_create_with_params(
205201
make_conda: Callable[..., tuple[CondaEnv, Path]],
206202
) -> None:
@@ -214,7 +210,6 @@ def test_condaenv_create_with_params(
214210
assert dir_.joinpath("bin", "pip").exists()
215211

216212

217-
@has_conda
218213
def test_condaenv_create_interpreter(
219214
make_conda: Callable[..., tuple[CondaEnv, Path]],
220215
) -> None:
@@ -230,7 +225,6 @@ def test_condaenv_create_interpreter(
230225
assert dir_.joinpath("bin", "python3.12").exists()
231226

232227

233-
@has_conda
234228
def test_conda_env_create_verbose(
235229
make_conda: Callable[..., tuple[CondaEnv, Path]],
236230
) -> None:
@@ -262,13 +256,11 @@ def test_condaenv_bin_windows(make_conda: Callable[..., tuple[CondaEnv, Path]])
262256
] == venv.bin_paths
263257

264258

265-
@has_conda
266259
def test_condaenv_(make_conda: Callable[..., tuple[CondaEnv, Path]]) -> None:
267260
venv, _dir = make_conda()
268261
assert not venv.is_offline()
269262

270263

271-
@has_conda
272264
def test_condaenv_detection(make_conda: Callable[..., tuple[CondaEnv, Path]]) -> None:
273265
venv, dir_ = make_conda()
274266
venv.create()
@@ -556,7 +548,7 @@ def test_not_stale_virtualenv_environment(
556548
assert reused
557549

558550

559-
@has_conda
551+
@pytest.mark.conda
560552
def test_stale_virtualenv_to_conda_environment(
561553
make_one: Callable[..., tuple[VirtualEnv | ProcessEnv, Path]],
562554
) -> None:
@@ -571,7 +563,7 @@ def test_stale_virtualenv_to_conda_environment(
571563
assert not reused
572564

573565

574-
@has_conda
566+
@pytest.mark.conda
575567
def test_reuse_conda_environment(
576568
make_one: Callable[..., tuple[VirtualEnv | ProcessEnv, Path]],
577569
) -> None:
@@ -587,7 +579,7 @@ def test_reuse_conda_environment(
587579

588580

589581
# This mocks micromamba so that it doesn't need to be installed.
590-
@has_conda
582+
@pytest.mark.conda
591583
def test_micromamba_environment(
592584
make_one: Callable[..., tuple[VirtualEnv | ProcessEnv, Path]],
593585
monkeypatch: pytest.MonkeyPatch,
@@ -612,7 +604,7 @@ def test_micromamba_environment(
612604
"params",
613605
[["--channel=default"], ["-cdefault"], ["-c", "default"], ["--channel", "default"]],
614606
)
615-
@has_conda
607+
@pytest.mark.conda
616608
def test_micromamba_channel_environment(
617609
make_one: Callable[..., tuple[VirtualEnv, Path]],
618610
monkeypatch: pytest.MonkeyPatch,
@@ -643,7 +635,7 @@ def test_micromamba_channel_environment(
643635
("venv", "virtualenv", True),
644636
("virtualenv", "uv", True),
645637
pytest.param("uv", "virtualenv", False, marks=has_uv),
646-
pytest.param("conda", "virtualenv", False, marks=has_conda),
638+
pytest.param("conda", "virtualenv", False, marks=pytest.mark.conda),
647639
],
648640
)
649641
def test_stale_environment(

0 commit comments

Comments
 (0)