Skip to content

const: generalise register_alias from AppType to every pcapkit.const enum #842

Closed

Description

@JarryShaw

Split out of #838 at the maintainer's direction. His framing, verbatim:

register_alias is an API on each Enum, and it only touches the concrete Enum
class it serves; AppType is the exception where it must goes to the underlying
transport protocol registry Enum class.

In this case, ETH will become an alias to IANA's default ETHERNET name and is
only register_alias'ed when PyShark being used. We might also check other PDML
attributes to extend other Wireshark's display-filter names as well.

Today it is bespoke to one registry. register_alias exists only on
AppType (pcapkit/const/reg/apptype/apptype.py, from #807). Its docstring scopes
itself deliberately to the per-transport port registry: TCP.register_alias(...) must
never touch UDP, so an alias registered on one transport cannot leak onto a transport
IANA never assigned it. It mints through extend_enum, which is the caller-named path
#775 deliberately preserves.

The design questions this has to answer:

  1. What does "this registry alone" mean for an enum that is not per-transport? On
    AppType the boundary is the transport class; elsewhere there is no analogue, so
    the generalised contract needs stating rather than inheriting.
  2. Does a generalised register_alias belong in the shared template
    (pcapkit/vendor/default.py), giving all 105 generated registries the method, or
    only where a caller needs it? The shared template reaches 26 registries directly;
    the other 79 carry their own process() or template — see Registries mint a permanent member for every unrecognised value: 1,169 extend_enum sites across 113 registries #775's census.
  3. AppType's override routes to the underlying transport registry. What is the hook
    that lets one registry opt into different routing without the template knowing
    about it?
  4. Lazy registration: ETH should only exist while pyshark is in use. What registers
    it, when, and does anything unregister it — a process-wide alias that outlives the
    engine that wanted it is the same class of surprise Registries mint a permanent member for every unrecognised value: 1,169 extend_enum sites across 113 registries #775 removed.
  5. Which other Wireshark display-filter names are worth aliasing, and from which PDML
    attribute are they read? pyshark/packet/layers/xml_layer.py takes the layer name
    from <proto name=…>; the maintainer suggests other attributes may carry more.

Not blocking #838, which carries a trivial local fix for the eth case per the
same ruling.

Activity

  1. added
    enhancementIssues requesting a new capability (set by the feature request template)
    designA design or decision issue: a pattern being decided rather than a defect or a request
    on Sep 27, 2026
  2. JarryShaw commented on Sep 27, 2026

    @JarryShaw
    OwnerAuthor
    1. For example, EhterType.register_alias will only touch EtherType itself, whilst AppType.register_alias will touch one of TCP/UDP/SCTP/DDCP/etc. depending on the *transport argument.
    2. i think it can exist in default, but will also have to live in every other pcapkit.vendor class who maintains their own template override.
    3. For non-AppType registry, register_alias only happens to the caller Enum class itself, no routing needed.
    4. i guess something down the path in pcapkit.toolkit.pyshark when we do the import pyshark call.
    5. needs answer for this and have them all registered as alias.
  3. JarryShaw commented on Sep 27, 2026

    @JarryShaw
    OwnerAuthor

    Recorded, verbatim:

    1. For example, EhterType.register_alias will only touch EtherType itself, whilst
      AppType.register_alias will touch one of TCP/UDP/SCTP/DDCP/etc. depending
      on the *transport argument.
    2. i think it can exist in default, but will also have to live in every other
      pcapkit.vendor class who maintains their own template override.
    3. For non-AppType registry, register_alias only happens to the caller Enum class
      itself, no routing needed.
    4. i guess something down the path in pcapkit.toolkit.pyshark when we do the
      import pyshark call.
    5. needs answer for this and have them all registered as alias.

    That settles 1-3 as a template question rather than a design one: the shared method in
    pcapkit/vendor/default.py does the no-routing case, AppType keeps its override, and
    the cost is the same shared-vs-bespoke split #775 measured — the bespoke vendor modules
    each need the method too.

    One consequence of (4) worth stating once, since you have ruled and I am not
    re-opening it.
    extend_enum mutates the class permanently for the process, so an
    alias registered at import pyshark time is not unregistered when the engine goes
    away: a program that runs the pyshark engine and then the scapy one will still have
    LinkType.ETH present. That is probably harmless — it resolves to the right member
    rather than a minted -1 — but it means "only when PyShark being used" is true of
    registration, not of lifetime.

    Worth knowing about the timing, and it helps: pcapkit/foundation/engines/pyshark.py:236
    imports pcapkit.toolkit.pyshark lazily inside the run method, not at package
    import. So registration happens the first time the pyshark engine actually runs, not
    when someone imports pcapkit — which is closer to engine-scoped than the phrasing
    suggests.

    (5) is in flight. A read-only investigation is enumerating what a PDML <proto>
    element actually carries and which filter names can be justified against Wireshark's
    dissector registrations. #838 meanwhile lands only the two that are unambiguous —
    eth → ETHERNET and tr → IEEE802_5 — and deliberately omits sll, fr, ip
    and ipv6, each of which is shared by two or three DLTs. I will post the full
    enumeration here when it lands.

  4. added
    wipWork in flight - a covering PR is open or an agent is actively on it
    on Sep 27, 2026
  5. JarryShaw commented on Sep 27, 2026

    @JarryShaw
    OwnerAuthor

    One consequence of (4)

    for this, it is expected and accepted by me. it's like import pyshark will load the module into cache and memory anyways, so having its aliases registered as permanent members of pcapkit.const is just part of the import's setup process. we dont have to overthink/overdesign on the lifecycle management thingy.

  6. JarryShaw commented on Sep 27, 2026

    @JarryShaw
    OwnerAuthor

    Design measurements in. Two findings change the plan, both verified by me.

    1. register_alias does not dispatch today — generalising means adding that, not
    preserving it.
    Your (1) says AppType.register_alias "will touch one of
    TCP/UDP/SCTP/DDCP depending on the *transport argument". Measured:

    inspect.signature(AppType.register_alias)  ->  (port: 'int', name: 'str')   # no transport param
    AppType.register_alias(80, 'zzz')          ->  ValueError: AppType holds no members;
                                                   register the alias on one of its
                                                   per-transport subclasses instead
    TCP.register_alias(80, 'my-www')           ->  <TCP.my-www: 80 [tcp] (aliases: http, www, www-http)>
    UDP: 6143 members before and after, 'my_www' absent
    

    It refuses on the base and works only when called on a subclass. get/get_all/
    _dispatch do take proto and delegate; register_alias never did.

    2. "Alias" means two structurally different things, and that is the real design
    risk.
    On a plain IntEnum an alias is a second name for the same member; on
    AppType it is a new member. Measured on LinkType:

    before: __members__=220  _member_names_=219  len(list())=219
    extend_enum(LinkType,'ETH',1) -> <LinkType.ETHERNET: 1>   is ETHERNET: True
    after:  __members__=221  _member_names_=219  len(list())=219      # iteration unchanged
    

    versus TCP.register_alias, which grew len(list(TCP)) by one. One name over two
    semantics invites callers to assume they match.

    Also measured: aenum already enforces the safety boundary — extend_enum with a
    colliding name raises TypeError for both same-value and different-value cases and
    leaves nothing partial (9999 in _value2member_map_ → False). So the generic method
    needs ValueError translation for consistency, not new protection. And a runtime alias
    is indistinguishable from a shipped one: LinkType already ships I2C_LINUX as an
    alias of IPMB_LINUX at 209, so __members__ − _member_names_ was non-empty before
    anyone called anything.

    On your (2), the split is better than I told the investigation. A single edit to
    LINE in pcapkit/vendor/default.py reaches 105 of 121 const modules; 16 need
    per-module work — the bespoke-context() set, which is ftp/{command,return_code},
    http/{method,status_code}, ipv6/extension_header, the four mh/*_flag,
    pcapng/option_type, tcp/flags, and the five reg/apptype/*. I had briefed 26/95,
    which is the process() axis — that governs member generation, not which helper
    methods get emitted. Wrong axis, my error. And the override vehicle already exists:
    today's register_alias text lives in pcapkit/vendor/reg/apptype/apptype.py:544,
    inside its bespoke BASE template.

    On your (4) and (5) — one hypothesis does not survive. pyshark reads only
    xml_obj.attrib['name'] from a PDML <proto> and discards the element
    (pyshark/packet/layers/xml_layer.py:16-32; the comment says the lxml objects are too
    memory-inefficient to keep). So "check other PDML attributes" would require parsing
    PDML in pcapkit rather than using pyshark's Packet. I could not enumerate the real
    <proto> attribute set — no tshark on this host and pyshark ships no sample — so that
    stays unverified; Wireshark's epan/print.c is where to read it.

    Justifiable to map: eth, sll/sll2, wlan/radiotap. Not justifiable: ip
    and ipv6, because a capture rooted there came from RAW, IPV4, IPV6 or NULL and
    the filter name cannot say which — mapping it is what the original ruling refused. And a
    trap for "register them all": 20 of 50 candidate filter names uppercase straight onto
    an existing LinkType member name by coincidence
    , including MPLS and NETLINK,
    which are Wireshark protocol dissectors that merely share a name with a DLT. There is
    no derivable rule — only a curated table.

    Three calls that are yours: whether the generic register_alias requires an existing
    member at the value (AppType does, register does not — this decides whether it is
    "alias" or just "second name"); whether both operations keep the same method name given
    they differ structurally; and whether those 20 coincidental matches get blessed, mapped
    explicitly, or refused.

  7. JarryShaw commented on Sep 27, 2026

    @JarryShaw
    OwnerAuthor

    Recorded, verbatim: "for this, it is expected and accepted by me. it's like import pyshark will load the module into cache and memory anyways, so having its aliases
    registered as permanent members of pcapkit.const is just part of the import's setup
    process. we dont have to overthink/overdesign on the lifecycle management thingy."

    That closes question (4) — the one the investigation called hardest. Option (a):
    register once on the pyshark import path, never undone, no teardown machinery. Noting
    for the record that the investigation's own preference was (c), resolve through a plain
    table and never touch the enum; your ruling overrides it and I am not re-arguing it. The
    measurement that makes (a) defensible is already on this issue: on a plain IntEnum an
    alias adds a name to __members__ and changes nothing else — _member_names_,
    iteration, _value2member_map_ and every repr are untouched — so the residue is
    confined to name lookup, which is precisely what you want it for.

    One sequencing consequence, no decision needed: #838 ships the curated table form for
    the two unambiguous names (eth, tr) because it must not wait for this design. Once
    register_alias lands here, Enum_LinkType.get('ETH') will resolve on its own and
    FILTER_NAME_TO_LINKTYPE becomes redundant for whatever it covers — so this issue
    should delete the entries it supersedes rather than leave two sources of truth. Worth
    doing in the same PR that adds the aliases.

    Still open from your (5), and the only thing blocking a full alias table: which
    Wireshark filter names to register. The measured constraints are above — ip/ipv6
    cannot be mapped at all, and 20 of 50 candidates uppercase onto an existing member name
    by coincidence, so the set has to be curated rather than derived.

  8. JarryShaw commented on Sep 27, 2026

    @JarryShaw
    OwnerAuthor

    It refuses on the base and works only when called on a subclass. get/get_all/
    _dispatch do take proto and delegate; register_alias never did.

    okay, then register_alias on AppType should also have this proto and delegate convention.

  9. JarryShaw commented on Sep 27, 2026

    @JarryShaw
    OwnerAuthor

    Recorded, verbatim: "okay, then register_alias on AppType should also have this
    proto and delegate convention."

    So the design is now specified apart from one item:

    1. Generic register_alias(value, name) in pcapkit/vendor/default.py's LINE,
      reaching 105 of 121 const modules from one edit. Validate the value already
      carries a member; translate aenum's TypeError on a colliding name to ValueError
      for consistency with the rest of the package.
    2. AppType overrides it in BASE/TRANSPORT, gaining proto and delegating
      through the existing _dispatch — matching get/get_all. This is the part that
      does not exist today, per the measurement above.
    3. Non-AppType registries: no routing, the caller class itself.
    4. Lifecycle: register once on the pyshark import path, never undone. No teardown.
    5. Which Wireshark filter names to register — still open, and the only thing left.

    16 modules need the method by hand beyond the template edit: ftp/command,
    ftp/return_code, http/method, http/status_code, ipv6/extension_header, the four
    mh/*_flag, pcapng/option_type, tcp/flags, and the five reg/apptype/*.

    Checkable blocker for implementation: #838 merged.

    gh pr view 838 -R JarryShaw/PyPCAPKit --json merged -q .merged   # true == unblocked
    

    Not a preference — a file conflict. This work edits pcapkit/vendor/default.py and
    regenerates the const tree, and #838 currently rewrites 117 files including all 105
    of those const modules. Starting before it merges guarantees conflicts and a re-derivation.
    Labelled blocked accordingly; wip removed since no PR covers it yet.

    The design record above is complete enough to implement from the moment #838 lands and
    (5) is answered.

  10. added
    blockedDeferred pending another issue or decision; see the last comment for what unblocks it
    and removed
    wipWork in flight - a covering PR is open or an agent is actively on it
    on Sep 27, 2026
  11. JarryShaw commented on Sep 27, 2026

    @JarryShaw
    OwnerAuthor

    "Alias" means two structurally different things, and that is the real design risk.

    For non-AppType registries, "Alias" is custom/caller-opt-in names, which are not recorded in IANA registrars, unlike AppType.

    pyshark reads only xml_obj.attrib['name'] from a PDML and discards the element (pyshark/packet/layers/xml_layer.py:16-32

    check PyShark/WireShark documentation then? they should have the names documented somewhere I believe.

    whether the generic register_alias requires an existing member at the value (AppType does, register does not — this decides whether it is "alias" or just "second name");

    actually i think it should always be for an existing member - unless AppType (and concrete enums) requires to call it on non-existing members.

    whether both operations keep the same method name given they differ structurally;

    same name for same convention - i think my ruling above makes them eseentially the same.

    and whether those 20 coincidental matches get blessed, mapped explicitly, or refused.

    so if the filter is same name as an existing enum, that's good, no alias needed.

  12. JarryShaw commented on Sep 27, 2026

    @JarryShaw
    OwnerAuthor

    Recorded, verbatim — all four judgement calls answered:

    For non-AppType registries, "Alias" is custom/caller-opt-in names, which are not
    recorded in IANA registrars, unlike AppType.

    check PyShark/WireShark documentation then? they should have the names documented
    somewhere I believe.

    actually i think it should always be for an existing member - unless AppType (and
    concrete enums) requires to call it on non-existing members.

    same name for same convention - i think my ruling above makes them eseentially the same.

    so if the filter is same name as an existing enum, that's good, no alias needed.

    So the contract is: always require an existing member, one method name, and the
    plain-enum case is explicitly the caller-opt-in, non-IANA half. That framing resolves
    the structural asymmetry I raised — the two operations differ in mechanism but share the
    convention, which is what the name is for.

    On the last one, I want to check rather than agree straight away. "If the filter is
    same name as an existing enum, that's good, no alias needed" is already how #838
    behaves — the table is consulted first, then Enum_LinkType.get catches anything that
    uppercases onto a member. The risk is that some of those 20 matches are coincidence,
    not identity
    : mpls and netlink are Wireshark protocol dissectors that happen to
    share a name with a DLT, so the rule would silently accept them for a capture whose link
    layer is something else.

    My prior is that it is harmless — pyshark strips geninfo/frame, so layers[0] is the
    first dissected protocol, and mpls appearing there implies MPLS is the link layer.
    But that is reasoning, not measurement, and a wrong DLT is silent corruption rather than
    a raise. Dispatching a read-only check of exactly that: for each of the 20, whether the
    filter name is reachable as layers[0] only from the DLT sharing its name.

    Same investigation takes your documentation pointer — Wireshark's protocol-abbreviation
    reference and epan/print.c for the real PDML <proto> attribute set, which I could not
    enumerate here (no tshark on the host, no sample in the pyshark distribution). That is
    the last open input for the alias list.

  13. 39 remaining items

  14. added this to the 1.5 milestone on Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    designA design or decision issue: a pattern being decided rather than a defect or a requestenhancementIssues requesting a new capability (set by the feature request template)

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions