Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/tests/downstream/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ build
packaging
requests
tomli
virtualenv>=20.13.1
virtualenv>=21
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"]
Expand Down
28 changes: 7 additions & 21 deletions src/hatch/env/virtual.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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))
)
Expand Down Expand Up @@ -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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks to @gaborbernat’s super fast response!

Sometimes all you have to do is ask. If you can think of any other monkey patching done in the code base, maybe it’s a good time to ask the relevant projects to add these respective features?

)
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
Expand Down
Loading