Repository navigation
test(const): audit every registry case sensitivity against its source specification #903
Description
Is your feature request related to a problem? Please describe.
Case sensitivity of registry lookups is currently decided ad hoc. Four registries override get and one helper case-folds, and until now nobody had checked whether each one's behaviour matches what its source specification actually says.
The ruling that governs it, from #877:
okay, then we've found the verdict. and that's the logic - if RFC states the values are case-insensitive, then our enum should also treat them that way. otherwise, we should treat them case sensitive.
and the follow-up that makes this its own piece of work:
on (2) - we should audit all registries and then decide if case (in)sensitive.
Describe the solution you'd like
Audit every registry under pcapkit/const/ plus the non-registry helper enums — 124 and 27 respectively by the last count — against the RFC or IANA registry its values come from, and record per registry whether the specification makes those values case-insensitive. Then apply the ruling: case-insensitive lookup only where the source says so, case-sensitive everywhere else.
The deliverable is the audit table plus the changes it implies, and the table belongs in docs/source/conventions.rst beside the ruling so the next registry added has something to check itself against.
What is already established, so it need not be re-derived
| class | behaviour | verdict |
|---|---|---|
pcapkit/const/ftp/command.py Command |
value.upper() at :104 |
correct, keep — RFC 959 §4.1: "The command codes are four or fewer alphabetic characters. Upper and lower case alphabetic characters are to be treated identically." |
pcapkit/const/http/method.py Method |
key.upper() at :232-242 |
defect — RFC 9110 §9.1: "The method token is case-sensitive because it might be used as a gateway to object-based systems with case-sensitive method names." Tracked as #896. |
helper TransportProtocol |
case-insensitive, load-bearing: pcapkit/const/reg/apptype/apptype.py:101-102 does key.lower() against __members__, and :2480 calls TransportProtocol.get(proto.lower()) |
open — the maintainer named this as his example of legitimate case-insensitivity, so if nothing backs it the ruling bites two real call sites |
pcapkit/const/pcapng/option_type.py OptionType |
get(key: 'int | str', …) |
open |
pcapkit/const/reg/apptype/apptype.py AppType |
get(cls, key: 'int', …), int-keyed |
open, probably moot |
Also settled: HTTP field names genuinely are case-insensitive (RFC 9110 §5.1), which is a different thing from method tokens and must not be conflated.
Method notes
Fetch RFC text with curl https://www.rfc-editor.org/rfc/rfcNNNN.txt and grep it — a WebFetch of these documents truncates before the relevant sections, which cost two wasted attempts.
Calling Cls(value) on a registry whose _missing_ mints mutates the class, so a probe is not a read: snapshot {m.value for m in Cls} first and use a throwaway process per registry.
Additional context
Depends on #877, whose phase 1 establishes the bare lookup base and records the convention. The base's get is case-sensitive by that ruling, so every case-insensitive registry becomes an explicit, justified override — which is what makes this audit actionable rather than academic.
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
Activity
JarryShaw commentedon Sep 29, 2026
Checkable blocker, recorded so this label is not taken on trust: #877 phase 1.
This audit's whole premise is that the bare lookup base's
getis case-sensitive by default, so that every case-insensitive registry becomes an explicit, justified override. Until that base exists, "is this registry case-insensitive on purpose" has no baseline to be measured against, and the audit would have to be redone once the base lands.Unblocks when #877 phase 1 merges. The two already-settled rows carry over and need no re-derivation:
ftp.command.Commandcorrect per RFC 959 §4.1,http.method.Methoda defect per RFC 9110 §9.1 (tracked as #896).JarryShaw commentedon Sep 29, 2026
Three of this issue's starting rows are already answered, and one of them contradicts the ruling that created it. Carried back from #877 phase 1, which completed the audit before I narrowed its scope. I have verified the load-bearing parts myself.
First, a count correction to this issue's own body: it is 125 registries, not 124. #890 (
5e7f91ebf) movedTLSKeyLabelfrom a hand-writtenStrEnumhelper inpcapkit/protocols/misc/pcapng.pyinto a generated registry atpcapkit/const/pcapng/tls_key_label.py, so one class changed columns. Measured on currentorigin/main: 125. I have been quoting 124 since before that merge. (The non-registry figure depends on whether nested classes likeFrameType.Flagsare counted — 19 by module-member walk, 26 including the six concrete nestedFlags— so I am not asserting a single number for it here.)pcapkit/const/pcapng/option_type.py— nothing to justify; it does no case folding at all.:263-264isif key in OptionType.__members__: return getattr(OptionType, key)— an exact match. It already conforms to the case-sensitive default. Verified directly. Close that row.reg/apptype/apptype.pyAppType.get— moot as suspected, but it inherits the question._dispatchrefuses a non-intkey outright, so no service-name lookup exists to fold. But:2480doesTransportProtocol.get(proto.lower()), so whatever is decided forTransportProtocolpropagates here. (Harmless redundancy noted: that site lowers, andTransportProtocol.getlowers again.)TransportProtocol— and this is the one for you, because nothing in an RFC backs its case-insensitivity. RFC 6335 governs (it defines the IANA registry the crawler reads atpcapkit/vendor/reg/apptype/apptype.py:942). Both of its case-insensitivity statements are explicitly about Service Names, not transport protocols:For the Transport Protocol field it says only "This field is currently limited to one or more of TCP, UDP, SCTP, and DCCP" — no comparison rule anywhere.
So under a strict reading of your ruling — "if RFC states the values are case-insensitive, then our enum should also treat them that way. otherwise, we should treat them case sensitive" —
apptype.py:101-102'skey.lower()has no RFC statement behind it, and you named this class as your example of legitimate case-insensitivity.There is a real adjacent justification, though it is a different claim: the two authorities disagree on the field's spelling. RFC 6335 spells it upper case throughout (§8.1.1 and the §10.2 registry templates read
| Transport Protocol | DCCP, SCTP, TCP, UDP |), while the live IANA CSV is exclusively lower case — measured over 14,536 rows:tcp6608,udp6357, empty 1467,sctp93,dccp11, zero upper-case. pcapkit's members follow IANA. So a caller holding the RFC's own spelling'TCP'would fail a case-sensitive lookup against a member namedtcp.The ask: does "the RFC says so" mean a comparison rule in the spec (strict —
TransportProtocolloses case-insensitivity and two call sites change), or does a documented spelling disagreement between the RFC and the registry also count (lenient — it keeps it, justified by that mismatch rather than by a case rule)? Markingneeds: decisionfor this one row only; the rest of the audit proceeds either way.An inverse finding worth this issue's attention: RFC 6335 §5.1 does make service names case-insensitive, and
AppType's members are service names — but no service-name lookup exists in the code to fold. So there is an RFC-sanctioned case-insensitivity that is simply unimplemented, which is the opposite shape from every other row here.And a trap for the implementation, measured rather than assumed:
Flagregistries do grow_value2member_map_on a composite lookup —_Flag.get(99)cachesfirst|second|96._member_names_and__members__stay untouched, so this is your ruling'sFlagcarve-out rather than minting, but a blanket "closed sets never grow" assertion will be false for flags.JarryShaw commentedon Sep 29, 2026
I say lenient. TransportProtocol for example should be case-insensitive. Upper or lower cases are being used everywhere in RFC and IANA themselves so that's an indication of case insensitivity.
JarryShaw commentedon Sep 29, 2026
Ruling recorded, verbatim:
So the criterion is settled and it is broader than "the spec states a comparison rule". A documented spelling inconsistency between the authorities is itself evidence of case-insensitivity — if RFC 6335 writes
TCPthroughout while the IANA registry writestcpexclusively, neither authority is treating case as significant, and a lookup that does would reject a caller holding the spec's own spelling.TransportProtocolkeeps its case-insensitivity,reg/apptype/apptype.py:101-102'skey.lower()stays, and the two dependent call sites are unaffected. The measurement that motivated the question stands as the justification rather than as a problem: RFC 6335 §8.1.1 and its §10.2 templates write the field upper case, while the live CSV is lower case in all 14,536 rows (tcp6608,udp6357, empty 1467,sctp93,dccp11, zero upper-case).needs: decisionremoved. Andblockedremoved too — that blocker discharged while this was waiting. #903 was blocked on #877 phase 1, which merged as #906 (c50f442db) at 04:54:03Z. SoEnumLookupnow exists, itsgetis case-sensitive by default, and every case-insensitive registry is an explicit override needing justification — which is exactly the baseline this audit needs to measure against. Worker dispatched.Where the audit now stands, with your ruling applied:
ftp.command.Commandvalue.upper()at:104TransportProtocolhttp.method.Methodkey.upper()pcapng.option_type.OptionTypereg.apptype.apptype.AppTypeTransportProtocol's behaviour via:2480That leaves the sweep over the remaining registries, which is what the worker is on. One shape to watch that the lenient reading makes more likely to recur: RFC 6335 §5.1 does explicitly make service names case-insensitive, and
AppType's members are service names — but no service-name lookup exists in the code to fold. An RFC-sanctioned case-insensitivity that is simply unimplemented is the opposite shape from every other row here, and under the lenient criterion it is a candidate to implement rather than to leave.fix(const,vendor,docs): audit registry case sensitivity, fold FEATCod…
fix(const,vendor,docs): audit registry case sensitivity, fold FEATCod…
fix(const,vendor,docs): audit registry case sensitivity, fold FEATCod…
docs(contributing): refer to the owner by role, not by gendered pronoun
docs(tests): second whole-repo pass over tests (#1103) (#1805)