From cd35053b8d9f8226bfd0cedce296b06d55b84438 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Wed, 6 Mar 2024 11:45:30 -0500 Subject: [PATCH 1/7] feat: add venv_backend property Signed-off-by: Henry Schreiner --- nox/sessions.py | 8 ++++++++ nox/virtualenv.py | 21 ++++++++++++++++++++- tests/test_sessions.py | 2 ++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/nox/sessions.py b/nox/sessions.py index 2728b1fa..83c3fdb4 100644 --- a/nox/sessions.py +++ b/nox/sessions.py @@ -193,6 +193,14 @@ def virtualenv(self) -> ProcessEnv: raise ValueError("A virtualenv has not been created for this session") return venv + @property + def venv_backend(self) -> str: + """The venv_backend selected.""" + venv = self._runner.venv + if venv is None: + return "none" + return venv.venv_backend + @property def python(self) -> str | Sequence[str] | bool | None: """The python version passed into ``@nox.session``.""" diff --git a/nox/virtualenv.py b/nox/virtualenv.py index 6f4b6099..2258a04b 100644 --- a/nox/virtualenv.py +++ b/nox/virtualenv.py @@ -93,6 +93,13 @@ def create(self) -> bool: Returns True if the environment is new, and False if it was reused. """ + @property + @abc.abstractmethod + def venv_backend(self) -> str: + """ + Returns the string used to select this environment. + """ + def locate_via_py(version: str) -> str | None: """Find the Python executable using the Windows Launcher. @@ -180,6 +187,10 @@ def create(self) -> bool: False since it's always reused.""" return False + @property + def venv_backend(self) -> str: + return "none" + class CondaEnv(ProcessEnv): """Conda environment management class. @@ -303,6 +314,10 @@ def is_offline() -> bool: except BaseException: # pragma: no cover return True + @property + def venv_backend(self) -> str: + return self.conda_cmd + class VirtualEnv(ProcessEnv): """Virtualenv management class. @@ -341,7 +356,7 @@ def __init__( self.interpreter = interpreter self._resolved: None | str | InterpreterNotFound = None self.reuse_existing = reuse_existing - self.venv_backend = venv_backend + self._venv_backend = venv_backend self.venv_params = venv_params or [] if venv_backend not in {"virtualenv", "venv", "uv"}: msg = f"venv_backend {venv_backend} not recognized" @@ -544,6 +559,10 @@ def create(self) -> bool: return True + @property + def venv_backend(self) -> str: + return self._venv_backend + ALL_VENVS: dict[str, Callable[..., ProcessEnv]] = { "conda": functools.partial(CondaEnv, conda_cmd="conda"), diff --git a/tests/test_sessions.py b/tests/test_sessions.py index 98478a60..aa71226c 100644 --- a/tests/test_sessions.py +++ b/tests/test_sessions.py @@ -641,6 +641,8 @@ class SessionNoSlots(nox.sessions.Session): session = SessionNoSlots(runner=runner) + assert session.venv_backend == "venv" + with mock.patch.object(session, "_run", autospec=True) as run: session.install("requests", "urllib3") run.assert_called_once_with( From 23e55d0085d04051e72c707f198b8bbd68df0145 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Wed, 6 Mar 2024 17:31:56 -0500 Subject: [PATCH 2/7] tests: add a couple of checks Signed-off-by: Henry Schreiner --- tests/test_sessions.py | 2 ++ tests/test_virtualenv.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/test_sessions.py b/tests/test_sessions.py index aa71226c..4d162f24 100644 --- a/tests/test_sessions.py +++ b/tests/test_sessions.py @@ -135,6 +135,8 @@ def test_virtualenv_as_none(self): with pytest.raises(ValueError, match="virtualenv"): _ = session.virtualenv + assert session.venv_backend == "none" + def test_interactive(self): session, runner = self.make_session_and_runner() diff --git a/tests/test_virtualenv.py b/tests/test_virtualenv.py index 6af872bc..57585089 100644 --- a/tests/test_virtualenv.py +++ b/tests/test_virtualenv.py @@ -490,9 +490,11 @@ def test_stale_environment(make_one, frm, to, result, monkeypatch): monkeypatch.setenv("NOX_ENABLE_STALENESS_CHECK", "1") venv, _ = make_one(reuse_existing=True, venv_backend=frm) venv.create() + assert venv.venv_backend == frm venv, _ = make_one(reuse_existing=True, venv_backend=to) reused = venv._check_reused_environment_type() + assert venv.venv_backend == to assert reused == result From 2e0627100dc3bcf93ef13c557a4d8296984350a4 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Wed, 6 Mar 2024 22:16:29 -0500 Subject: [PATCH 3/7] docs: update for venv_backend Signed-off-by: Henry Schreiner --- docs/config.rst | 10 +++++++++- docs/usage.rst | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/config.rst b/docs/config.rst index 74bb7836..53ab6931 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -167,7 +167,7 @@ You can also specify that the virtualenv should *always* be reused instead of re def tests(session): pass -You are not limited to virtualenv, there is a selection of backends you can choose from as venv, conda, mamba, or virtualenv (default): +You are not limited to virtualenv, there is a selection of backends you can choose from as venv, uv, conda, mamba, or virtualenv (default): .. code-block:: python @@ -175,6 +175,11 @@ You are not limited to virtualenv, there is a selection of backends you can choo def tests(session): pass +You can chain together optional backends with ``|``, such as ``uv|virtualenv`` +or ``mamba|conda``, and the first available backend will be selected. You +cannot put anything after a backend that can't be missing like ``venv`` or +``virtualenv``. + Finally, custom backend parameters are supported: .. code-block:: python @@ -183,6 +188,9 @@ Finally, custom backend parameters are supported: def tests(session): pass +If you need to check to see which backend was selected, you can access it via +``session.venv_backend``. + Passing arguments into sessions ------------------------------- diff --git a/docs/usage.rst b/docs/usage.rst index e32808da..f29dc42f 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -165,6 +165,9 @@ Note that using this option does not change the backend for sessions where ``ven Backends that could be missing (``uv``, ``conda``, and ``mamba``) can have a fallback using ``|``, such as ``uv|virtualenv`` or ``mamba|conda``. This will use the first item that is available on the users system. +If you need to check to see which backend was selected, you can access it via +``session.venv_backend`` in your noxfile. + .. _opt-force-venv-backend: Forcing the sessions backend From 4379325da1d3f9b1483cdfc59f42d7107c816939 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Wed, 6 Mar 2024 22:59:53 -0500 Subject: [PATCH 4/7] tests: add one more test for coverage Signed-off-by: Henry Schreiner --- tests/test_virtualenv.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/test_virtualenv.py b/tests/test_virtualenv.py index 57585089..26bf9af2 100644 --- a/tests/test_virtualenv.py +++ b/tests/test_virtualenv.py @@ -14,6 +14,7 @@ from __future__ import annotations +import functools import os import re import shutil @@ -52,14 +53,11 @@ class TextProcessResult(NamedTuple): def make_one(tmpdir): def factory(*args, venv_backend: str = "virtualenv", **kwargs): location = tmpdir.join("venv") - if venv_backend in {"mamba", "conda"}: - venv = nox.virtualenv.CondaEnv( - location.strpath, *args, conda_cmd=venv_backend, **kwargs - ) - else: - venv = nox.virtualenv.VirtualEnv( - location.strpath, *args, venv_backend=venv_backend, **kwargs - ) + try: + venv_fn = nox.virtualenv.ALL_VENVS[venv_backend] + except KeyError: + venv_fn = functools.partial(nox.virtualenv.VirtualEnv, venv_backend=venv_backend) + venv = venv_fn(location.strpath, *args, **kwargs) return (venv, location) return factory @@ -499,6 +497,12 @@ def test_stale_environment(make_one, frm, to, result, monkeypatch): assert reused == result +def test_passthrough_environment_venv_backend(): + venv, _ = make_one(reuse_existing=True, venv_backend="none") + venv.create() + assert venv.venv_backend == "none" + + @has_uv def test_create_reuse_stale_virtualenv_environment(make_one, monkeypatch): monkeypatch.setenv("NOX_ENABLE_STALENESS_CHECK", "1") From f1b5a758e083b2c769e94368bd12d9bd65a66b0d Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Wed, 6 Mar 2024 23:30:09 -0500 Subject: [PATCH 5/7] Update test_virtualenv.py --- tests/test_virtualenv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_virtualenv.py b/tests/test_virtualenv.py index 26bf9af2..31ab8bd9 100644 --- a/tests/test_virtualenv.py +++ b/tests/test_virtualenv.py @@ -497,7 +497,7 @@ def test_stale_environment(make_one, frm, to, result, monkeypatch): assert reused == result -def test_passthrough_environment_venv_backend(): +def test_passthrough_environment_venv_backend(make_one): venv, _ = make_one(reuse_existing=True, venv_backend="none") venv.create() assert venv.venv_backend == "none" From 51b3ca776fc39ae11f752e398a728160dc72eb1e Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 7 Mar 2024 00:42:38 -0500 Subject: [PATCH 6/7] Update test_virtualenv.py --- tests/test_virtualenv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_virtualenv.py b/tests/test_virtualenv.py index 31ab8bd9..c2e86011 100644 --- a/tests/test_virtualenv.py +++ b/tests/test_virtualenv.py @@ -498,7 +498,7 @@ def test_stale_environment(make_one, frm, to, result, monkeypatch): def test_passthrough_environment_venv_backend(make_one): - venv, _ = make_one(reuse_existing=True, venv_backend="none") + venv, _ = make_one(venv_backend="none") venv.create() assert venv.venv_backend == "none" From 58fae2ffca9a6bb91b2ddfbcb4113f769e820dd2 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 7 Mar 2024 01:10:21 -0500 Subject: [PATCH 7/7] Update test_virtualenv.py --- tests/test_virtualenv.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_virtualenv.py b/tests/test_virtualenv.py index c2e86011..69dd6c5c 100644 --- a/tests/test_virtualenv.py +++ b/tests/test_virtualenv.py @@ -56,7 +56,9 @@ def factory(*args, venv_backend: str = "virtualenv", **kwargs): try: venv_fn = nox.virtualenv.ALL_VENVS[venv_backend] except KeyError: - venv_fn = functools.partial(nox.virtualenv.VirtualEnv, venv_backend=venv_backend) + venv_fn = functools.partial( + nox.virtualenv.VirtualEnv, venv_backend=venv_backend + ) venv = venv_fn(location.strpath, *args, **kwargs) return (venv, location)