Lesson map
Lesson 06 published. Map, example, EX016.
flowchart TD
N0["Learning outcomes"]
N1["6.1 Versions as tuples"]
N2["6.2 Ranges as sets"]
N3["Lab — Lab — version ranges"]
N4["6.3 Lab result"]
N5["6.4 Why it matters"]
N6["6.5 Lockfiles"]
N7["Pre-releases"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
N7 --> EX["Worked example"]
EX --> Q["Exercise EX016"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["6.1 Versions as tuples"]
N2["6.2 Ranges as sets"]
N3["Lab — Lab — version ranges"]
N4["6.3 Lab result"]
N5["6.4 Why it matters"]
N6["6.5 Lockfiles"]
N7["Pre-releases"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX016"]
[Learning outcomes]
|
v
[6.1 Versions as tuples]
|
v
[6.2 Ranges as sets]
|
v
[Lab — Lab — version ranges]
|
v
[6.3 Lab result]
|
v
[6.4 Why it matters]
|
v
[6.5 Lockfiles]
|
v
[Pre-releases]
|
v
[Worked example] --> [Exercise EX016]
Learning outcomes
You will (1) represent version ranges as sets; (2) compare versions as ordered tuples; (3) reason about which version a range selects; (4) avoid text-ordering mistakes.
6.1 Versions as tuples
Semantic versions such as 1.10.2 have three numbers: major, minor and patch. They are ordered as tuples: compare major first, then minor, then patch. As text, "1.10.2" sorts before "1.4.7" because the character "1" comes before "4", which is wrong.
Lena writes a small comparison function that splits a version into its three numbers and compares them in order. Comparing tuples gives the right order in every case, including versions with numbers of different lengths. She tests it against the examples in the lab and adds the case of equal versions, which must compare as neither older nor newer. The function is later reused in the society's dependency report.
6.2 Ranges as sets
A caret range ^1.4.0 means the set of versions at least 1.4.0 and below 2.0.0. Package managers pick the highest version in the set that is available. Understanding the set tells you which fixes you receive automatically and which need a manual change.
For CampusEats, a caret range on a payment library means patch and minor releases arrive automatically at the next install, while a new major version needs a deliberate upgrade and testing. Some teams prefer narrower tilde ranges, which allow only patches, trading automatic fixes for fewer surprises.
Lab — Lab — version ranges
"""Which available versions satisfy a caret range ^1.4.0 (>=1.4.0, <2.0.0)? Synthetic versions."""
def v(s): return tuple(int(x) for x in s.split("."))
available = ["1.3.9", "1.4.0", "1.4.7", "1.10.2", "2.0.0", "2.1.0"]
lo, hi = v("1.4.0"), v("2.0.0")
ok = [a for a in available if lo <= v(a) < hi]
print("satisfying:", ok)
print("highest chosen:", max(ok, key=v))
print("text sort would pick:", max(ok))
Run with python3 lab_semver.py. Verified output:
satisfying: ['1.4.0', '1.4.7', '1.10.2']
highest chosen: 1.10.2
text sort would pick: 1.4.7
6.3 Lab result
Of six versions, three satisfy ^1.4.0: 1.4.0, 1.4.7 and 1.10.2. The highest, chosen correctly by tuple order, is 1.10.2. Text sorting would wrongly pick 1.4.7, which could leave a project on an older, possibly vulnerable version.
Lena adds the six versions as a test case so the comparison cannot regress unnoticed. She also adds a case with a pre-release version, since such versions usually sit outside a normal range and are a common source of confusion when a fix is published first as a test release.
6.4 Why it matters
If a fix is released in 1.10.2 and a tool sorts as text, it would think 1.4.7 is newest and report the project as up to date. Kula et al. (2018) showed that many projects already lag behind fixes; errors like this make it worse.
Lena checks the society's dependency report and finds it uses proper version comparison. It also adds a test that fails if version strings are ever sorted as text, so a future change to the tool cannot reintroduce the mistake. A report that wrongly says a project is current is worse than no report, because it discourages anyone from checking.
6.5 Lockfiles
A lockfile records the exact version chosen, so builds are repeatable. Ranges decide what may be chosen; lockfiles record what was chosen.
CampusEats commits its lockfile, and the build installs exactly the locked versions. Updating a dependency is then a visible change in review: the lockfile diff shows which versions moved. The integrity check in Lena's first week depended on this too, since the lockfile also records the expected hash of each package, and the build refused a download that did not match it.
Pre-releases
Versions like 2.0.0-beta are pre-releases and usually excluded from ranges unless asked for, so tests should include them as edge cases.
Updating safely
When a range allows a new minor version, CampusEats updates the lockfile weekly in a separate pull request, so tests can confirm nothing broke before the change is merged.
Worked example
Lena finds a small internal script that reported "latest version" for the society's dashboard by text sorting. She replaces it with tuple comparison and adds a test with 1.4.7 versus 1.10.2.
Synthetic case. Versions are synthetic.
Errors and limits
Some ecosystems use pre-release labels and other schemes; follow each ecosystem's rules.
Exercises
- EX016 — Lab set. Which versions satisfy ^1.4.0 in the lab?
Show marking criteria
Rubric: 3 = 1.4.0, 1.4.7, 1.10.2; 2 = two; 1 = one; 0 = none. - EX017 — Text mistake. What would text sorting pick and why is it wrong?
Show marking criteria
Rubric: 3 = 1.4.7; "1" before "4" in text; 2 = partial; 1 = vague; 0 = none. - EX018 — Range vs lockfile. Distinguish ranges and lockfiles.
Show marking criteria
Rubric: 3 = ranges allow; lockfile records chosen; 2 = partial; 1 = vague; 0 = none.
Synthesis
Treat versions as ordered tuples and ranges as sets, and record exact choices.
Continue studying — chapter 06 of 40
← Chapter 05 (draft) · Course chapter index · Chapter 07 (draft) →