Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ Which bases an IPv6 extension header names

Every IPv6 extension header in this package subclasses
:class:`~pcapkit.protocols.internet.ipv6_ext.IPv6_Ext`. Some name a **second** base
as well, and which ones do is a ruling rather than an accident. The owner's words,
on `#924 <https://github.com/JarryShaw/PyPCAPKit/pull/924>`__:

I think on subclassing, we might wanna keep this convention: if the IPv6
extension header is only usable as an extension header, then it only inherit
from ``IPv6_Ext``, like ``IPv6_Frag``; but if it is useable as a standalone
protocol itself, then it herit from both ``IPv6_Ext`` and ``Internet`` (or
``IPsec``), like ``ESP``.
as well, and which ones do is a ruling rather than an accident. The owner ruled on
`#924 <https://github.com/JarryShaw/PyPCAPKit/pull/924>`__ that a header usable *only*
as an extension header inherits ``IPv6_Ext`` and nothing else -- ``IPv6_Frag`` being
the example -- while one that is usable as a standalone protocol in its own right
inherits both ``IPv6_Ext`` and ``Internet`` (or ``IPsec``), as ``ESP`` does.

The family as it stands:

Expand Down Expand Up @@ -40,7 +37,7 @@ The family as it stands:

:class:`~pcapkit.protocols.internet.ipsec.IPsec` is itself an
:class:`~pcapkit.protocols.internet.internet.Internet` subclass, which is the
parenthetical *"(or* ``IPsec``\ *)"* in the ruling: naming it satisfies the
parenthetical ``IPsec`` alternative in the ruling: naming it satisfies the
convention, and it is the right second base for a header whose standalone form is an
IPsec one.

Expand Down Expand Up @@ -74,11 +71,10 @@ whether the same header is also a protocol in its own right.
The operative test is what the RFCs say
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

So the census is read out of the specifications, on the owner's instruction:

So my suggestion is to read through the RFCs to figure out any of the defined
IPv6 extension headers are extension header only or standalone protocol as well.
Then we can decide if they should inherit only ``IPv6_Ext`` or additional bases.
So the census is read out of the specifications, on the owner's instruction: work
through the RFCs to establish, for each defined IPv6 extension header, whether it is
extension-header-only or a standalone protocol as well, and decide from that whether it
inherits ``IPv6_Ext`` alone or names additional bases.

And the limb that decides is **whether a primary source shows the header carried
directly as an IPv4 payload**:
Expand Down Expand Up @@ -139,9 +135,9 @@ The class arrived as ``IPv6_GenericExt``, a fallback parser for an unrecognised
extension header (`#891 <https://github.com/JarryShaw/PyPCAPKit/issues/891>`__), and
`#917 <https://github.com/JarryShaw/PyPCAPKit/issues/917>`__ merged that role with
the shared-base role into one class under the shorter name. No compatibility alias
was left behind, and that was deliberate. The owner's ruling:

No more ``IPv6_GenericExt`` name. Its an intermediate state and never released.
was left behind, and that was deliberate. The owner ruled that the
``IPv6_GenericExt`` name goes for good: it was an intermediate state, and it was never
released.

The reasoning is what makes it safe rather than merely decided: the old name existed
on ``main`` from ``b3551cb63`` to ``93cf940b3`` -- under four hours on one day, and
Expand Down
7 changes: 4 additions & 3 deletions docs/source/contributing/conventions/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ House Conventions
when a question is answered in a way the code cannot express on its own -- a
classification, a naming rule, a deliberate asymmetry -- it is written onto
the page that covers it in the same change that implements it, rather than
left in the issue for the next contributor to find. The owner's standing
ask, on `#918 <https://github.com/JarryShaw/PyPCAPKit/issues/918>`__: *"And
any future conventions to be settled - document them as well."*
left in the issue for the next contributor to find. That is the owner's
standing ask on
`#918 <https://github.com/JarryShaw/PyPCAPKit/issues/918>`__: every
convention settled from here on gets documented here as well.

.. toctree::
:maxdepth: 1
Expand Down
19 changes: 9 additions & 10 deletions docs/source/contributing/conventions/mint-criterion.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@ a given range gets is a **design decision, not a style preference**:
The criterion
~~~~~~~~~~~~~

The test, in the maintainer's words:
The test, paraphrased from the maintainer's ruling: does the upstream registry treat
the label as the final, concrete assigned name (**mint**), or only as a notation for a
human reading the table (**unmint**)?

Is this considered as the final concrete assigned name (**mint**), or just a
notation for the readers (**unmint**)?

Settled on `#847 <https://github.com/JarryShaw/PyPCAPKit/issues/847>`__ and confirmed
as "a core concept of the ruling" on
`#775 <https://github.com/JarryShaw/PyPCAPKit/issues/775>`__.
Settled on `#847 <https://github.com/JarryShaw/PyPCAPKit/issues/847>`__ and reaffirmed
on `#775 <https://github.com/JarryShaw/PyPCAPKit/issues/775>`__ as a core concept of
the ruling.

