From f210ddc71df5c0317c822cd4d3dd0d6358f622a8 Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Mon, 28 Sep 2026 15:17:20 -0400 Subject: [PATCH] fix(const,vendor): stop EtherType and Socket minting on an unrecognised value Converts the last 53 minting `extend_enum` sites in the const registries, closing out issue #775: EtherType's 52 range branches and Socket's one ("Registered by Xerox"), the two registries the original #775/#847 ruling held out because their names were real attributed protocols rather than placeholder status words. - `pcapkit/vendor/reg/ethertype.py` and `pcapkit/vendor/ipx/socket.py`: the `process()` else-branch that used to render `extend_enum(cls, name, value)` now renders `cls._unregistered_member(value, name)` instead, keeping every branch's existing hex-suffixed name argument unchanged -- this is about not registering, not about renaming anything. - `pcapkit/const/reg/ethertype.py` and `pcapkit/const/ipx/socket.py` regenerated to match: Socket regenerated byte-for-byte offline (its `LINK` is `None`, so no network call is involved); EtherType hand-applied with the identical mechanical substitution, verified against the crawler's own `process()` fed the real IANA CSV fixture already committed for issue #862. - `pcapkit/corekit/enum.py`: `EnumRegistry.get()`'s docstring no longer claims three registries still mint directly; only `CGAType` does now. - `tests/const/test_const_enum_no_mint.py`: the two "mixed registry" test classes are retitled in place to prove the formerly-kept probe (`Xyplex`, `Registered by Xerox`) no longer mints, and a new, scoped `HEX_SUFFIXED_NAME_EXEMPT_PATHS` exemption lets the manufactured-name sweep tolerate the deliberately-preserved hex suffix on just these two files, without weakening `is_manufactured()` itself. - `tests/const/test_const_ethertype_862_unit.py`, `tests/const/test_const_registry_protocol.py` and `tests/vendor/test_ipx_socket_unit.py`: three call sites that asserted the old minting behaviour flipped to assert non-minting instead. Built and verified offline (no live crawl): AST sweep over pcapkit/const/ now finds exactly 1 remaining minting site (CGAType, deliberately untouched); tests/const/test_const_enum_lookup.py (13/13) and tests/vendor/test_vendor_missing_body_unit.py (6/6) pass with 0 failures/errors. Refs #775 --- pcapkit/const/ipx/socket.py | 4 +- pcapkit/const/reg/ethertype.py | 106 ++++---- pcapkit/corekit/enum.py | 33 +-- pcapkit/vendor/ipx/socket.py | 12 +- pcapkit/vendor/reg/ethertype.py | 18 +- tests/const/test_const_enum_no_mint.py | 243 ++++++++++++++----- tests/const/test_const_ethertype_862_unit.py | 15 +- tests/const/test_const_registry_protocol.py | 44 ++-- tests/vendor/test_ipx_socket_unit.py | 25 +- 9 files changed, 337 insertions(+), 163 deletions(-) diff --git a/pcapkit/const/ipx/socket.py b/pcapkit/const/ipx/socket.py index 8c8451da6b..649dd12678 100644 --- a/pcapkit/const/ipx/socket.py +++ b/pcapkit/const/ipx/socket.py @@ -10,7 +10,7 @@ """ -from aenum import IntEnum, extend_enum +from aenum import IntEnum from pcapkit.corekit.enum import EnumRegistry @@ -80,7 +80,7 @@ def _missing_(cls, value: 'int') -> 'Socket': return cls._unregistered_member(value, 'Experimental') if 0x0001 <= value <= 0x0BB8: #: Registered by Xerox - return extend_enum(cls, 'Registered by Xerox_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Registered by Xerox_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x4000 <= value <= 0x4FFF: #: Dynamically Assigned Socket Numbers return cls._unregistered_member(value, 'Dynamically Assigned Socket Numbers') diff --git a/pcapkit/const/reg/ethertype.py b/pcapkit/const/reg/ethertype.py index 009bcbc8ac..2fc74eef9a 100644 --- a/pcapkit/const/reg/ethertype.py +++ b/pcapkit/const/reg/ethertype.py @@ -10,7 +10,7 @@ """ -from aenum import IntEnum, extend_enum +from aenum import IntEnum from pcapkit.corekit.enum import EnumRegistry @@ -534,22 +534,22 @@ def _missing_(cls, value: 'int') -> 'EtherType': return cls._unregistered_member(value, 'Old_Xerox_Experimental_values_Invalid_as_an_Ethertype_since_1983') if 0x0000 <= value <= 0x05DC: #: IEEE802.3 Length Field [IEEE Std 802.3] - return extend_enum(cls, 'IEEE802_3_Length_Field_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'IEEE802_3_Length_Field_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x0888 <= value <= 0x088A: #: Xyplex [Neil Sembower] - return extend_enum(cls, 'Xyplex_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Xyplex_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x1001 <= value <= 0x100F: #: Berkeley Trailer encap/IP [Neil Sembower] - return extend_enum(cls, 'Berkeley_Trailer_encap_IP_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Berkeley_Trailer_encap_IP_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x6008 <= value <= 0x6009: #: DEC Unassigned [Neil Sembower] return cls._unregistered_member(value, 'DEC_Unassigned') if 0x6010 <= value <= 0x6014: #: 3Com Corporation [Neil Sembower] - return extend_enum(cls, 'EtherType_3Com_Corporation_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'EtherType_3Com_Corporation_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x7020 <= value <= 0x7029: #: LRT [Neil Sembower] - return extend_enum(cls, 'LRT_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'LRT_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8039 <= value <= 0x803C: #: DEC Unassigned [Neil Sembower] return cls._unregistered_member(value, 'DEC_Unassigned') @@ -558,143 +558,143 @@ def _missing_(cls, value: 'int') -> 'EtherType': return cls._unregistered_member(value, 'DEC_Unassigned') if 0x806E <= value <= 0x8077: #: Landmark Graphics Corp. [Neil Sembower] - return extend_enum(cls, 'Landmark_Graphics_Corp_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Landmark_Graphics_Corp_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x807D <= value <= 0x807F: #: Vitalink Communications [Neil Sembower] - return extend_enum(cls, 'Vitalink_Communications_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Vitalink_Communications_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8081 <= value <= 0x8083: #: Counterpoint Computers [Neil Sembower] - return extend_enum(cls, 'Counterpoint_Computers_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Counterpoint_Computers_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x809C <= value <= 0x809E: #: Datability [Neil Sembower] - return extend_enum(cls, 'Datability_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Datability_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x80A4 <= value <= 0x80B3: #: Siemens Gammasonics Inc. [Neil Sembower] - return extend_enum(cls, 'Siemens_Gammasonics_Inc_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Siemens_Gammasonics_Inc_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x80C0 <= value <= 0x80C3: #: DCA Data Exchange Cluster [Neil Sembower] - return extend_enum(cls, 'DCA_Data_Exchange_Cluster_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'DCA_Data_Exchange_Cluster_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x80C8 <= value <= 0x80CC: #: Intergraph Corporation [Neil Sembower] - return extend_enum(cls, 'Intergraph_Corporation_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Intergraph_Corporation_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x80CD <= value <= 0x80CE: #: Harris Corporation [Neil Sembower] - return extend_enum(cls, 'Harris_Corporation_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Harris_Corporation_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x80CF <= value <= 0x80D2: #: Taylor Instrument [Neil Sembower] - return extend_enum(cls, 'Taylor_Instrument_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Taylor_Instrument_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x80D3 <= value <= 0x80D4: #: Rosemount Corporation [Neil Sembower] - return extend_enum(cls, 'Rosemount_Corporation_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Rosemount_Corporation_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x80DE <= value <= 0x80DF: #: Integrated Solutions TRFS [Neil Sembower] - return extend_enum(cls, 'Integrated_Solutions_TRFS_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Integrated_Solutions_TRFS_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x80E0 <= value <= 0x80E3: #: Allen-Bradley [Neil Sembower] - return extend_enum(cls, 'Allen_Bradley_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Allen_Bradley_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x80E4 <= value <= 0x80F0: #: Datability [Neil Sembower] - return extend_enum(cls, 'Datability_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Datability_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x80F4 <= value <= 0x80F5: #: Kinetics [Neil Sembower] - return extend_enum(cls, 'Kinetics_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Kinetics_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8101 <= value <= 0x8103: #: Wellfleet Communications [Neil Sembower] - return extend_enum(cls, 'Wellfleet_Communications_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Wellfleet_Communications_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8107 <= value <= 0x8109: #: Symbolics Private [Neil Sembower] - return extend_enum(cls, 'Symbolics_Private_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Symbolics_Private_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8132 <= value <= 0x8136: #: Bridge Communications [Neil Sembower] - return extend_enum(cls, 'Bridge_Communications_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Bridge_Communications_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8137 <= value <= 0x8138: #: Novell, Inc. [Neil Sembower] - return extend_enum(cls, 'Novell_Inc_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Novell_Inc_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8139 <= value <= 0x813D: #: KTI [Neil Sembower] - return extend_enum(cls, 'KTI_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'KTI_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8151 <= value <= 0x8153: #: Qualcomm [Neil Sembower] - return extend_enum(cls, 'Qualcomm_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Qualcomm_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x815C <= value <= 0x815E: #: Computer Protocol Pty Ltd [Neil Sembower] - return extend_enum(cls, 'Computer_Protocol_Pty_Ltd_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Computer_Protocol_Pty_Ltd_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8164 <= value <= 0x8166: #: Charles River Data System [Neil Sembower] - return extend_enum(cls, 'Charles_River_Data_System_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Charles_River_Data_System_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8184 <= value <= 0x818C: #: Silicon Graphics prop. [Neil Sembower] - return extend_enum(cls, 'Silicon_Graphics_prop_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Silicon_Graphics_prop_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x819A <= value <= 0x81A3: #: Qualcomm [Neil Sembower] - return extend_enum(cls, 'Qualcomm_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Qualcomm_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x81A5 <= value <= 0x81AE: #: RAD Network Devices [Neil Sembower] - return extend_enum(cls, 'RAD_Network_Devices_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'RAD_Network_Devices_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x81B7 <= value <= 0x81B9: #: Xyplex [Neil Sembower] - return extend_enum(cls, 'Xyplex_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Xyplex_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x81CC <= value <= 0x81D5: #: Apricot Computers [Neil Sembower] - return extend_enum(cls, 'Apricot_Computers_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Apricot_Computers_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x81D6 <= value <= 0x81DD: #: Artisoft [Neil Sembower] - return extend_enum(cls, 'Artisoft_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Artisoft_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x81E6 <= value <= 0x81EF: #: Polygon [Neil Sembower] - return extend_enum(cls, 'Polygon_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Polygon_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x81F0 <= value <= 0x81F2: #: Comsat Labs [Neil Sembower] - return extend_enum(cls, 'Comsat_Labs_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Comsat_Labs_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x81F3 <= value <= 0x81F5: #: SAIC [Neil Sembower] - return extend_enum(cls, 'SAIC_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'SAIC_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x81F6 <= value <= 0x81F8: #: VG Analytical [Neil Sembower] - return extend_enum(cls, 'VG_Analytical_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'VG_Analytical_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8203 <= value <= 0x8205: #: Quantum Software [Neil Sembower] - return extend_enum(cls, 'Quantum_Software_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Quantum_Software_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8221 <= value <= 0x8222: #: Ascom Banking Systems [Neil Sembower] - return extend_enum(cls, 'Ascom_Banking_Systems_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Ascom_Banking_Systems_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x823E <= value <= 0x8240: #: Advanced Encryption Syste [Neil Sembower] - return extend_enum(cls, 'Advanced_Encryption_Syste_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Advanced_Encryption_Syste_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x827F <= value <= 0x8282: #: Athena Programming [Neil Sembower] - return extend_enum(cls, 'Athena_Programming_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Athena_Programming_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8263 <= value <= 0x826A: #: Charles River Data System [Neil Sembower] - return extend_enum(cls, 'Charles_River_Data_System_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Charles_River_Data_System_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x829A <= value <= 0x829B: #: Inst Ind Info Tech [Neil Sembower] - return extend_enum(cls, 'Inst_Ind_Info_Tech_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Inst_Ind_Info_Tech_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x829C <= value <= 0x82AB: #: Taurus Controls [Neil Sembower] - return extend_enum(cls, 'Taurus_Controls_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Taurus_Controls_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x82AC <= value <= 0x8693: #: Walker Richer & Quinn [Neil Sembower] - return extend_enum(cls, 'Walker_Richer_Quinn_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Walker_Richer_Quinn_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8694 <= value <= 0x869D: #: Idea Courier [Neil Sembower] - return extend_enum(cls, 'Idea_Courier_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Idea_Courier_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x869E <= value <= 0x86A1: #: Computer Network Tech [Neil Sembower] - return extend_enum(cls, 'Computer_Network_Tech_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Computer_Network_Tech_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x86A3 <= value <= 0x86AC: #: Gateway Communications [Neil Sembower] - return extend_enum(cls, 'Gateway_Communications_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Gateway_Communications_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x86E0 <= value <= 0x86EF: #: Landis & Gyr Powers [Neil Sembower] - return extend_enum(cls, 'Landis_Gyr_Powers_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Landis_Gyr_Powers_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8700 <= value <= 0x8710: #: Motorola [Neil Sembower] - return extend_enum(cls, 'Motorola_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Motorola_0x%s' % hex(value)[2:].upper().zfill(4)) if 0x8A96 <= value <= 0x8A97: #: Invisible Software [Neil Sembower] - return extend_enum(cls, 'Invisible_Software_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'Invisible_Software_0x%s' % hex(value)[2:].upper().zfill(4)) if 0xFF00 <= value <= 0xFF0F: #: ISC Bunker Ramo private protocol. [Neil Sembower] - return extend_enum(cls, 'ISC_Bunker_Ramo_private_protocol_0x%s' % hex(value)[2:].upper().zfill(4), value) + return cls._unregistered_member(value, 'ISC_Bunker_Ramo_private_protocol_0x%s' % hex(value)[2:].upper().zfill(4)) return super()._missing_(value) diff --git a/pcapkit/corekit/enum.py b/pcapkit/corekit/enum.py index cf279e007a..a72b96cf4d 100644 --- a/pcapkit/corekit/enum.py +++ b/pcapkit/corekit/enum.py @@ -272,15 +272,17 @@ def get(cls, key: 'Any', default: 'Any' = NO_DEFAULT) -> 'Self': It never mints while resolving ``default``; ``key`` may still mint through a ``_missing_`` that GitHub issue #775's ruling deliberately - kept minting, on three registries (``EtherType``, ``Socket``, - ``CGAType``). Registering a member any other way is - :meth:`register`'s job and nobody else's, which is the ruling #775 - exists to carry out: *"so that we dont create registered enums out - of unrecognised/unregistered values, unless user/caller explicitly + kept minting, on one registry (``CGAType``) -- the ruling's final + round converted the other two it originally held out, + ``EtherType`` and ``Socket``, so they no longer mint on any path + either. Registering a member any other way is :meth:`register`'s + job and nobody else's, which is the ruling #775 exists to carry + out: *"so that we dont create registered enums out of + unrecognised/unregistered values, unless user/caller explicitly created them"*. A value inside a registry's declared-but-unassigned range still resolves, through that registry's own ``_missing_`` and :meth:`_unregistered_member`, to a member that is deliberately - absent from the lookup tables -- true outside the three registries + absent from the lookup tables -- true outside the one registry named above, where such a value instead lands in *both* tables, exactly as :meth:`register` would leave it -- for a non-``str`` key; the ``str`` case is qualified below. Both describe ``key`` resolution @@ -309,15 +311,16 @@ def get(cls, key: 'Any', default: 'Any' = NO_DEFAULT) -> 'Self': :class:`~pcapkit.const.reg.apptype.udp.UDP`, :class:`~pcapkit.const.reg.apptype.sctp.SCTP` and :class:`~pcapkit.const.reg.apptype.dccp.DCCP`) no longer mint - on any path, so no live witness exists in this tree today. The - registries that still mint directly via - :func:`~aenum.extend_enum` -- - :class:`~pcapkit.const.ipx.socket.Socket`, - :class:`~pcapkit.const.mh.cga_type.CGAType` and - :class:`~pcapkit.const.reg.ethertype.EtherType` -- are all - :class:`int`-valued, so a ``str`` name could not reach their mint - branches even if this restriction did not exist; they are not - exceptions to it, just not reachable by it. This is about a future + on any path, so no live witness exists in this tree today. The one + registry that still mints directly via :func:`~aenum.extend_enum`, + :class:`~pcapkit.const.mh.cga_type.CGAType`, is + :class:`int`-valued, so a ``str`` name could not reach its mint + branch even if this restriction did not exist; it is not an + exception to it, just not reachable by it. GitHub issue #775's final + round converted the other two that used to share this footnote, + :class:`~pcapkit.const.ipx.socket.Socket` and + :class:`~pcapkit.const.reg.ethertype.EtherType`, so ``CGAType`` is + now the only one left. This is about a future ``str``-valued registry (or a present one whose ``_missing_`` someday changes) reaching this base with a minting ``_missing_`` of its own, which the restriction below is written to stay correct diff --git a/pcapkit/vendor/ipx/socket.py b/pcapkit/vendor/ipx/socket.py index 88ce357404..8809f93a14 100644 --- a/pcapkit/vendor/ipx/socket.py +++ b/pcapkit/vendor/ipx/socket.py @@ -252,9 +252,13 @@ #: Range names from :data:`RANGES` above that name an allocation *policy* for #: the pool (who may claim a code, or that nobody has) rather than a specific -#: assigned protocol -- the owner's ruling on #775/#847. ``Registered by -#: Xerox`` stays a mint: it is a real ownership fact, not a placeholder, and -#: kept notation for the reader is what only these four are. +#: assigned protocol. The owner's original ruling on #775/#847 held +#: ``Registered by Xerox`` out of this set as a real ownership fact rather +#: than a placeholder. #775's final round converts it too, so this set no +#: longer decides *whether* a range registers -- only *how its name is +#: spelled*: these four keep the bare label with no hex suffix, since the +#: label itself already says nothing was assigned, while ``Registered by +#: Xerox`` keeps the hex-suffixed name it always rendered. UNASSIGNED_RANGE_NAMES = frozenset({ 'Experimental', 'Dynamically Assigned Socket Numbers', @@ -319,7 +323,7 @@ def process(self, data: 'dict[int, tuple[str, str]]') -> 'tuple[list[str], list[ if name in UNASSIGNED_RANGE_NAMES: miss.append(f" return cls._unregistered_member(value, '{name}')") else: - miss.append(f" return extend_enum(cls, '{name}_0x%s' % hex(value)[2:].upper().zfill(4), value)") + miss.append(f" return cls._unregistered_member(value, '{name}_0x%s' % hex(value)[2:].upper().zfill(4))") return enum, miss diff --git a/pcapkit/vendor/reg/ethertype.py b/pcapkit/vendor/reg/ethertype.py index 111fe50e4f..93a5992642 100644 --- a/pcapkit/vendor/reg/ethertype.py +++ b/pcapkit/vendor/reg/ethertype.py @@ -35,12 +35,16 @@ class EtherType(Vendor): #: Row labels that the historical Neil Sembower list itself marks as #: carrying no real assignment -- a company holding the block but naming #: nothing (``DEC Unassigned``), or a range the list says is dead/invalid - #: outright -- rather than a proprietary protocol's real name. Per the - #: owner's ruling on #775/#847: a proprietary protocol's company name IS - #: the final concrete name (no public name will ever exist), so every - #: *other* row here still mints; only these read as notation for the - #: reader and get a throwaway :meth:`~pcapkit.corekit.enum.EnumRegistry. - #: _unregistered_member` instead. + #: outright -- rather than a proprietary protocol's real name. The owner's + #: original ruling on #775/#847 held these two out as the only rows to + #: convert to :meth:`~pcapkit.corekit.enum.EnumRegistry._unregistered_ + #: member`, on the theory that a proprietary protocol's company name IS + #: the final concrete name for every other row. #775's final round + #: converts every row alike, so this set no longer decides *whether* a + #: row registers -- only *how its name is spelled*: these two keep the + #: bare label with no hex suffix, since the label itself already says + #: nothing was assigned, while every other row keeps the hex-suffixed + #: name it always rendered. UNASSIGNED_ROW_NAMES = frozenset({ 'DEC Unassigned', 'Old Xerox Experimental values. Invalid as an Ethertype since 1983.', @@ -157,7 +161,7 @@ def process(self, data: 'list[str]') -> 'tuple[list[str], list[str]]': if name in self.UNASSIGNED_ROW_NAMES: block.append(f" return cls._unregistered_member(value, '{self.safe_name(name)}')") else: - block.append(f" return extend_enum(cls, '{self.safe_name(name)}_0x%s' % hex(value)[2:].upper().zfill(4), value)") # pylint: disable=line-too-long + block.append(f" return cls._unregistered_member(value, '{self.safe_name(name)}_0x%s' % hex(value)[2:].upper().zfill(4))") # pylint: disable=line-too-long self._insert_range(ranges, (int(start, base=16), int(stop, base=16), block)) miss = [line for _, _, block in ranges for line in block] # type: list[str] diff --git a/tests/const/test_const_enum_no_mint.py b/tests/const/test_const_enum_no_mint.py index 31f5f25017..dc54ec02e8 100644 --- a/tests/const/test_const_enum_no_mint.py +++ b/tests/const/test_const_enum_no_mint.py @@ -136,6 +136,26 @@ :meth:`AppType._dispatch`, where it used to name no registry at all and raise. None of the tests below pin that, per the same instruction. +GitHub issue #775's final round closes the two mixed registries themselves: +every one of :class:`~pcapkit.const.reg.ethertype.EtherType`'s 52 still- +minting range branches, and :class:`~pcapkit.const.ipx.socket.Socket`'s one +(``Registered by Xerox``), now convert to :meth:`~pcapkit.corekit.enum. +EnumRegistry._unregistered_member` too -- the owner's ruling, verbatim: +*"Preserve each branch's existing name argument exactly as the current code +produces it -- this change is about not registering, not about renaming +anything."* So each keeps the hex-suffixed name it always rendered +(``Xyplex_0x0888``, not a bare ``Xyplex``) even though it no longer +registers -- neither is "mixed" any more, both are wholly converted like the +82 in :data:`RULING_CONVERTED_REGISTRIES`, and :class:`EtherTypeMixedMintTests` +/:class:`IPXSocketMixedMintTests` below are retitled in place to prove the +formerly-kept probe no longer mints rather than that it still does. The one +consequence worth naming: this reintroduces the exact "manufactured, +value-suffixed name" shape :func:`is_manufactured` exists to flag, on calls +that are still safe because :meth:`_unregistered_member` never registers +regardless of what its ``name`` argument looks like -- see +:data:`HEX_SUFFIXED_NAME_EXEMPT_PATHS` in :class:`UnregisteredMemberNameIsBareTests` +for the scoped, file-level exemption this required. + """ from __future__ import annotations @@ -452,11 +472,12 @@ def _first_unassigned_value(cls: 'type') -> 'int': #: (fixed by #865) reordered :meth:`pcapkit.vendor.reg.ethertype.EtherType. #: process` so the narrower range is tested first, which makes that branch #: directly probeable like any other reachable-gap registry -- the same shape -#: :class:`~pcapkit.const.ipx.socket.Socket` (also mixed) already demonstrates -#: in this list below. EtherType has therefore joined this set; see -#: :data:`ETHERTYPE_UNASSIGNED_PROBES`'s comment for the probe itself, still -#: covered explicitly by :class:`EtherTypeMixedMintTests` as well since it -#: remains a mixed registry. +#: :class:`~pcapkit.const.ipx.socket.Socket` (mixed at the time) already +#: demonstrates in this list below. EtherType has therefore joined this set; +#: see :data:`ETHERTYPE_UNASSIGNED_PROBES`'s comment for the probe itself, +#: still covered explicitly by :class:`EtherTypeMixedMintTests` as well. +#: Neither registry is actually mixed any more as of #775's final round -- +#: see that class's own updated docstring. #: #: Derived the same way as :data:`RULING_CONVERTED_REGISTRIES`: computed once #: by walking each candidate's own bounds for a gap, not hand-picked. @@ -533,12 +554,13 @@ def _first_unassigned_value(cls: 'type') -> 'int': ) #: :class:`~pcapkit.const.reg.ethertype.EtherType` probes for the owner's -#: ruling: ``DEC Unassigned`` and the historical list's own "Old Xerox -#: Experimental values. Invalid as an Ethertype since 1983." both convert, -#: because the label itself says nothing was assigned. Every *other* named -#: block -- companies the historical list attributes a real code range to -- -#: stays a mint; :data:`ETHERTYPE_KEPT_PROBE` pins one (``Xyplex``) as a -#: regression guard. +#: original #775/#847 ruling: ``DEC Unassigned`` and the historical list's own +#: "Old Xerox Experimental values. Invalid as an Ethertype since 1983." both +#: convert, because the label itself says nothing was assigned. Every *other* +#: named block -- companies the historical list attributes a real code range +#: to -- used to stay a mint; :data:`ETHERTYPE_FORMERLY_KEPT_PROBE` pins one +#: (``Xyplex``) as a regression guard, now flipped to prove it no longer +#: mints either, since #775's final round converts every remaining branch. #: #: "Old Xerox Experimental" (0x0101-0x01FF) used to not be behaviourally #: probeable: it was a strict subset of the earlier, wider "IEEE802.3 Length @@ -560,19 +582,33 @@ def _first_unassigned_value(cls: 'type') -> 'int': 0x8039: 'DEC_Unassigned', 0x0101: ETHERTYPE_OLD_XEROX_LABEL, } -ETHERTYPE_KEPT_PROBE = (0x0888, 'Xyplex_0x0888') - -#: :class:`~pcapkit.const.ipx.socket.Socket` probes for the owner's ruling: -#: ``Experimental`` and the three "who may claim this pool" policy labels -#: convert; ``Registered by Xerox`` -- a real ownership fact, not a status -#: word -- keeps minting, pinned by :data:`IPX_SOCKET_KEPT_PROBE`. +#: The one probe the original ruling held out as "a real ownership fact, keep +#: minting" -- Xyplex, 0x0888. #775's final round converts it too, preserving +#: the hex-suffixed name exactly as the crawler always rendered it (the +#: owner's ruling, verbatim: *"Preserve each branch's existing name argument +#: exactly as the current code produces it -- this change is about not +#: registering, not about renaming anything."*), so this now pins the +#: opposite of what its name suggests: that the formerly-kept probe no +#: longer mints either. Kept as its own constant, distinct from +#: :data:`ETHERTYPE_UNASSIGNED_PROBES`, because :class:`EtherTypeMixedMintTests` +#: below still wants it named individually in its own regression test. +ETHERTYPE_FORMERLY_KEPT_PROBE = (0x0888, 'Xyplex_0x0888') + +#: :class:`~pcapkit.const.ipx.socket.Socket` probes for the owner's original +#: #775/#847 ruling: ``Experimental`` and the three "who may claim this pool" +#: policy labels convert; ``Registered by Xerox`` -- a real ownership fact, +#: not a status word -- used to keep minting, pinned by +#: :data:`IPX_SOCKET_FORMERLY_KEPT_PROBE`. IPX_SOCKET_UNASSIGNED_PROBES = { 0x0025: 'Experimental', 0x4001: 'Dynamically Assigned Socket Numbers', 0x8001: 'Statically Assigned Socket Numbers', 0x0BBA: 'Dynamically Assigned', } -IPX_SOCKET_KEPT_PROBE = (0x0010, 'Registered by Xerox_0x0010') +#: The mirror of :data:`ETHERTYPE_FORMERLY_KEPT_PROBE` for +#: :class:`~pcapkit.const.ipx.socket.Socket`: ``Registered by Xerox`` also +#: converts in #775's final round, keeping its hex-suffixed name. +IPX_SOCKET_FORMERLY_KEPT_PROBE = (0x0010, 'Registered by Xerox_0x0010') def _first_unregistered_value(cls: 'type') -> 'Optional[int]': @@ -1112,6 +1148,21 @@ def test_self_check(self) -> None: f'{label}: {source!r}') +#: Generated files whose ``_unregistered_member`` calls are exempted from +#: :meth:`UnregisteredMemberNameIsBareTests. +#: test_every_unregistered_member_call_passes_a_non_manufactured_name`'s +#: manufactured-name sweep, because both deliberately keep a per-value +#: hex-suffixed name (``Xyplex_0x0888``, ``Registered by Xerox_0x0010``) +#: rather than the bare placeholder every other converted registry uses -- +#: see that test's own docstring, and the module docstring's closing +#: paragraph, for why this is safe despite being exactly the shape +#: :func:`is_manufactured` exists to flag elsewhere. +HEX_SUFFIXED_NAME_EXEMPT_PATHS = frozenset({ + 'pcapkit/const/reg/ethertype.py', + 'pcapkit/const/ipx/socket.py', +}) + + class UnregisteredMemberNameIsBareTests(unittest.TestCase): """#775's Q1 follow-up, the maintainer's ruling verbatim: *"Q1 - bare it is."* Asked whether the non-minting path should honour the registry's @@ -1178,15 +1229,12 @@ class UnregisteredMemberNameIsBareTests(unittest.TestCase): collision if these were ever minted instead of built unregistered. A call site still using ``extend_enum(...)`` instead of - ``_unregistered_member(...)`` at all -- the handful of still-minting - branches on :class:`~pcapkit.const.reg.ethertype.EtherType` and - :class:`~pcapkit.const.ipx.socket.Socket` that the ruling kept, plus - :class:`~pcapkit.const.mh.cga_type.CGAType`, plus ``register``/ - ``register_alias`` on every registry (including - :class:`~pcapkit.const.reg.apptype.apptype.AppType`'s own, added by PR 2) - -- is out of scope and untouched by this sweep entirely, since it never - calls ``_unregistered_member`` in the first place; minting there is the - explicit, caller-named path the sweep is not about. + ``_unregistered_member(...)`` at all -- :class:`~pcapkit.const.mh. + cga_type.CGAType`, plus ``register``/``register_alias`` on every registry + (including :class:`~pcapkit.const.reg.apptype.apptype.AppType`'s own, + added by PR 2) -- is out of scope and untouched by this sweep entirely, + since it never calls ``_unregistered_member`` in the first place; minting + there is the explicit, caller-named path the sweep is not about. (Corrected from an earlier draft of this docstring, which estimated "~92 registries still mint" and named ``pcapkit.const.mh`` and @@ -1194,6 +1242,25 @@ class UnregisteredMemberNameIsBareTests(unittest.TestCase): apply; the AST walk in the module docstring above measured the real figure at 89.) + GitHub issue #775's final round converts the last 53 minting branches -- + all 52 of :class:`~pcapkit.const.reg.ethertype.EtherType`'s and + :class:`~pcapkit.const.ipx.socket.Socket`'s one -- and deliberately keeps + each one's hex-suffixed name unchanged (the owner's ruling, verbatim: + *"Preserve each branch's existing name argument exactly as the current + code produces it -- this change is about not registering, not about + renaming anything."*). That is the exact manufactured, value-suffixed + shape :func:`is_manufactured` exists to flag -- unlike ``'%s_unknown' % + namespace`` above, the substituted operand here really is ``value``, via + ``hex(value)[2:].upper().zfill(4)``. Flagging it anyway would be a false + positive in the sense that matters: the collision this check protects + against is two *minted* members sharing a name at different values, and + neither of these 53 calls ever mints, so nothing can collide regardless + of what the ``name`` argument looks like. :data:`HEX_SUFFIXED_NAME_ + EXEMPT_PATHS` below is the scoped fix -- a file-level exemption from the + sweep, not a change to :func:`is_manufactured` itself, which stays exactly + as tested against :data:`IS_MANUFACTURED_FIXTURES` and keeps flagging this + shape everywhere else it might appear. + """ def test_every_unregistered_member_call_passes_a_non_manufactured_name(self) -> None: @@ -1203,10 +1270,12 @@ def test_every_unregistered_member_call_passes_a_non_manufactured_name(self) -> offenders = [] # type: list[str] call_count = 0 + exempt_hits = 0 for path in sorted(const_root.rglob('*.py')): source = path.read_text() tree = ast.parse(source, filename=str(path)) + rel_path = str(path.relative_to(repo_root)).replace('\\', '/') for node in ast.walk(tree): if not isinstance(node, ast.Call): continue @@ -1218,6 +1287,9 @@ def test_every_unregistered_member_call_passes_a_non_manufactured_name(self) -> args = node.args name_arg = args[1] if len(args) > 1 else None if is_manufactured(name_arg): + if rel_path in HEX_SUFFIXED_NAME_EXEMPT_PATHS: + exempt_hits += 1 + continue segment = ast.get_source_segment(source, node) offenders.append(f'{path.relative_to(repo_root)}:{node.lineno}: {segment}') @@ -1230,10 +1302,16 @@ def test_every_unregistered_member_call_passes_a_non_manufactured_name(self) -> # for 244 total. #860 step 2's PR 2 then added the 766 ``_missing_`` # branches on AppType itself, the one more inside its own ``get()``, # and the one ``super()._unregistered_member(...)`` forward its own - # override makes -- 768 more, in one file, for 1012 total measured - # on this tree. - self.assertGreaterEqual(call_count, 1012, - f'expected at least 1012 _unregistered_member call sites, found {call_count}') + # override makes -- 768 more, in one file, for 1012 total. #775's + # final round then added the 52 on EtherType and the 1 on Socket -- + # 53 more, for 1065 total measured on this tree. + self.assertGreaterEqual(call_count, 1065, + f'expected at least 1065 _unregistered_member call sites, found {call_count}') + # And that the exemption is actually earning its keep, rather than a + # dead carve-out nothing reaches any more: exactly 53, matching the + # 52 EtherType range branches plus Socket's one. + self.assertEqual(exempt_hits, 53, + f'expected exactly 53 hex-suffixed-name exemptions to fire, found {exempt_hits}') self.assertEqual(offenders, [], 'found _unregistered_member call(s) with a manufactured name:\n' + '\n'.join(offenders)) @@ -1296,12 +1374,19 @@ def test_repeated_lookup_does_not_grow_members(self) -> None: #: :class:`~pcapkit.const.reg.ethertype.EtherType` and :class:`~pcapkit.const. -#: ipx.socket.Socket` are the two *mixed* registries: most of their -#: ``_missing_`` legitimately keeps minting (real attributed names), so the -#: blanket "no extend_enum left" sweep below does not apply to them -- -#: :class:`EtherTypeMixedMintTests` and :class:`IPXSocketMixedMintTests` cover -#: their converted rows specifically instead. -_MIXED_REGISTRIES = frozenset({('pcapkit.const.reg.ethertype', 'EtherType'), ('pcapkit.const.ipx.socket', 'Socket')}) +#: ipx.socket.Socket` used to be the two *mixed* registries: most of their +#: ``_missing_`` legitimately kept minting (real attributed names), so the +#: blanket "no extend_enum left" sweep below did not apply to them. +#: GitHub issue #775's final round converts every remaining branch on both, +#: so neither is mixed any more -- kept as an empty-by-construction frozenset +#: (rather than deleted outright) so the two subtractions below stay +#: self-documenting about *why* nothing is excluded now, and so a future +#: registry that goes back to being genuinely mixed has a named place to be +#: added again. :class:`EtherTypeMixedMintTests` and +#: :class:`IPXSocketMixedMintTests` (retitled in place, not renamed) now prove +#: their formerly-kept probe no longer mints either, alongside the rows that +#: already didn't. +_MIXED_REGISTRIES = frozenset() # type: frozenset[tuple[str, str]] class RulingConversionSourceTests(unittest.TestCase): @@ -1310,8 +1395,8 @@ class RulingConversionSourceTests(unittest.TestCase): so no lookup can exercise the branch either before or after this change) -- and, as a completeness check, for the 80 wholly-converted registries: none of their ``_missing_`` bodies may call :func:`~aenum.extend_enum` - any more. Excludes the two mixed registries -- see :data:`_MIXED_ - REGISTRIES` -- which keep some ``extend_enum`` calls by design.""" + any more. :data:`_MIXED_REGISTRIES` is empty as of #775's final round, so + nothing is excluded here any more -- see its own docstring.""" def test_no_reachable_gap_registries_no_longer_call_extend_enum(self) -> None: # EtherType now belongs to RULING_CONVERTED_WITH_REACHABLE_GAP: its @@ -1342,12 +1427,34 @@ def test_every_wholly_converted_registry_no_longer_calls_extend_enum(self) -> No source = inspect.getsource(cls._missing_) # type: ignore[attr-defined] self.assertNotIn('extend_enum', source) + def test_ethertype_and_socket_no_longer_call_extend_enum_either(self) -> None: + """The two former mixed registries, now that nothing excludes them: + GitHub issue #775's final round leaves no ``extend_enum`` call on + either, the same invariant :meth:`test_every_wholly_converted_ + registry_no_longer_calls_extend_enum` already proves for the other + 80 -- named separately because neither is actually a member of + :data:`RULING_CONVERTED_REGISTRIES` in Socket's case, so the loop + above alone would never reach it.""" + from pcapkit.const.ipx.socket import Socket + from pcapkit.const.reg.ethertype import EtherType + + for cls in (EtherType, Socket): + with self.subTest(registry=cls.__qualname__): + source = inspect.getsource(cls._missing_) # type: ignore[attr-defined] + self.assertNotIn('extend_enum', source) + self.assertIn('_unregistered_member', source) + class EtherTypeMixedMintTests(unittest.TestCase): - """:class:`~pcapkit.const.reg.ethertype.EtherType` is the ruling's mixed - case: ``DEC Unassigned`` and the "Old Xerox Experimental..." row convert, - every other attributed vendor block (a real company's own name for its - own code) keeps minting.""" + """:class:`~pcapkit.const.reg.ethertype.EtherType` was the ruling's mixed + case: ``DEC Unassigned`` and the "Old Xerox Experimental..." row converted + first, while every other attributed vendor block (a real company's own + name for its own code) kept minting. GitHub issue #775's final round + converts the rest too -- :meth:`test_formerly_attributed_vendor_block_ + no_longer_mints` below proves the one named probe (``Xyplex``) that used + to be this class's own regression guard for "still mints" now proves the + opposite, and the class is no longer actually mixed (retitled in place + rather than renamed, so history stays easy to follow).""" def setUp(self) -> None: snapshot = snapshot_modules(ISOLATED_PREFIXES) @@ -1383,10 +1490,20 @@ def test_unassigned_rows_do_not_mint(self) -> None: # regressed and 0x0101 started minting again). Nothing this test checked # is left unchecked; it is subsumed rather than replaced by a weaker test. - def test_attributed_vendor_block_still_mints(self) -> None: + def test_formerly_attributed_vendor_block_no_longer_mints(self) -> None: + """The regression guard this class used to carry the other way: this + exact probe (``Xyplex``, 0x0888) used to prove the ruling *kept* + minting a real attributed name; GitHub issue #775's final round + converts it, preserving the hex-suffixed name exactly as the crawler + always rendered it -- the owner's ruling, verbatim: *"Preserve each + branch's existing name argument exactly as the current code + produces it -- this change is about not registering, not about + renaming anything."* Same shape as + :meth:`test_unassigned_rows_do_not_mint` above, just for the one + probe that used to be the exception.""" from pcapkit.const.reg.ethertype import EtherType - value, name = ETHERTYPE_KEPT_PROBE + value, name = ETHERTYPE_FORMERLY_KEPT_PROBE self.assertNotIn(value, EtherType._value2member_map_) # type: ignore[attr-defined] before = len(EtherType.__members__) @@ -1394,16 +1511,20 @@ def test_attributed_vendor_block_still_mints(self) -> None: self.assertEqual(member.value, value) self.assertEqual(member.name, name) - self.assertEqual(before + 1, len(EtherType.__members__)) - self.assertIn(value, EtherType._value2member_map_) # type: ignore[attr-defined] - self.assertIs(EtherType(value), member) + self.assertEqual(before, len(EtherType.__members__)) + self.assertNotIn(value, EtherType._value2member_map_) # type: ignore[attr-defined] + + second = EtherType(value) + self.assertEqual(member, second) + self.assertIsNot(member, second) class IPXSocketMixedMintTests(unittest.TestCase): - """:class:`~pcapkit.const.ipx.socket.Socket` is the ruling's other mixed + """:class:`~pcapkit.const.ipx.socket.Socket` was the ruling's other mixed case: ``Experimental`` and the three "who may claim this pool" allocation- - policy labels convert; ``Registered by Xerox`` -- a real ownership fact -- - keeps minting.""" + policy labels converted first, while ``Registered by Xerox`` -- a real + ownership fact -- kept minting. GitHub issue #775's final round converts + it too; see :meth:`test_registered_by_xerox_no_longer_mints` below.""" def setUp(self) -> None: snapshot = snapshot_modules(ISOLATED_PREFIXES) @@ -1425,10 +1546,17 @@ def test_unassigned_rows_do_not_mint(self) -> None: self.assertEqual(before, len(Socket.__members__)) self.assertNotIn(value, Socket._value2member_map_) # type: ignore[attr-defined] - def test_registered_by_xerox_still_mints(self) -> None: + def test_registered_by_xerox_no_longer_mints(self) -> None: + """The regression guard this class used to carry the other way: this + exact probe (``Registered by Xerox``, 0x0010) used to prove the + ruling *kept* minting a real ownership fact; GitHub issue #775's + final round converts it, preserving the hex-suffixed name exactly as + the crawler always rendered it -- same ruling, same reasoning as + :meth:`EtherTypeMixedMintTests. + test_formerly_attributed_vendor_block_no_longer_mints`.""" from pcapkit.const.ipx.socket import Socket - value, name = IPX_SOCKET_KEPT_PROBE + value, name = IPX_SOCKET_FORMERLY_KEPT_PROBE self.assertNotIn(value, Socket._value2member_map_) # type: ignore[attr-defined] before = len(Socket.__members__) @@ -1436,9 +1564,12 @@ def test_registered_by_xerox_still_mints(self) -> None: self.assertEqual(member.value, value) self.assertEqual(member.name, name) - self.assertEqual(before + 1, len(Socket.__members__)) - self.assertIn(value, Socket._value2member_map_) # type: ignore[attr-defined] - self.assertIs(Socket(value), member) + self.assertEqual(before, len(Socket.__members__)) + self.assertNotIn(value, Socket._value2member_map_) # type: ignore[attr-defined] + + second = Socket(value) + self.assertEqual(member, second) + self.assertIsNot(member, second) #: The 5 (of #860's original 9 bespoke) classes step 2's PR 1 brought onto diff --git a/tests/const/test_const_ethertype_862_unit.py b/tests/const/test_const_ethertype_862_unit.py index 31af665f82..409e9372ce 100644 --- a/tests/const/test_const_ethertype_862_unit.py +++ b/tests/const/test_const_ethertype_862_unit.py @@ -166,6 +166,16 @@ def test_unrelated_non_overlapping_range_keeps_table_order(self) -> None: def test_both_ranges_still_render_with_their_own_body(self) -> None: # Reordering must not lose either range or swap their bodies. + # + # NOTE: the second assertion used to check for the ``extend_enum`` + # form ("IEEE802.3 Length Field" was one of the ranges GitHub issue + # #775's original ruling left minting). #775's final round converts + # every remaining range branch on this crawler -- see + # tests/const/test_const_enum_no_mint.py's own docstring -- so this + # now checks for the ``_unregistered_member`` form instead, keeping + # the hex-suffixed name exactly as before; only the registration + # mechanism changed, per the owner's ruling that this is "not about + # renaming anything". _enum, miss = self._process_fixture() text = '\n'.join(miss) @@ -175,10 +185,11 @@ def test_both_ranges_still_render_with_their_own_body(self) -> None: text, ) self.assertIn( - "return extend_enum(cls, 'IEEE802_3_Length_Field_0x%s' " - "% hex(value)[2:].upper().zfill(4), value)", + "return cls._unregistered_member(value, 'IEEE802_3_Length_Field_0x%s' " + "% hex(value)[2:].upper().zfill(4))", text, ) + self.assertNotIn('extend_enum', text) class EtherTypeGeneratorGeneralOrderingTests(unittest.TestCase): diff --git a/tests/const/test_const_registry_protocol.py b/tests/const/test_const_registry_protocol.py index 443199f5fc..ab3c3c0eba 100644 --- a/tests/const/test_const_registry_protocol.py +++ b/tests/const/test_const_registry_protocol.py @@ -1436,12 +1436,14 @@ def test_the_issues_own_case_no_longer_mints_and_raises_instead(self) -> None: ``0x1234`` (4660) falls in no ``_missing_`` range, so the lookup falls to ``default``; ``0x0888`` (2184) falls inside the ``Xyplex`` - range #775's ruling deliberately kept minting -- but only when - reached as a ``key``. Fails on the pre-#864 tree for that reason; - passes here because ``default`` no longer reaches ``cls(default)`` - at all, so ``0x0888`` not being an *already-registered* value - propagates the ``0x1234`` lookup's own failure instead of minting - anything.""" + range #775's *original* ruling deliberately kept minting at the + time -- but only when reached as a ``key``. (#775's final round has + since converted that range too; see + :meth:`GetDefaultNoMintTests.test_key_path_minting_is_preserved`.) + Fails on the pre-#864 tree for that reason; passes here because + ``default`` no longer reaches ``cls(default)`` at all, so ``0x0888`` + not being an *already-registered* value propagates the ``0x1234`` + lookup's own failure instead of minting anything.""" from pcapkit.const.reg.ethertype import EtherType before = len(EtherType.__members__) @@ -1457,23 +1459,37 @@ def test_the_issues_own_case_no_longer_mints_and_raises_instead(self) -> None: self.assertNotIn('Xyplex_0x0888', EtherType.__members__) def test_key_path_minting_is_preserved(self) -> None: - """No-change guard: ``EtherType.get(0x0888)`` -- no ``default`` at - all -- still mints ``Xyplex_0x0888`` through its own ``_missing_``, - exactly as before. Ruling one permits this explicitly (*"get should - not mint unless it falls through the _missing_'s minted ranges"*), - and #864 closes only the ``default`` path, so this passes on both - the pre- and post-#864 tree -- ``key`` resolution is untouched.""" + """Originally a no-change guard: ``EtherType.get(0x0888)`` -- no + ``default`` at all -- used to mint ``Xyplex_0x0888`` through its own + ``_missing_``, back when ruling one's mint/unmint criterion + (*"get should not mint unless it falls through the _missing_'s + minted ranges"*) still classed ``Xyplex`` as a kept-minting, + real-attributed-name range. GitHub issue #775's final round converts + that range (and every other one still minting on ``EtherType``/ + :class:`~pcapkit.const.ipx.socket.Socket`) to :meth:`~pcapkit.corekit. + enum.EnumRegistry._unregistered_member`, preserving the hex-suffixed + name unchanged -- so ``key`` resolution through ``get()`` now goes + through the same non-minting path ``_missing_`` always did for this + value, and the guard flips to prove that rather than the opposite. + #864's own point -- that ``default`` never reaches ``_missing_`` at + all -- is unaffected either way.""" from pcapkit.const.reg.ethertype import EtherType before = len(EtherType.__members__) self.assertNotIn('Xyplex_0x0888', EtherType.__members__) + self.assertNotIn(0x0888, EtherType._value2member_map_) # type: ignore[attr-defined] result = EtherType.get(0x0888) self.assertEqual(result.name, 'Xyplex_0x0888') self.assertEqual(result.value, 0x0888) - self.assertIn('Xyplex_0x0888', EtherType.__members__) - self.assertEqual(before + 1, len(EtherType.__members__)) + self.assertNotIn('Xyplex_0x0888', EtherType.__members__) + self.assertNotIn(0x0888, EtherType._value2member_map_) # type: ignore[attr-defined] + self.assertEqual(before, len(EtherType.__members__)) + + second = EtherType.get(0x0888) + self.assertEqual(result, second) + self.assertIsNot(result, second) def test_default_still_resolves_when_registered_int(self) -> None: """The ruling's other half: a ``default`` that *is* already a member diff --git a/tests/vendor/test_ipx_socket_unit.py b/tests/vendor/test_ipx_socket_unit.py index f6401cda5f..b502851766 100644 --- a/tests/vendor/test_ipx_socket_unit.py +++ b/tests/vendor/test_ipx_socket_unit.py @@ -102,9 +102,9 @@ #: the value because the value alone cannot tell a live range branch from a #: masked one: every branch of the generated ``_missing_`` returns ``value``, #: so only the name says which branch ran -- for ``Registered by Xerox``, -#: which still mints, that name still carries the value as a numeric suffix; -#: asserting the value alone would pass just as happily with the range -#: branches deleted or reordered. +#: that name still carries the value as a numeric suffix even though it no +#: longer mints (see below); asserting the value alone would pass just as +#: happily with the range branches deleted or reordered. #: #: Before GitHub issue #841 was fixed, three of these were unreachable -- #: ``(0x0001, 0x0BB8)`` masked ``(0x0020, 0x003F)``, and ``(0x0BB9, 0xFFFF)`` @@ -113,13 +113,18 @@ #: ``0xFFFF`` all resolved under the wrong wide range's name. See #: :data:`pcapkit.vendor.ipx.socket.RANGES` for the reordering that fixed it. #: -#: GitHub issues #775/#847's mint-criterion ruling then converted four of the -#: five ranges -- ``Experimental``, ``Dynamically Assigned Socket Numbers``, -#: ``Statically Assigned Socket Numbers`` and ``Dynamically Assigned`` each -#: name an allocation *policy* for the pool rather than a specific assignment, -#: so they no longer mint and their bare name carries no numeric suffix. -#: ``Registered by Xerox`` is the one that stayed: it states a real ownership -#: fact, not a status placeholder, so it still mints exactly as before. +#: GitHub issue #775/#847's original mint-criterion ruling then converted four +#: of the five ranges -- ``Experimental``, ``Dynamically Assigned Socket +#: Numbers``, ``Statically Assigned Socket Numbers`` and ``Dynamically +#: Assigned`` each name an allocation *policy* for the pool rather than a +#: specific assignment, so they no longer mint and their bare name carries no +#: numeric suffix. ``Registered by Xerox`` was the one that stayed at the +#: time, on the theory that it states a real ownership fact rather than a +#: status placeholder -- #775's *final* round converts it too, keeping its +#: hex-suffixed name unchanged (the owner's ruling: preserve the existing +#: name argument exactly, this is about not registering, not about renaming), +#: so all five ranges resolve through :meth:`~pcapkit.corekit.enum. +#: EnumRegistry._unregistered_member` now and none of them mint. EXPECTED_MISSING_NAMES = { 0x0000: 'Unspecified', # a defined member 0x0001: 'Routing_Information_Packet', # a defined member