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
1 change: 1 addition & 0 deletions docs/changelog/+typed-marker-environment.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Expose the 11 marker keys through ``MarkerEnvironment`` for callers that type-check ``PythonInfo.marker_environment``.
5 changes: 3 additions & 2 deletions docs/how-to/standalone-usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ Once you have a :class:`~python_discovery.PythonInfo`, you can inspect everythin
+platform: str
+sysconfig_vars: dict
+sysconfig_paths: dict
+marker_environment: dict[str, str]
+marker_environment: MarkerEnvironment
+machine: str
+free_threaded: bool
+debug_build: bool
Expand Down Expand Up @@ -182,7 +182,8 @@ Once you have a :class:`~python_discovery.PythonInfo`, you can inspect everythin
info.marker_environment

Use ``marker_environment`` to evaluate :pep:`508` dependency markers for the discovered interpreter. The mapping
contains all standard marker keys, including its implementation version and operating system.
contains all standard marker keys, including its implementation version and operating system. The exported
``MarkerEnvironment`` type names those keys for type checkers.

Prefer :attr:`~python_discovery.PythonInfo.system_exe` over the raw
:attr:`~python_discovery.PythonInfo.system_executable` field it reads. The field is typed ``str | None`` because it
Expand Down
3 changes: 2 additions & 1 deletion src/python_discovery/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from ._cache import ContentStore, DiskCache, PyInfoCache
from ._discovery import get_interpreter, iter_interpreters
from ._py_info import KNOWN_ARCHITECTURES, PythonInfo, normalize_isa
from ._py_info import KNOWN_ARCHITECTURES, MarkerEnvironment, PythonInfo, normalize_isa
from ._py_spec import KNOWN_IMPLEMENTATIONS, PythonSpec
from ._specifier import SimpleSpecifier, SimpleSpecifierSet, SimpleVersion

Expand All @@ -17,6 +17,7 @@
"KNOWN_IMPLEMENTATIONS",
"ContentStore",
"DiskCache",
"MarkerEnvironment",
"PyInfoCache",
"PythonInfo",
"PythonSpec",
Expand Down
19 changes: 17 additions & 2 deletions src/python_discovery/_py_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from collections import OrderedDict
from itertools import product
from string import digits
from typing import TYPE_CHECKING, ClassVar, Final, NamedTuple
from typing import TYPE_CHECKING, ClassVar, Final, NamedTuple, TypedDict

from ._py_info_collect import PythonInfoCollector

Expand Down Expand Up @@ -57,7 +57,7 @@ class PythonInfo: # ruff:ignore[too-many-public-methods]
os: str
free_threaded: bool
debug_build: bool
marker_environment: dict[str, str]
marker_environment: MarkerEnvironment
prefix: str | None
base_prefix: str | None
real_prefix: str | None
Expand Down Expand Up @@ -629,6 +629,20 @@ def _possible_base(self) -> Generator[str, None, None]:
yield upper


class MarkerEnvironment(TypedDict):
implementation_name: str
implementation_version: str
os_name: str
platform_machine: str
platform_release: str
platform_system: str
platform_version: str
python_full_version: str
platform_python_implementation: str
python_version: str
sys_platform: str


KNOWN_ARCHITECTURES: frozenset[str] = frozenset({
"arm64",
"loongarch64",
Expand Down Expand Up @@ -672,6 +686,7 @@ def normalize_isa(isa: str) -> str:

__all__ = [
"KNOWN_ARCHITECTURES",
"MarkerEnvironment",
"PythonInfo",
"VersionInfo",
"normalize_isa",
Expand Down
6 changes: 5 additions & 1 deletion tests/py_info/test_py_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from packaging.markers import default_environment
from setuptools.dist import Distribution

from python_discovery import DiskCache, PythonInfo, PythonSpec
from python_discovery import DiskCache, MarkerEnvironment, PythonInfo, PythonSpec
from python_discovery import _cached_py_info as cached_py_info
from python_discovery._py_info import VersionInfo

Expand Down Expand Up @@ -500,6 +500,10 @@ def test_marker_environment_matches_target_interpreter() -> None:
assert info.marker_environment == default_environment()


def test_marker_environment_type_has_standard_keys() -> None:
assert MarkerEnvironment.__required_keys__ == default_environment().keys()


def test_marker_environment_uses_target_platform(tmp_path: Path) -> None:
(tmp_path / "sitecustomize.py").write_text(
'import platform\nplatform.machine = lambda: "target-machine"\nplatform.release = lambda: "target-release"\n',
Expand Down
Loading