So the question to ask of a range is **what the upstream registry actually did**, not
what the generated code happens to look like:
Expand Down Expand Up @@ -74,9 +73,9 @@ Why the company names mint

The ethertype case looks like an exception to the rule and is not. The maintainer's
reasoning, settled on `#775 <https://github.com/JarryShaw/PyPCAPKit/issues/775>`__ after
being raised on `#847 <https://github.com/JarryShaw/PyPCAPKit/issues/847>`__:

Proprietary protocols won't have public names so company names serve this purpose.
being raised on `#847 <https://github.com/JarryShaw/PyPCAPKit/issues/847>`__: a
proprietary protocol will never have a public name, so the company name is what serves
that purpose in its place.

So the company name is not a note *about* the code -- it is the best name that will ever
exist *for* it, which makes it the final concrete assigned name under the test above.
Expand Down
61 changes: 29 additions & 32 deletions docs/source/contributing/conventions/registry-protocol.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,17 @@ parent, and the line between them is whether the enumeration may *grow*:
``_extend``, ``_unregistered_member``
=================================================== ==============================================================

The owner's ruling, verbatim: *"they may subclass a bare base enum from
pcapkit.corekit.enum - where EnumRegistry subclasses it for using in the other
mutable ones."* So a **closed** set inherits :class:`~pcapkit.corekit.enum.EnumLookup`
The owner ruled on #877 that a registry may subclass a bare base enum out of
:mod:`pcapkit.corekit.enum`, with :class:`~pcapkit.corekit.enum.EnumRegistry`
subclassing that base in turn for use by the mutable ones. So a **closed** set
inherits :class:`~pcapkit.corekit.enum.EnumLookup`
directly and is never handed a ``register`` it would have to refuse; an **open**
registry inherits :class:`~pcapkit.corekit.enum.EnumRegistry` exactly as before.

What settled the split is the owner's own second thought about carrying ``register``
on the base: *"if it carries ``register``, then why not ``register_alias``. We might
be creating a bad ruling."* Following that through leaves
on the base: if the base carries ``register``, there is no principled reason for it
not to carry ``register_alias`` as well, and the ruling would be the worse for it.
Following that through leaves
:class:`~pcapkit.corekit.enum.EnumRegistry` holding only three methods, too thin to
justify a second class -- so the two tiers collapse into one, which is the opposite of
what was ruled.
Expand All @@ -55,8 +57,8 @@ it was -- ``LinkType -> EnumRegistry -> EnumLookup -> IntEnum -> int`` -- so
itself and broken ``int``, ``str`` and flag registries at once.

:meth:`~pcapkit.corekit.enum.EnumLookup._validate_value` is what the base carries
*instead* of ``register``, and it answers the owner's other requirement: *"there must
be some sort of range validation logic for the inherited classes to hook in."* The
*instead* of ``register``, and it answers the owner's other requirement: the base has
to offer range-validation logic for its inheriting classes to hook into. The
base implementation accepts everything; an override states a range, in the shape the
generated registries currently spell by hand in ``_missing_``:

Expand Down Expand Up @@ -118,13 +120,11 @@ Three things about it are easy to get wrong:
What a Failed Lookup Raises
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Two rules govern it, and they pull in opposite directions on purpose. The owner's
ruling, verbatim, on
`#923 <https://github.com/JarryShaw/PyPCAPKit/issues/923>`__:

Either ``ValueError`` or ``KeyError``, that's depending on how stdlib's ``Enum``
would raise on these circumstances. And we should raise one from
``pcapkit.utilities.exceptions`` rather builtin exceptions.
Two rules govern it, and they pull in opposite directions on purpose. The owner ruled
on `#923 <https://github.com/JarryShaw/PyPCAPKit/issues/923>`__ that the choice between
:exc:`ValueError` and :exc:`KeyError` follows whichever stdlib's :class:`~enum.Enum`
would raise in the same circumstance, and that whichever it is comes from
:mod:`pcapkit.utilities.exceptions` rather than from builtins.

So the **provenance** is in-library and the **shape** is stdlib's:

Expand All @@ -148,8 +148,8 @@ one into the other is exactly what #923 retired, and it was retired in three pla
at once: ``TransportProtocol.get`` and ``Criticality.get`` had each turned the
base's :exc:`KeyError` into a :exc:`ValueError`, and
``FastBindingAcknowledgmentStatus.get`` raised
:exc:`~pcapkit.utilities.exceptions.EnumValueError` for a name miss so that "the
two ways of getting it wrong reported identically".
:exc:`~pcapkit.utilities.exceptions.EnumValueError` for a name miss, so that the two
ways of getting it wrong reported identically.

One asymmetry between the two is deliberate and is **not** visible from the
exception class: the **name** miss is raised quietly
Expand Down Expand Up @@ -286,17 +286,16 @@ what it adds to the base, and goes when the answer is nothing.**
Case Sensitivity Is RFC-Directed
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The rule, in the owner's own wording on
`#877 <https://github.com/JarryShaw/PyPCAPKit/issues/877>`__:

if RFC states the values are case-insensitive, then our enum should also treat them
that way. otherwise, we should treat them case sensitive.
The rule, as the owner ruled it on
`#877 <https://github.com/JarryShaw/PyPCAPKit/issues/877>`__: where the RFC states the
values are case-insensitive, the enumeration treats them that way too; otherwise it
treats them as case-sensitive.

And the reason a registry's spelling is never quietly normalised, from the same thread:
*"enum should honour and keep their original writings as in the registrars. case
in-sensitivity only applies to certain selected ones, where logically it makes sense
(like ``TransportProtocol``) and/or RFC documentation itself recognises them as
case-insensitive (like, maybe, FTP/HTTP commands)."*
an enumeration honours and keeps the original writing the registrar used, and
case-insensitivity applies only to the selected registries where it makes logical sense
-- ``TransportProtocol`` being one -- or where the RFC documentation itself recognises
the values as case-insensitive, FTP and HTTP commands being the likely candidates.

So :meth:`~pcapkit.corekit.enum.EnumLookup.get` is **case-sensitive**, and that is the
default every enumeration gets. Case-insensitivity is a per-class ``get`` override that
Expand Down Expand Up @@ -330,11 +329,9 @@ The ruling above leaves one question open, and
`#903 <https://github.com/JarryShaw/PyPCAPKit/issues/903>`__ settled it: does a
specification have to state a **comparison rule** for a registry to be treated
case-insensitively, or does it also count when the authorities merely **disagree
about spelling**? The owner's answer, verbatim:

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.
about spelling**? The owner ruled for the lenient reading, with ``TransportProtocol``
as his own example: upper and lower casings are used throughout the RFCs and IANA's own
data, and that mixed usage is itself an indication of case-insensitivity.

So the test a new registry has to pass has **two limbs**, and satisfying either one
justifies case-insensitivity:
Expand All @@ -358,8 +355,8 @@ The Audit, per Class
~~~~~~~~~~~~~~~~~~~~

`#903 <https://github.com/JarryShaw/PyPCAPKit/issues/903>`__'s sweep, so that a
registry added later has something to check itself against. The owner's scope for it,
verbatim: *"we should audit all registries and then decide if case (in)sensitive."*
registry added later has something to check itself against. The owner set its scope:
audit every registry first, and decide case-sensitivity per registry from that.

The population it covers, with the counting convention spelled out because the
figures move: **127** :class:`~pcapkit.corekit.enum.EnumRegistry` subclasses, every
Expand Down
28 changes: 14 additions & 14 deletions docs/source/contributing/conventions/sentinel-convention.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ Naming a sentinel
A *sentinel* here is a module-level singleton whose only job is to be recognised by
identity -- ``value is SENTINEL`` -- so that it can never be confused with a value a
caller might legitimately pass. The house rule, from the maintainer, covers the type:

Keep the sentinel object's type class naming as ``<SENTINEL>Type``.
the sentinel object's type class is named ``<SENTINEL>Type``.

That is, the class takes the instance's name in CamelCase with ``Type`` appended. It
says nothing about the **object**'s own name, which is what let three casings diverge
with no rule naming any of them wrong. GitHub issue #937 closed that gap, verbatim:
*"take SCREAMING_SNAKE and accept the breaking change (no backport needed)."* So the
object is named in SCREAMING_SNAKE, and the type-naming rule above derives from it
with no rule naming any of them wrong. GitHub issue #937 closed that gap: the owner
ruled for SCREAMING_SNAKE and accepted the resulting breaking change outright, with no
backport. So the object is named in SCREAMING_SNAKE, and the type-naming rule above
derives from it
mechanically -- title-case each underscore-separated word and append ``Type``, no
per-sentinel exception needed. The four in the tree follow it:

Expand All @@ -40,9 +40,9 @@ per-sentinel exception needed. The four in the tree follow it:
All four used to live beside the one class that used them --
:mod:`pcapkit.corekit.module`, :mod:`pcapkit.corekit.fields.field`,
:mod:`pcapkit.corekit.enum` and :mod:`pcapkit.protocols.protocol` respectively.
GitHub issue #911's housing ruling, verbatim -- *"Okay one module for all four it
is."* -- moved the four definitions into the single shared module the table now
names; each original module keeps a re-export so every existing
GitHub issue #911's housing ruling -- one module for all four -- moved the four
definitions into the single shared module the table now names; each original module
keeps a re-export so every existing
``from <module> import <name>`` keeps working, including the
``if TYPE_CHECKING:``-only imports of the types.

