ADR-051: Second migration family¶
Status¶
Accepted; amended by ADR-056.
Decision¶
A migration family joins the bundled packs only after a Gate 0 that measures every fact its rules
rest on, with a stop condition: a fact that fails ends the pack, not the measurement. The second
family is PyPDF2 -> pypdf, py-pdf/pypdf2-to-pypdf, chosen because it needs none of what
Gemini needs (no client, no object to follow) and so shows that the pack format, not the SDK, is
what the engine depends on (ADR-050).
D1. The Gate 0 measured, and the target moved because of it¶
Run on PyPDF2 3.0.1 and pypdf 3.17.4, 4.3.1, 5.9.0 and 6.19.0:
- PyPDF2 3.0.1 imports on CPython 3.12, 3.13 and 3.14, and the project itself says it is deprecated.
- Its camelCase API raises
DeprecationErrorrather than warn, so a name that still uses it is already broken on the versions the pack migrates from. - pypdf 3.17.4 has PyPDF2 3.0.1's public surface exactly, but 97 advisories name it, all denial of service on a crafted file, some rated high, each first fixed in the 6 line. pypdf 6.19.0 has none and lacks four things PyPDF2 3.0.1 accepts, all on an object.
The pack therefore targets pypdf >=6.19 and says in limitations what it cannot see, rather than
pin the line whose surface matches and leave the user on known vulnerabilities. This is the one
place a pack's to.version is chosen over the surface that would make the migration provable.
D2. A pack's facts are tests against both installed projects¶
tests/packs/test_pypdf_facts.py reads the names, kinds and parameters from the two modules and
fails when a mapped name stops being the same kind of thing, when a class PyPDF2 defines is neither
mapped nor reported, or when to.requires_python differs from pypdf's metadata. The generic
contract (tests/packs/test_all_packs.py) runs over every bundled pack.
D3. The pack reports instead of guessing about objects¶
There is no rule kind that follows an object, so a method or a parameter is never rewritten or
reported. The four losses D1 names are in the pack's limitations, and the verification phase
(--verify) is what finds the rest in a repository.
Consequences¶
- A repository that declares Python below 3.9, or PyPDF2 below 3, or uses PyPDF2 and declares it nowhere obelize reads, is blocked, not migrated. A declaration that names no version, or a range below the floor that admits 3.0.1, is taken as 3.0.1 unless the repository shows another release (ADR-056).
- Gate 1 is not measured for this pack: the README and BENCHMARK say so.
pypdfandPyPDF2are development dependencies pinned to the measured releases, and Dependabot leaves them alone.