![]() In *some* places in our crates, `serde` (and `rkyv`) are optional dependencies. I believe this was done out of reasons of "good sense," that is, it follows a Rust ecosystem pattern where serde integration tends to be an opt-in crate feature. (And similarly for `rkyv`.) However, ultimately, `uv` itself requires `serde` and `rkyv` to function. Since our crates are strictly internal, there are limited consumers for our crates without `serde` (and `rkyv`) enabled. I think one possibility is that optional `serde` (and `rkyv`) integration means that someone can do this: cargo test -p pep440_rs And this will run tests _without_ `serde` or `rkyv` enabled. That in turn could lead to faster iteration time by reducing compile times. But, I'm not sure this is worth supporting. The iterative compilation times of individual crates are probably fast enough in debug mode, even with `serde` and `rkyv` enabled. Namely, `serde` and `rkyv` themselves shouldn't need to be re-compiled in most cases. On `main`: ``` from-scratch: `cargo test -p pep440_rs --lib` 0.685 incremental: `cargo test -p pep440_rs --lib` 0.278s from-scratch: `cargo test -p pep440_rs --features serde,rkyv --lib` 3.948s incremental: `cargo test -p pep440_rs --features serde,rkyv --lib` 0.321s ``` So while a from-scratch build does take significantly longer, an incremental build is about the same. The benefit of doing this change is two-fold: 1. It brings out crates into alignment with "reality." In particular, some crates were _implicitly_ relying on `serde` being enabled without explicitly declaring it. This technically means that our `Cargo.toml`s were wrong in some cases, but it is hard to observe it because of feature unification in a Cargo workspace. 2. We no longer need to deal with the cognitive burden of writing `#[cfg_attr(feature = "serde", ...)]` everywhere. |
||
---|---|---|
.. | ||
src | ||
Cargo.lock | ||
Cargo.toml | ||
License-Apache | ||
License-BSD | ||
Readme.md |
Dependency specifiers (PEP 508) in Rust
A library for python dependency specifiers, better known as PEP 508.
Usage
In Rust
use std::str::FromStr;
use pep508_rs::Requirement;
let marker = r#"requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8""#;
let dependency_specification = Requirement::from_str(marker).unwrap();
assert_eq!(dependency_specification.name, "requests");
assert_eq!(dependency_specification.extras, Some(vec!["security".to_string(), "tests".to_string()]));
In Python
from pep508_rs import Requirement
requests = Requirement(
'requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"'
)
assert requests.name == "requests"
assert requests.extras == ["security", "tests"]
assert [str(i) for i in requests.version_or_url] == [">= 2.8.1", "== 2.8.*"]
Python bindings are built with maturin, but you can also use the normal pip install .
Version
and VersionSpecifier
from pep440_rs are reexported to avoid type mismatches.
Markers
Markers allow you to install dependencies only in specific environments (python version, operating system, architecture, etc.) or when a specific feature is activated. E.g. you can say importlib-metadata ; python_version < "3.8"
or itsdangerous (>=1.1.0) ; extra == 'security'
. Unfortunately, the marker grammar has some oversights (e.g. https://github.com/pypa/packaging.python.org/pull/1181) and the design of comparisons (PEP 440 comparisons with lexicographic fallback) leads to confusing outcomes. This implementation tries to carefully validate everything and emit warnings whenever bogus comparisons with unintended semantics are made.
In python, warnings are by default sent to the normal python logging infrastructure:
from pep508_rs import Requirement, MarkerEnvironment
env = MarkerEnvironment.current()
assert not Requirement("numpy; extra == 'science'").evaluate_markers(env, [])
assert Requirement("numpy; extra == 'science'").evaluate_markers(env, ["science"])
assert not Requirement(
"numpy; extra == 'science' and extra == 'arrays'"
).evaluate_markers(env, ["science"])
assert Requirement(
"numpy; extra == 'science' or extra == 'arrays'"
).evaluate_markers(env, ["science"])
from pep508_rs import Requirement, MarkerEnvironment
env = MarkerEnvironment.current()
Requirement("numpy; python_version >= '3.9.'").evaluate_markers(env, [])
# This will log:
# "Expected PEP 440 version to compare with python_version, found '3.9.', "
# "evaluating to false: Version `3.9.` doesn't match PEP 440 rules"