Skip to content

reg: expose AppType aliases as a public property and in repr/str, and allow registering them per transport #807

Description

@JarryShaw

Is this a bug or a feature request? A feature, ruled on in #801.

Step 2 of #801's three. The maintainer's rulings, verbatim:

and maybe allow aliasing, like register_apptype(port: int, name: str, *transport: TransportProtocol, aliases: list[str] = None), where aliases is an optional keyword argument using name as the canonical name with aliases for the port's aliases like port 80's.

alias should thus be per-transport. and we should probably allow registering aliases for a given enum in the transport apptype enum.

and on each apptype enum's repr/str, it should expose its associated aliases (if any) as well. And the enum should also have a public property/member to fetch its aliases list.

(He also noted his signature sketches are "just POC not how it should actually look like", so treat the shape as the requirement and the parameter list as illustrative.)

What changes

  1. A public .aliases property on each AppType member.
  2. __repr__ and __str__ expose the aliases when there are any.
  3. A way to register an alias on a per-transport registry.

Expected behavior

TCP['http'].aliases gives the other services IANA registers on TCP/80 — and SCTP['http'].aliases is empty, because www is not registered on SCTP. Aliases are a property of (port, transport), never of the port alone.

Additional context

Most of this already exists — check before building. Reading tests/const/test_const_apptype_split_unit.py (415 lines, 15 methods) shows the canonical/alias split is already implemented and tested:

TCP.__canonical__ entries = 23    UDP = 21    SCTP = 0    DCCP = 0
TCP.get(80).svc      = http                   <- canonical
TCP.get_all(80) svcs = ['http', 'www', 'www-http']
TCP.http == TCP.www  = True   (is: False)     <- equal and same hash, distinct members

__canonical__ is a per-registry port → canonical service map curated from /etc/services, because IANA names no precedence and registry row order matches /etc/services on 28 of the 44 colliding pairs and differs on 16 (test_a_port_lookup_returns_the_canonical_service at :114 records the reasoning; test_get_all_reaches_the_aliases at :143 is the existing reader).

So .aliases needs no new storage. It derives as __registry__.getlist(self.port) minus self — verified:

derived aliases of TCP.http -> ['www', 'www-http']
derived aliases of TCP.www  -> ['http', 'www-http']

Return an empty tuple rather than None when there are none, so callers need no guard, and a tuple rather than a list because members are otherwise immutable.

Hard constraint, measured: the aliases must NOT reach _value_. AppType is a StrEnum and _value_ is the live lookup key in _value2member_map_, built once at class creation. Mutating it afterwards leaves the map stale:

TCP('http [80 - tcp|udp|sctp]') is TCP['http'] -> True
after mutating _value_:  TCP(old) -> still resolves ; TCP(new) -> ValueError: not a valid TCP

Since aliases are registerable at runtime, an alias list inside _value_ goes stale the moment one is registered. So __repr__/__str__ grow them and _value_ does not.

Two consequences to handle rather than discover:

  • str(m) == m.value is True today and becomes False. 0 tests or docs pin the bracketed value form — verified with a self-tested pattern (grep -rnE "\[[0-9]+ - (tcp|udp|sctp|dccp)" tests/ docs/source/ examples/ → 0, and the same pattern matches the real string, so the zero is real).
  • repr/str output becomes mutable at runtime, since registering an alias changes it. Note that in the docstring so nobody pins a fixed alias list in a doctest.

Related: #801, #806, #732.

Activity

  1. added
    enhancementIssues requesting a new capability (set by the feature request template)
    blockedDeferred pending another issue or decision; see the last comment for what unblocks it
    on Sep 25, 2026
  2. JarryShaw commented on Sep 25, 2026

    @JarryShaw
    OwnerAuthor

    Checkable blocker: #806 merged.

    gh issue view 806 -R JarryShaw/PyPCAPKit --json state
    

    #806 rewrites the three format strings this issue then has to extend — pcapkit/vendor/reg/apptype/apptype.py:250 (__new__, which sets _value_), :270 (__repr__), :273 (__str__) — because the retyping drops the composite proto.name from all three. Adding aliases to repr/str first means writing against strings that are about to change, then re-deriving. Doing it after means extending strings that have already reached their final shape.

    Chain: #803 → #806 → this issue, with #808 also waiting on #806.

  3. removed
    blockedDeferred pending another issue or decision; see the last comment for what unblocks it
    on Sep 26, 2026
  4. JarryShaw commented on Sep 26, 2026

    @JarryShaw
    OwnerAuthor

    Checkable blocker: #808 merged.

    gh pr view 808 -R JarryShaw/PyPCAPKit --json state 2>/dev/null || gh issue view 808 -R JarryShaw/PyPCAPKit --json state
    

    Unblocked from #806 by #815 merging (3118ed796), but sequenced behind #808 rather than dispatched now, because both change pcapkit/const/reg/apptype/apptype.py and its generator pcapkit/vendor/reg/apptype/apptype.py — and #808 may replace TransportProtocol's base class, which changes the ground this work stands on. Two agents in one generated file is how a regeneration silently clobbers the other's work.

    Ordering, not scope. Dispatching as soon as #808 lands.

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

    @JarryShaw
    OwnerAuthor

    Unblocked — #808 is closed. Its PR #836 merged as 14d3d3dc7, so the file-collision reason for sequencing this behind it is gone.

    gh issue view 808 -R JarryShaw/PyPCAPKit --json state   ->  CLOSED
    git log --oneline -1 origin/main  ->  14d3d3dc7 fix(reg)!: drop TransportProtocol's IntFlag base … (#836)
    

    Dispatching a worker now. One thing #836 changed that this issue's design has to account for: TransportProtocol is a plain aenum.IntEnum and no longer extensible — TransportProtocol.get() refuses an unrecognised name rather than minting. So per-transport alias registration must resolve its transport against the five declared members and refuse anything else, not create one.

    wip.

  7. JarryShaw commented on Sep 26, 2026

    @JarryShaw
    OwnerAuthor

    Closing — delivered. PR #837 merged as add2a8255 on main, which now carries the public aliases property, alias-aware repr/str, and the per-transport register_alias classmethod in both the generator template and the generated const file.

    Verified on the merged head: 52.65% → 53.59% coverage of pcapkit/const/reg/apptype/apptype.py (1624/769 → 1657/769 statements, all 33 new ones covered), tests/const/ 87 run / OK, and CI 27✅ / 0❌ / 3 skipped / 0 incomplete. Cross-reviewed GOOD TO GO on the merged sha by a model other than the author.

  8. removed
    wipWork in flight - a covering PR is open or an agent is actively on it
    on Sep 26, 2026
  9. 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

    enhancementIssues requesting a new capability (set by the feature request template)

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions