Repository navigation
refactor(corekit): normalise sentinel object names to SCREAMING_SNAKE #937
Description
The four sentinel objects in pcapkit/corekit/sentinels.py carry three different casings, while all four types already follow CamelCase + Type. Normalise the objects to SCREAMING_SNAKE.
The owner's ruling, verbatim (from #719):
take SCREAMING_SNAKE and accept the breaking change (no backport needed). we can change
_ABSENTtoABSENTjust document it as private type/class in the documentation and not for public use is enough.
So:
| current | becomes |
|---|---|
NoValue |
NO_VALUE |
_Absent |
ABSENT — leading underscore dropped |
_AbsentType |
AbsentType |
NULL, NO_DEFAULT, NullType, NoValueType, NoDefaultType |
unchanged |
No deprecation alias. The break is accepted deliberately — NoValue is in pcapkit.corekit.fields.field.__all__ as of #911, so import * callers are affected.
Blast radius, measured (grep -rnw over pcapkit/ and tests/):
NoValue 109 references across 21 files
NoValueType 55 references across 13 files
_Absent 41 references across 5 files
_AbsentType 26 references across 5 files
plus docs/source/pcapkit/corekit/fields/{field,index,misc}.rst, docs/source/contributing/conventions.rst, and the sentinels.rst page that landed in #936.
Three consequences worth handling explicitly, not incidentally:
- Dropping the underscore makes
ABSENT/AbsentTypelook public to tooling. They must stay out of__all__—test_sentinel_exports_unit.py::test_the_private_sentinel_is_exported_neither_waypins that, and should keep pinning it under the new names. Privacy becomes documentation-only, per the ruling, so Sphinx will now pick them up where_-prefixed names were hidden; decide whethersentinels.rstdocuments them as explicitly-private or keeps omitting them. docs(corekit): add the sentinels API page so conventions.rst references resolve (#934) #936 omitted_Absentbecause it was underscore-private, so that reasoning needs restating either way. conventions.rstneeds an object-naming rule added.:152today states only "Keep the sentinel object's type class naming as<SENTINEL>Type" — it governs the type, never the object, which is the gap that let them diverge. The new rule belongs there._expected_type_nameintest_sentinel_exports_unit.pyshould collapse. Itsbare.isupper()branch and its leading-underscore branch both exist only to cope with the inconsistency; after this,<SENTINEL>Typeis one mechanical rule and the helper's special cases become dead.
Blocked on #932, which owns pcapkit/corekit/enum.py (a NO_DEFAULT/sentinel re-export shim), docs/source/contributing/conventions.rst, and tests/protocols/internet/test_mh_unit.py — all three in this change's path. Starts once #932 merges.
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
Activity
refactor(corekit,protocols): normalise the four sentinel objects to S…
refactor(corekit,protocols): normalise the four sentinel objects to S…
JarryShaw commentedon Sep 30, 2026
Closing: the rename is on
main, verified rather than assumed. #939 merged as part of83c7552b8;mainnow carriesABSENT = AbsentType()atpcapkit/corekit/sentinels.py:489andNO_VALUEthroughout.All three renames landed as ruled —
NoValue→NO_VALUE,_Absent→ABSENT,_AbsentType→AbsentType, with no deprecation alias, per "take SCREAMING_SNAKE and accept the breaking change (no backport needed)".The three consequences this issue flagged were each handled rather than left to chance:
ABSENT/AbsentTypeare documented as expressly private onsentinels.rst, with the ruling quoted — not omitted. Omission would have been underscore-hiding by another name, which is what dropping the underscore moved away from. Both stay out of every__all__;sentinels.__all__is['NULL','NO_VALUE','NO_DEFAULT'],field.__all__is['NO_VALUE','Field'],protocol.__all__is['ProtocolBase'].conventions.rstgained the object-naming rule it previously lacked — it had governed only the type class, which is the gap that let three casings diverge._expected_type_namecollapsed to one mechanical rule, with strength preserved by a new regex test pinning^[A-Z][A-Z0-9_]*$across all four objects independently of type derivation.Two findings from the work worth keeping, both reusable:
isortreordered five files becauseNO_VALUEsorts beforeField/FieldBaseunderorder_by_type— a pure case change moving imports is not obvious. Andpylint'scyclic-importoutput is nondeterministic: 236 differing lines across two runs of the unmodified baseline, soR0401churn can never be attributed to a diff.One thing that was my error, not this work's: I declared
tests/protocols/internet/test_mh_unit.pyoff-limits to the worker after measuring "zero sentinel references inmh.py" — the source, never the test. The test had three, and #939's first head broke all five Python legs withImportError: cannot import name 'NoValue'. The worker stopped and reported rather than crossing the boundary I drew, which was right.docs(contributing): sweep the conventions pages for accuracy and conc…
docs(pcapkit,ci): cite the issue a defect belongs to, not the pull re…
docs(corekit): cite #719, not #937, for the AbsentType ruling
docs(tests): re-point six ruling citations at their actual threads, per
docs(pcapkit,ci): cite the issue a defect belongs to, not the pull re…
docs(corekit): state the sentinel rulings instead of quoting them
docs(corekit): state the sentinel rulings instead of quoting them