diff --git a/nox/_options.py b/nox/_options.py index 1096c4ef..a198b9ac 100644 --- a/nox/_options.py +++ b/nox/_options.py @@ -26,6 +26,7 @@ from nox import _option_set from nox.tasks import discover_manifest, filter_manifest, load_nox_module +from nox.virtualenv import ALL_VENVS if sys.version_info < (3, 8): from typing_extensions import Literal @@ -423,10 +424,9 @@ def _tag_completer( merge_func=_default_venv_backend_merge_func, help=( "Virtual environment backend to use by default for Nox sessions, this is" - " ``'virtualenv'`` by default but any of ``('uv, 'virtualenv'," - " 'conda', 'mamba', 'venv')`` are accepted." + " ``'virtualenv'`` by default but any of ``{list(ALL_VENVS)!r}`` are accepted." ), - choices=["none", "virtualenv", "conda", "mamba", "venv", "uv"], + choices=list(ALL_VENVS), ), _option_set.Option( "force_venv_backend", @@ -438,10 +438,9 @@ def _tag_completer( help=( "Virtual environment backend to force-use for all Nox sessions in this run," " overriding any other venv backend declared in the Noxfile and ignoring" - " the default backend. Any of ``('uv', 'virtualenv', 'conda', 'mamba'," - " 'venv')`` are accepted." + " the default backend. Any of ``{list(ALL_VENVS)!r}`` are accepted." ), - choices=["none", "virtualenv", "conda", "mamba", "venv", "uv"], + choices=list(ALL_VENVS), ), _option_set.Option( "no_venv", diff --git a/nox/sessions.py b/nox/sessions.py index a98916f4..44c7b446 100644 --- a/nox/sessions.py +++ b/nox/sessions.py @@ -38,6 +38,7 @@ ) import nox.command +import nox.virtualenv from nox._decorators import Func from nox.logger import logger from nox.virtualenv import CondaEnv, PassthroughEnv, ProcessEnv, VirtualEnv @@ -757,39 +758,29 @@ def envdir(self) -> str: return _normalize_path(self.global_config.envdir, self.friendly_name) def _create_venv(self) -> None: + reuse_existing = self.reuse_existing_venv() + backend = ( self.global_config.force_venv_backend or self.func.venv_backend or self.global_config.default_venv_backend + or "virtualenv" ) + if backend not in nox.virtualenv.ALL_VENVS: + msg = f"Expected venv_backend one of {list(nox.virtualenv.ALL_VENVS)!r}, but got {backend!r}." + raise ValueError(msg) + if backend == "none" or self.func.python is False: - self.venv = PassthroughEnv() + self.venv = nox.virtualenv.ALL_VENVS["none"]() return - reuse_existing = self.reuse_existing_venv() - - if backend is None or backend in {"virtualenv", "venv", "uv"}: - self.venv = VirtualEnv( - self.envdir, - interpreter=self.func.python, # type: ignore[arg-type] - reuse_existing=reuse_existing, - venv_backend=backend or "virtualenv", - venv_params=self.func.venv_params, - ) - elif backend in {"conda", "mamba"}: - self.venv = CondaEnv( - self.envdir, - interpreter=self.func.python, # type: ignore[arg-type] - reuse_existing=reuse_existing, - venv_params=self.func.venv_params, - conda_cmd=backend, - ) - else: - raise ValueError( - "Expected venv_backend one of ('virtualenv', 'conda', 'mamba'," - f" 'venv'), but got '{backend}'." - ) + self.venv = nox.virtualenv.ALL_VENVS[backend]( + self.envdir, + interpreter=self.func.python, + reuse_existing=reuse_existing, + venv_params=self.func.venv_params, + ) self.venv.create() diff --git a/nox/virtualenv.py b/nox/virtualenv.py index 1895f841..84844e59 100644 --- a/nox/virtualenv.py +++ b/nox/virtualenv.py @@ -15,13 +15,14 @@ from __future__ import annotations import contextlib +import functools import os import platform import re import shutil import subprocess import sys -from collections.abc import Mapping +from collections.abc import Callable, Mapping from socket import gethostbyname from typing import Any, ClassVar @@ -532,3 +533,13 @@ def create(self) -> bool: nox.command.run(cmd, silent=True, log=nox.options.verbose or False) return True + + +ALL_VENVS: dict[str, Callable[..., ProcessEnv]] = { + "conda": functools.partial(CondaEnv, conda_cmd="conda"), + "mamba": functools.partial(CondaEnv, conda_cmd="mamba"), + "virtualenv": functools.partial(VirtualEnv, venv_backend="virtualenv"), + "venv": functools.partial(VirtualEnv, venv_backend="venv"), + "uv": functools.partial(VirtualEnv, venv_backend="uv"), + "none": PassthroughEnv, +}