diff --git a/backend/tests/downstream/requirements.txt b/backend/tests/downstream/requirements.txt index b7f56dcc6..ef2b32b0a 100644 --- a/backend/tests/downstream/requirements.txt +++ b/backend/tests/downstream/requirements.txt @@ -2,4 +2,4 @@ build packaging requests tomli -virtualenv>=20.13.1 +virtualenv>=21 diff --git a/pyproject.toml b/pyproject.toml index 709b7879d..f17149154 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,7 @@ dependencies = [ "keyring>=23.5.0", "packaging>=24.2", "pexpect~=4.8", + "python-discovery>=1.1", "platformdirs>=2.5.0", "pyproject-hooks", "rich>=11.2.0", @@ -54,7 +55,7 @@ dependencies = [ "tomlkit>=0.11.1", "userpath~=1.7", "uv>=0.5.23", - "virtualenv>=20.26.6", + "virtualenv>=21", "backports.zstd>=1.0.0 ; python_version<'3.14'", ] dynamic = ["version"] diff --git a/src/hatch/env/virtual.py b/src/hatch/env/virtual.py index 646554fe1..7a9b7e653 100644 --- a/src/hatch/env/virtual.py +++ b/src/hatch/env/virtual.py @@ -22,7 +22,7 @@ from collections.abc import Callable, Iterable from packaging.specifiers import SpecifierSet - from virtualenv.discovery.py_info import PythonInfo + from python_discovery import PythonInfo from hatch.dep.core import Dependency from hatch.dep.sync import InstalledDistributions @@ -334,7 +334,7 @@ def upgrade_possible_internal_python(self, python_path: str) -> None: def _interpreter_is_compatible(self, interpreter: PythonInfo) -> bool: return ( - interpreter.executable + interpreter.executable is not None and self._is_stable_path(interpreter.executable) and (self.skip_install or self._python_constraint.contains(interpreter.version_str)) ) @@ -376,26 +376,12 @@ def _resolve_internal_interpreter_path(self, python_version: str) -> str | None: return None def _find_existing_interpreter(self, python_version: str = "") -> str | None: - from virtualenv.discovery import builtin as virtualenv_discovery + import python_discovery - propose_interpreters = virtualenv_discovery.propose_interpreters - - def _patched_propose_interpreters(*args, **kwargs): - for interpreter, impl_must_match in propose_interpreters(*args, **kwargs): - if not self._interpreter_is_compatible(interpreter): - continue - - yield interpreter, impl_must_match - - virtualenv_discovery.propose_interpreters = _patched_propose_interpreters - try: - python_info = virtualenv_discovery.get_interpreter( - python_version, (), env=self.get_interpreter_resolver_env() - ) - if python_info is not None: - return python_info.executable - finally: - virtualenv_discovery.propose_interpreters = propose_interpreters + python_info = python_discovery.get_interpreter( + python_version, (), env=self.get_interpreter_resolver_env(), predicate=self._interpreter_is_compatible + ) + return None if python_info is None else python_info.executable def _get_available_distribution(self, python_version: str = "") -> str | None: from hatch.python.resolve import get_compatible_distributions