We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Modelling Data with Dataclasses step 12 of 28
Sort Semantic Versions with Prerelease Labels
The earlier exercise Sort Version Numbers with a Dataclass sorted versions
such as 1.2.0 and 1.10.0 with order=True. Real releases also carry
labels. Under Semantic Versioning (SemVer), the major.minor.patch scheme
most packages use, 1.0.0-rc.1 is a release candidate that comes before
1.0.0. A + part such as 1.0.0+build.5 records how the release was
built and does not change the order.
order=True gets these labels wrong. A plain release has an empty
prerelease tuple, which sorts first, so 1.0.0 would come before
1.0.0-alpha. And "11" sorts before "2" as a string, so beta.11
would come before beta.2. Here you write the comparison yourself.
solve(["1.0.0", "1.0.0-beta.11", "1.0.0-beta.2", "1.0.0-alpha"])["sorted"]
# ["1.0.0-alpha", "1.0.0-beta.2", "1.0.0-beta.11", "1.0.0"]
Parse a version
Fill in Version.parse(text). It returns a Version with the int fields
major, minor and patch, and two tuples of strings, prerelease and
build. With no - part, prerelease is (). With no + part, build
is (). So 1.2.3-beta.1+build.005 parses to
Version(1, 2, 3, ("beta", "1"), ("build", "005")).
The supplied VERSION_RE checks the format. It accepts three numbers with
no leading zeros, then an optional - part and an optional + part. Each
part holds one or more identifiers, joined by dots and made of ASCII
letters, digits and -.
- Match with
VERSION_RE.fullmatch(text). The$in the pattern also matches before a final newline, somatchwould accept"1.0.0\n". - Check one more rule yourself. A prerelease identifier made only of digits
must not start with
0unless it is exactly0, so1.2.3-01is invalid. Build identifiers may start with0. - For any invalid text, raise
ValueError(f"not a semantic version: {text!r}").
Compare two versions
Write __lt__(self, other: object) -> bool by hand, and leave
order=True off the decorator. Compare (major, minor, patch) as numbers
first. When those are equal:
- If exactly one of the two versions has a prerelease, that one is lower:
1.0.0-rc.1 < 1.0.0. - Otherwise compare the prerelease identifiers from left to right, and
stop at the first pair that differs. An identifier is numeric when it is
all digits. Two numeric identifiers compare as integers, so
beta.2 < beta.11. A numeric identifier is lower than a non-numeric one. Two non-numeric identifiers, such asalphaandrc-2, compare as strings in ASCII order. - If every pair matches, the shorter tuple is lower:
alpha < alpha.1. - Build metadata never changes the order.
1.0.0+aand1.0.0+bare equal in precedence.
If other is not a Version, return the constant NotImplemented, not
the exception NotImplementedError. The constant tells Python to try the
other object's method, and < raises TypeError when neither can compare
the pair. sorted uses only <, so you do not need __le__, __gt__ or
__ge__.
Make equality ignore build metadata
Give build the option compare=False, as in the earlier exercise Hide
Secret Fields When You Print a Record. The generated __eq__ then ignores
it, so Version.parse("1.0.0+a") == Version.parse("1.0.0+b") is True.
The generated __hash__ leaves build out too, so equal versions still
have equal hashes.
What solve returns
solve(versions) takes a list of valid version strings, which may be
empty, and returns four keys:
| Key | Value |
|---|---|
"sorted" |
sorted(versions, key=Version.parse), the original strings in SemVer order |
"eq_build_ignored" |
Version.parse("1.0.0+a") == Version.parse("1.0.0+b"), which is True |
"eq_other_type" |
whether a parsed 1.0.0 equals the string "1.0.0", which is False |
"lt_other_type" |
"TypeError", the exception that < raises between a version and a string |
The supplied helpers _eq_other_type and _lt_other_type compute the last
two keys.
Edge cases
sortedis stable, so versions of equal precedence keep their input order.["1.0.0+b", "1.0.0-alpha", "1.0.0+a"]sorts to["1.0.0-alpha", "1.0.0+b", "1.0.0+a"].- Return the original strings. Do not rewrite them, remove duplicates or change the input list.
- An empty list gives
"sorted": []. The other three keys do not depend on the input.
The full rules are in the Semantic Versioning 2.0.0 specification.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Python reference solution
Sign in to track your attempts toward unlocking the reference solution.