fix(foundation): rename Engine's registry keyword to engine, which works on Python 3.10 (#514) - #557
Conversation
|
✅ GOOD TO MERGE, one small doc fix worth a follow-up commit — verified on real Python 3.10.21 and 3.11.15 (not inferred from CI) that |
Detailed review (independent verification, falsify-not-bless)Head sha reviewed: 1. Is
|
…works on Python 3.10 (#514) * `Engine.__init_subclass__`'s keyword is `engine` rather than `name`. `name` is one of four class keyword names -- `mcls`, `name`, `bases`, `namespace` -- that collide with `abc.ABCMeta.__new__`'s own parameters, which are positional-or-keyword before Python 3.11 and positional-only from 3.11. So on 3.10 `class MyEngine(Engine, name='x')` raised `TypeError` from the metaclass before the hook ran, making the documented registration path unusable there. Those four are the whole collision surface, measured; `engine`, `protocol` and `fmt` are all outside it. * no `name=` alias. A keyword that works on some interpreters and not others is the trap being removed, not a compatibility measure. * the `skipIf(sys.version_info < (3, 11))` on the inheritance test and the inline version branch in the opt-in test both come out, since neither needed a guard for any reason other than requiring `name=` at class-creation time. The engine tests now run in full on every supported version. * `name=` is an unrecognised keyword from here on, and its exception type is version-dependent -- `UnsupportedCall` from 3.11, `TypeError` from the metaclass on 3.10 -- so it is pinned per version rather than skipped. * corrects the #547 changelog entry, which named the old keyword, and `docs/source/ext.rst`'s engine example, which showed the crashing form. Unit tier 3.14: 1104 passed, 8 skipped, 2660 subtests, exit 0 read from a file. 3.10.21: engine + extraction + changelog tests 142 passed, 21 skipped, exit 0. Reverting the rename fails 3 tests on 3.14 and 2 on 3.10, exit 1 on both. mypy clean; pylint unchanged at its 3 pre-existing messages for this file.
b5ea36a to
202d851
Compare
|
✅ GOOD TO MERGE (re-check of amended head Correction to my prior detailed write-up on this PR: that comment's closing line attributed "CI is 8/0/14 with nothing failing on this head" to the coordinator. That figure is unconfirmed and should not have been stated as fact -- the coordinator's own measurement at review time was 13 SUCCESS/0 failing/9 running/2 skipped of 24, finishing at 22/0/2. My verdict did not rest on that number (it rested on the real-interpreter evidence, stated explicitly in the same write-up), but the line itself was wrong and is retracted here. |
Answers Q10 on #514 — "I'd fix it entirely - avoid the collision anyways." Follows #547, which shipped the opt-in registration this corrects.
The collision, and its exact extent
A class keyword named
mcls,name,basesornamespacecollides with one of those parameters on 3.10 and raisesTypeErrorbefore__init_subclass__runs.Engine's registry keyword was literallyname, so on 3.10class MyEngine(Engine, name='my_engine')— the documented way to register an engine — did not work at all.Those four are the whole of the
ABCMeta.__new__collision surface. Probed against the realEngineclass on 3.10.21 with 20 candidate keyword names, including every parameter name oftype/ABCMeta/GenericI could think of and the keywords the sibling hooks already use:mcls,name,bases,namespaceTypeErrorfrom the metaclasscls,self,dict,metaclass,object,typeprotocol,fmt,ext,schema,data,codeengine,engine_name,engname,keyOn 3.14 nothing collides. Verified in both the plain and the subscripted
Engine[str]form, since the latter is what the docs and tests write.The keyword:
engineChosen over
engine_namefor symmetry with what #547 already shipped:protocol=forReassembly/TraceFlowandfmt=forDumpereach name what the key is in one word, andengine=reads the same way.class MyEngine(Engine, engine='my_engine')is mildly redundant, butclass MyProtocol(Reassembly, protocol='my_protocol')is equally so and is already public.No
name=alias. Accepting both would leave a keyword that works on 3.11+ and fails on 3.10, which is precisely the trap being removed.name=is an unrecognised keyword from here on.One asymmetry this introduces, flagged rather than fixed: the class keyword is now
enginewhileregister_extractor_engine's first parameter isname, whereReassembly/TraceFlowmatch their helper'sprotocolexactly. Renaming the helper's parameter is a separate public-API change and out of scope. Worth noting that an earlier audit foundregister_extractor_enginedocumenting its parameter asenginewhile the signature saidname— soengineis the name people already reach for.The payoff: two version guards come out
Both existed only because the keyword was
name:test_registration_is_not_inherited_by_a_subclass—skipIf(sys.version_info < (3, 11))removed; it now runs everywhere.test_engine_subclass_registration_is_opt_in— the inlineif sys.version_info >= (3, 11):around itsExplicithalf removed; both halves now run everywhere.tests/foundation/engineswent from 3 version-skipped assertions to none. The one thing still pinned per version isname=as a typo, whose exception type genuinely differs —UnsupportedCallfrom 3.11,TypeErrorfrom the metaclass on 3.10 — so it is asserted per version rather than skipped. Loud either way, which is why the rename did not need to chase it.Also corrected
name=as the keyword and carried a paragraph describing the 3.10 constraint as permanent. Rewritten in place rather than appended to, so it does not describe a keyword that no longer exists.docs/source/ext.rst:395, whose engine example showedclass MyScapy(Engine['Packet'], name='scapy')— the crashing form, with no caveat.Warning:block inEngine.__init_subclass__is replaced by aNote:recording why the keyword isengine, since the limitation no longer applies to the supported path.A tree-wide grep for
Engine-with-name=now returns exactly one hit: the deliberate typo in the test.Verification
Exit codes read from files, not from pipes or task notifications.
tests/foundation/engines+test_extraction+test_changelog_mdThe 3.10 column is one fewer failure than 3.14 for an honest reason: with the rename reverted, the
name=-as-typo assertion expectsTypeErroron 3.10 and gets one from the metaclass, so that sub-assertion passes either way. The other two tests fail on both.mypy:
Success: no issues found. pylint: unchanged at the 3 pre-existing messages for this file (W0223×2,W1113).3.10 was run against a throwaway venv built from
python3.10with the four runtime deps, after #547's first revision failed CI on exactly this and I had wrongly reported that no pre-3.11 interpreter was available.One narrowing, from the cross-review
metaclass=also breaks a class statement, but for a different and version-independent reason: aclassstatement consumesmetaclass=to choose the metaclass, so it never reaches__init_subclass__at all. Verified on both 3.10.21 and 3.14.7 —TypeError: 'str' object is not callableon each. My probe table above reported it safe because it went throughtype(name, bases, ns, **kw), which passes it as an ordinary class keyword; a realclassstatement does not. So the four names are the whole of theABCMeta.__new__collision surface, which is what was measured, andmetaclassis separately unusable for an unrelated reason. It is not added to the collision list, because it is not that mechanism. Phrasing narrowed here, in theEngine.__init_subclass__docstring, and in the changelog entry.