Expand All @@ -59,9 +59,9 @@ every caller for no further gain.
The rename also dropped the **leading underscore** ``_Absent``/``_AbsentType`` used to
carry. ``ABSENT`` is private -- it is read in ``_declared_keywords`` and discarded
there, never leaving :mod:`pcapkit.protocols.protocol` -- and the underscore used to be
the mechanical signal of that. The maintainer's ruling on #937, verbatim: *"we can
change* ``_ABSENT`` *to* ``ABSENT`` *just document it as private type/class in the
documentation and not for public use is enough."* So privacy is documentation-only from
the mechanical signal of that. The owner ruled on #937 that dropping it is fine, so
long as the documentation states that the type and class are private and not for public
use -- that alone is enough. So privacy is documentation-only from
here on, carried by this paragraph and by
:class:`~pcapkit.corekit.sentinels.AbsentType`'s own docstring
(:file:`pcapkit/corekit/sentinels.py`, line 439), which still says so:
Expand All @@ -78,9 +78,9 @@ filtered on capitalised names did not see ``_Absent`` -- and that history does n
change now that nothing in the name itself marks it out. When adding a sentinel, add
it here whether or not it is public.

What reaches users is the **object only**. The maintainer's ruling: *"we should ONLY
export the objects (like* ``NULL`` *) to users"* -- so a public sentinel names its
instance in its module's ``__all__`` and leaves the type out of it (GitHub issue #911).
What reaches users is the **object only**. The owner ruled on GitHub issue #911 that
the objects alone -- ``NULL`` and its siblings -- are exported to users, so a public
sentinel names its instance in its module's ``__all__`` and leaves the type out of it.
The type stays importable by its dotted path, for an annotation or an ``is`` guard; it
is ``import *`` that no longer offers it. A private sentinel such as ``ABSENT`` is in
neither, which is what private means here -- dropping its leading underscore did not
Expand Down
22 changes: 11 additions & 11 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ cli = [ "emoji" ]
# for ESP payload decryption, c.f. pcapkit.protocols.internet.esp
crypto = [ "cryptography>=3.4" ]
# for NGAP decoding, c.f. pcapkit.protocols.application.ngap. Counted among
# ``all``'s *core addons* by the owner's ruling on #910 -- "everything that
# makes the library itself work at full functionality" -- even though the
# ``all``'s *core addons* by the owner's ruling on #910 -- everything needed for
# the library itself to work at full functionality -- even though the
# ``pycrate`` this needs is not a free addition:
#
# * Size. ``pycrate`` ships every specification it has ever compiled in one
Expand Down Expand Up @@ -221,15 +221,15 @@ PyPCAPFile = [ "pypcapfile; python_version < '3.12'" ]
# moved to ``dev`` instead. Either way, this extra's own requirements are what
# it is correct against, not whatever ``all`` happens to carry.
vendor = [ "requests[socks]", "beautifulsoup4[html5lib]", "pycrate" ]
# #910: narrowed to *core addons* only -- the owner's own words are "everything
# that makes the library itself work at full functionality", not "everything an
# end user might ever want". An earlier ruling on the same issue read the
# latter and added ``pycrate`` on that basis; the ruling that stuck narrowed it
# again, this time by kind: ``cli``, ``crypto`` and ``pycrate`` (NGAP) are core
# addons and stay, and the four 3rd-party capture engines that used to be here
# -- ``dpkt``, ``scapy``, ``pyshark``, ``pypcapfile`` -- are "on demand for each
# one" like ``PyPCAP`` and ``PCAP_CT`` always were, and move out to their own
# extras below. Install one explicitly: ``pip install pypcapkit[DPKT]`` /
# #910: narrowed to *core addons* only -- the owner's ruling scopes this to
# everything needed for the library itself to work at full functionality, rather
# than everything an end user might conceivably want. An earlier ruling on the
# same issue read the latter and added ``pycrate`` on that basis; the ruling that
# stuck narrowed it again, this time by kind: ``cli``, ``crypto`` and ``pycrate``
# (NGAP) are core addons and stay, and the four 3rd-party capture engines that
# used to be here -- ``dpkt``, ``scapy``, ``pyshark``, ``pypcapfile`` -- are
# installed one at a time on demand, as ``PyPCAP`` and ``PCAP_CT`` always were,
# and move out to their own extras below. Install one explicitly: ``pip install pypcapkit[DPKT]`` /
# ``[Scapy]`` / ``[PyShark]`` / ``[PyPCAPFile]``.
#
# ``pypcap`` and ``pcap-ct``/``libpcap`` stay out for the reason they always
Expand Down
Loading
Loading