PEP 508 marker conversion#
This page documents how conda-pypi translates PEP 508 environment markers for repodata and for .whl → .conda conversion, and how that interacts with conda’s MatchSpec. For stability expectations of wheel repodata and the v3.whl channel layout, see the Wheel channels section in features.
Context#
Conda does not yet support [when="…"] conditional syntax on dependency strings, nor serialized optional-extras forms on MatchSpec (for example bracket spellings such as [extras=[…]]).
PyPI environment markers (python_version, sys_platform, extra, and related variables) are not valid conda MatchSpec syntax on their own. For wheel repodata from conda_pypi.markers.pypi_to_repodata_noarch_whl_entry(), conda-pypi does emit [when="…"] on dependency strings and extra_depends tables so that the Rattler solver can use them. The inner condition is JSON-encoded (json.dumps) so nested quotes are safe.
When building .conda packages from wheel METADATA (conda_pypi.translate.requires_to_conda()), markers are not encoded as [when="…"] on depends. The PEP 508 marker extra == "…" is split into the per-extra requirement map. Other marker dimensions are omitted from depends.
Translation does not evaluate markers against the build machine at conversion time. Output is shaped for conda-style metadata and repodata.
Additionally, this topic only pertains to dependency markers, not marker files per PEP 668 EXTERNALLY-MANAGED.
Optional dependency extras#
In the context of environment markers above, one of them is called extra. For example, extra == "dev" in a condition such as requests ; extra == "dev" means the dependency is only active when the package’s own dev optional group is installed. The separate concept of optional dependency extras is described below.
Syntax such as httpx[cli] denotes PEP 508 optional extras on the dependency name. This syntax means that we want httpx with an optional group of dependencies called cli. The dependency specifier grammar allows a comma-separated list of extra names.
When doing wheel to conda package conversion, the brackets are dropped, for example httpx[cli]>=0.24 becomes httpx>=0.24. conda_pypi.translate.requires_to_conda() keeps only the base package name and version specifier.
For creating repodata, the bracket syntax is supported, for example httpx[cli]>=0.24 is kept as is. This is done by conda_pypi.markers.pypi_to_repodata_noarch_whl_entry() which handles the bracket extras syntax (including conda_pypi.markers.dependency_extras_suffix() for optional dependency extras).
PEP 508 variables#
The table is ordered by how often each variable appears in PyPI dependency metadata (census from PyPI, January 2025). The Supported column summarizes whether _normalize_marker_clause in conda_pypi.markers emits a fragment for when, partially handles it, or omits it. The Notes column describes what is translated or why it is skipped.
Marker variable |
~Uses on PyPI |
Supported |
Notes |
|---|---|---|---|
|
2,034,408 |
Yes |
Emits |
|
243,706 |
Yes |
Maps known literals to virtual packages ( |
|
223,549 |
Partial |
Same mapping as |
|
145,549 |
No |
No fragment. Limited alignment between PEP 508 arch strings and conda virtuals. |
|
89,434 |
Partial |
Common interpreters omitted so noarch paths are not over-restricted. |
|
25,840 |
Yes |
Same rules as |
|
22,158 |
Partial |
Same general approach as |
|
17,294 |
Partial |
|
|
6,316 |
No |
Omitted. |
|
241 |
No |
Omitted. |
|
44 |
No |
Omitted. |
|
— |
Yes |
Drives |
Variables not listed produce no fragment. Boolean and / or use _combine_conditions to retain a usable branch when one side cannot be translated.
Omissions are mostly intentional, for example virtual-package coverage is bounded, architecture strings map poorly to conda subdirs, and the default stance is slightly permissive on noarch-style metadata rather than incorrectly excluding dependencies.
Where this runs in the codebase#
Location |
Role |
|---|---|
Marker AST walk and clause normalization. |
|
Splits a |
|
|
|
|
MatchSpec and [when="…"]#
Strings such as pkg>=1[when="…"] are not valid conda MatchSpec input, since conda.models.match_spec.MatchSpec does not yet expose a when field. Proposed standardization of conditional dependencies and a serialized when syntax lives in CEP PR #111 (conditional dependencies, extras, and flags).
conda-pypi may emit [when="…"] only in repodata for solvers that accept that shape (for example Rattler). Wheel → .conda index.json does not put [when="…"] on dependency strings, because conda does not support that syntax.
If all marker conditions are untranslatable and there is no extra, the dependency is recorded without the when. For example, cffi ; platform_machine == "x86_64" becomes just cffi in the repodata, since platform_machine produces no fragment. Here we fallback to adding the dependecy unconditionally, which is more cautious than dropping it.