PythiloLesson 15 of 15 · progress kept in this browser

Requirements and versions

A dependency line such as requests>=2.30,<3 is a requirement: a project name, an optional version specifier, and sometimes extras in brackets and an environment marker after ;. The packaging library parses them with the same rules pip uses.

from packaging import requirements
from packaging.version import Version

print("1.10" > "1.9")
print(Version("1.10") > Version("1.9"))
req = requirements.Requirement(
    "rich[jupyter]>=13,<15")
print(req.name, req.extras, req.specifier)
print(req.specifier.contains("14.2"))
  • ==2.* allows any 2.x release, ~=2.1 means at least 2.1 but below 3, and != excludes one version.
  • Versions compare by their numbers. As text, "1.10" sorts before "1.9".
  • Pre-releases such as 2.0rc1 are left out of ranges unless you ask for them.
  • A marker such as sys_platform == "win32" limits a requirement to some machines, and req.marker.evaluate() tests it on this one.
  • Names compare after canonicalize_name from packaging.utils, so Typing_Extensions and typing-extensions are the same project.
Your task

Write unmet(requirements, installed) taking requirement strings and a dict of installed names to versions, and returning a sorted list of the requirement names, as written, that are missing or out of range (an installed pre-release counts when in range). Compare names with canonicalize_name, and skip requirements whose marker does not apply here.

This lesson uses packaging, which Python in this browser does not include. Read it here, and run and check it in the Pythilo app on iPhone or iPad.

All lessons