astral-sh/uv
uv-pep508
crates/uv-pep508/ implements PEP 508 plus uv's
non-PEP 508 extensions. It parses requirement strings (`requests[security] >=2; python_version
= "3.8"`) and the markers attached to them, and provides arithmetic and rendering for both.
It was imported in October 2023 from the standalone
pep508-rs crate (#32) and absorbed into the workspace
during the rename to uv in early 2024.
Purpose
Provide a battle-tested PEP 508 parser/printer for:
Requirements with optional version specifiers, extras, URLs, and markers.MarkerEnvironments (the runtime values ofpython_version,sys_platform, etc.).MarkerTrees (a normalized DNF / CNF representation of marker expressions).VerbatimUrls (URLs that round-trip with their original syntax preserved for diagnostics).- The
non-pep508-extensionsfeature: variable-arity in/notin and a few extensions uv uses internally.
Directory layout
crates/uv-pep508/src/
├── lib.rs # 62k chars: Requirement, ExtraName/PackageName parsing, public API
├── verbatim_url.rs # 31k chars: VerbatimUrl + ParsedUrl (preserve original syntax)
├── unnamed.rs # UnnamedRequirement (URL-only without a package name)
├── marker/ # MarkerTree, MarkerEnvironment, MarkerExpression
├── origin.rs # RequirementOrigin (pyproject.toml? requirements.txt? CLI?)
└── cursor.rs # Tiny string cursor used by the hand-rolled parserKey abstractions
| Type | File | Role |
|---|---|---|
Requirement |
lib.rs |
A complete requirement: name, extras, version specifier, URL, marker. |
MarkerEnvironment |
marker/ |
The runtime environment values (PEP 508 §6) used to evaluate a MarkerTree. |
MarkerTree |
marker/ |
Normalized boolean tree of marker expressions; supports intersection, conjunction, simplification. |
MarkerExpression |
marker/ |
A leaf marker like python_version >= "3.8". |
MarkerOperator |
marker/ |
The operator: ==, !=, <=, >=, <, >, ~=, in, not in. |
MarkerValueString |
marker/ |
The right-hand side of a marker (a literal or a variable). |
VerbatimUrl |
verbatim_url.rs |
A URL that also remembers its original textual form. |
UnnamedRequirement |
unnamed.rs |
A requirement specified by URL only. |
RequirementOrigin |
origin.rs |
Where a requirement came from (a path, a CLI flag, …). Used in error messages. |
How it works
Parsing
The parser is hand-written using a small Cursor over the input bytes (cursor.rs). It is
strict by default but accepts a few uv-specific extensions when the non-pep508-extensions
feature is enabled (which is the default in the workspace):
- Multi-value
in/not inoperators. - Specific recovery rules for malformed but obviously-intended inputs.
Errors carry byte offsets so they can be displayed with miette underlines.
Markers
MarkerTree is the most subtle part. Operations like tree.evaluate(env) are simple, but the
resolver also needs:
- Conjunction (
tree.and(other)) and disjunction (tree.or(other)). - Negation (
!tree). - Implication / containment (does tree A imply tree B?).
- Simplification (
(python_version >= "3.8") & (python_version >= "3.9")→python_version >= "3.9"). - Universal evaluation ("does this tree hold for every environment in this set?"), used during forking.
The implementation uses BDD-style canonicalization plus PEP 508-aware specializations for
python_version, python_full_version, sys_platform, os_name, platform_machine, and
the various other markers.
VerbatimUrl
Because uv has to reproduce user input in error messages and lockfiles, plain url::Url is
not enough — it normalizes paths and percent-encodings. VerbatimUrl stores the original
string alongside the parsed Url so that round-tripping is exact.
ParsedUrl (in crates/uv-pypi-types/) builds on VerbatimUrl to classify URLs into
git/path/archive forms.
Integration points
uv-pep440forVersionandVersionSpecifiers.uv-resolverfor marker arithmetic during forking and conflict marker construction.uv-pypi-typesconsumesRequirement/UnnamedRequirementand re-exports them.uv-requirements-txtparses pip-style requirements files intoRequirements.uv-workspaceparsespyproject.tomlrequirements.
Entry points for modification
- Add a new marker variable —
marker/. Each new variable needs:- A token in the parser.
- A field on
MarkerEnvironment. - Comparison logic in evaluation and simplification.
- Extend the requirement grammar —
lib.rs. Be careful: most requirement strings come from the wild and any change risks breaking real-world packages. - Improve marker simplification —
marker/. The resolver benefits directly: smaller marker trees mean smaller lockfiles and faster forks.
Key source files
| File | Purpose |
|---|---|
crates/uv-pep508/src/lib.rs |
The Requirement type and parser. |
crates/uv-pep508/src/verbatim_url.rs |
VerbatimUrl for byte-faithful URL display. |
crates/uv-pep508/src/marker/ |
The marker subsystem. |
crates/uv-pep508/src/cursor.rs |
The byte-oriented cursor used by the parser. |
See also
uv-pep440— versions and specifiers.uv-resolver— the heaviest consumer of marker arithmetic.- The upstream specification: https://peps.python.org/pep-0508/.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.