Version specifiers, checked by hand
A small model of what an installer does when it tests a version against a rule. The function allowed calls itself once for each rule in a comma list (that is recursion, Topic 5.8). Real tools use the packaging library, which handles every PEP 440 form. Check how ~=1.4.2 accepts 1.4.9 but rejects 1.5.0.
pyproject.toml is the one standard file that describes a Python project: its name, version, supported Python, dependencies (with version rules), optional extras, development-only groups, commands and tool settings.
Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.
Ready for more? This phase has 5 hands-on exercises with automatic checks.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Write the dependencies line for a project that needs requests at least version 2.31 but below 3.
Write a function newest(versions) that takes a list of version strings such as ["2.9.0", "2.10.0", "2.1.5"] and returns the highest one as a string. Compare the parts as numbers, not as text.
Make a string that holds a tiny TOML file with a [project] table and the line name = "demo". Parse it with tomllib.loads and print the name.
Explain it without notes
What goes into the [project] table of pyproject.toml?
Explain >=1.4,<2, ~=1.4.2 and ==1.4.2.
What is the difference between optional dependencies and dependency groups?
Why do we have both pyproject.toml and a lock file?
Expected output
2.10.0 > 2.9.0 as numbers: True
2.10.0 > 2.9.0 as text: False
1.4.1 {'>=1.4,<2': True, '~=1.4.2': False, '==1.4.2': False}
1.4.2 {'>=1.4,<2': True, '~=1.4.2': True, '==1.4.2': True}
1.4.9 {'>=1.4,<2': True, '~=1.4.2': True, '==1.4.2': False}
1.5.0 {'>=1.4,<2': True, '~=1.4.2': False, '==1.4.2': False}
2.0.0 {'>=1.4,<2': False, '~=1.4.2': False, '==1.4.2': False}