We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Seams, Modules, Packaging and Tooling step 21 of 36
Does this version satisfy this specifier?
Implement the comparison a resolver runs thousands of times per install.
def satisfies(version: str, specifier: str) -> bool:
version is a release like "1.4.2"; specifier is a comma-joined list of
clauses like ">=1.2, <2". Return True only if every clause holds.
Restricted to release segments — no pre-releases, post-releases, dev releases, epochs or local versions. Support these operators:
| Operator | Meaning |
|---|---|
== != >= <= > < |
ordered comparison of the release tuple |
~= |
compatible release: >=V and the prefix of V with its last segment dropped must match |
The two rules that make this more than string handling:
Compare numerically, segment by segment. "1.10" is newer than
"1.9". A lexicographic comparison says the opposite and looks correct until
a project reaches its tenth minor release.
Missing segments are zero. "1.0" and "1.0.0" are the same release, so
satisfies("1.0", "==1.0.0") is True. Pad the shorter tuple before
comparing; do not compare tuples of different lengths directly.
And the operator worth getting exactly right:
-
~=1.4.2means>=1.4.2and==1.4.*— it accepts1.4.9, rejects1.5.0. -
~=1.4means>=1.4and==1.*— it accepts both.
The dropped segment is the one allowed to move, so the number of segments
written changes the meaning. ~= with a single segment (~=1) is not
meaningful; raise ValueError.
Why this matters in a real repository. This function is the difference between an upper bound that expresses what you tested and one that strands every downstream consumer. A library’s ranges are the only thing its users can see, and a consumer cannot loosen a bound you imposed — they can only pin you to an older release or fork you.
Types. Model a clause as a frozen dataclass with an operator field
typed Literal["==", "!=", ">=", "<=", "~=", ">", "<"] and a
release: tuple[int, ...]. Dispatch with match. Exhaustiveness only holds
if the Literal lists every operator you parse — if you parse an eighth
operator into a seven-member Literal, the checker cannot help you, and
assert_never in the final arm is what makes that failure visible.
Your submission must pass mypy --strict.
Loading visualization…
Stuck?
Python reference solution
Sign in to attempt this problem and reveal the reference solution.