From e6aec8ad2ef6a2f3e644d24edcd779ac3d247073 Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Mon, 26 Oct 2026 16:18:52 -0400 Subject: [PATCH] docs(protocols): name ProtocolBase in every register's Raises, as #1016 ruled (#2316) Problem: Link, Internet and Transport register's Raises said "not a Protocol subclass", and the ProtocolBase.__proto__ comment said values are Protocol subclasses, while _next_layer_entry checks ProtocolBase and the other layers already said so. Fix: name ProtocolBase in those three Raises clauses and the __proto__ comment. The error message keeps naming Protocol, per #1016. Docstring and comment only; the AST is unchanged. Test: test_register_class_guard_unit pins ProtocolBase in every register site's Raises and in the __proto__ comments; it fails on origin/main. --- pcapkit/protocols/internet/internet.py | 2 +- pcapkit/protocols/link/link.py | 2 +- pcapkit/protocols/protocol.py | 2 +- pcapkit/protocols/transport/transport.py | 2 +- .../test_register_class_guard_unit.py | 37 +++++++++++++++++++ 5 files changed, 41 insertions(+), 4 deletions(-) diff --git a/pcapkit/protocols/internet/internet.py b/pcapkit/protocols/internet/internet.py index 69bfc4ceb3..2498f0f979 100644 --- a/pcapkit/protocols/internet/internet.py +++ b/pcapkit/protocols/internet/internet.py @@ -173,7 +173,7 @@ def register(cls, code: 'Enum_TransType', protocol: 'ModuleDescriptor[ProtocolBa Raises: pcapkit.utilities.exceptions.RegistryError: If ``protocol`` is not a - class, or not a :class:`~pcapkit.protocols.protocol.Protocol` subclass. + class, or not a :class:`~pcapkit.protocols.protocol.ProtocolBase` subclass. Warns: pcapkit.utilities.warnings.RegistryWarning: If this protocol number diff --git a/pcapkit/protocols/link/link.py b/pcapkit/protocols/link/link.py index 4e48983e9f..ce41a97434 100644 --- a/pcapkit/protocols/link/link.py +++ b/pcapkit/protocols/link/link.py @@ -128,7 +128,7 @@ def register(cls, code: 'Enum_EtherType', protocol: 'ModuleDescriptor[ProtocolBa Raises: pcapkit.utilities.exceptions.RegistryError: If ``protocol`` is not a - class, or not a :class:`~pcapkit.protocols.protocol.Protocol` subclass. + class, or not a :class:`~pcapkit.protocols.protocol.ProtocolBase` subclass. Warns: pcapkit.utilities.warnings.RegistryWarning: If this EtherType is diff --git a/pcapkit/protocols/protocol.py b/pcapkit/protocols/protocol.py index c144fbdc22..601e6e0270 100644 --- a/pcapkit/protocols/protocol.py +++ b/pcapkit/protocols/protocol.py @@ -512,7 +512,7 @@ class ProtocolBase(Generic[_PT, _ST], metaclass=ProtocolMeta): #: :meth:`self._decode_next_layer ` #: and :meth:`self._import_next_layer `. #: Each value is a :class:`~pcapkit.corekit.module.ModuleDescriptor` naming the - #: module and class, or a :class:`Protocol` subclass. + #: module and class, or a :class:`ProtocolBase` subclass. __proto__: 'DefaultDict[int, ModuleDescriptor[ProtocolBase] | Type[ProtocolBase]]' = collections.defaultdict( lambda: ModuleDescriptor('pcapkit.protocols.misc.raw', 'Raw'), ) diff --git a/pcapkit/protocols/transport/transport.py b/pcapkit/protocols/transport/transport.py index 96f14e059c..5054d54649 100644 --- a/pcapkit/protocols/transport/transport.py +++ b/pcapkit/protocols/transport/transport.py @@ -90,7 +90,7 @@ def register(cls, code: 'int', protocol: 'ModuleDescriptor[ProtocolBase] | Type[ pcapkit.utilities.exceptions.UnsupportedCall: If called on :class:`Transport` itself. pcapkit.utilities.exceptions.RegistryError: If ``protocol`` is not a - class, or not a :class:`~pcapkit.protocols.protocol.Protocol` subclass. + class, or not a :class:`~pcapkit.protocols.protocol.ProtocolBase` subclass. Warns: pcapkit.utilities.warnings.RegistryWarning: If this port is already diff --git a/tests/protocols/test_register_class_guard_unit.py b/tests/protocols/test_register_class_guard_unit.py index 10655ac3af..334fb469fd 100644 --- a/tests/protocols/test_register_class_guard_unit.py +++ b/tests/protocols/test_register_class_guard_unit.py @@ -10,6 +10,10 @@ :class:`~pcapkit.corekit.module.ModuleDescriptor` is unwrapped, so a descriptor naming a non-class attribute is rejected too. +GitHub issue #2316: every ``register``'s ``Raises:`` clause, and the +``__proto__`` comment, name ``ProtocolBase``, the class the guard checks; the +error message names ``Protocol``, as #1016 ruled. + Every registry is process-wide, so each call runs under :func:`unittest.mock.patch.dict`; the refusals under test write nothing, and the patch keeps a regression that *did* write from leaking into other tests. @@ -19,6 +23,8 @@ import importlib import importlib.util +import inspect +import re import unittest import warnings from unittest import mock @@ -40,6 +46,13 @@ ('UDP', 'pcapkit.protocols.transport.udp', 'UDP', 1), ) +#: Modules whose ``__proto__`` ``#:`` comment names the type of a value. +PROTO_COMMENT_MODULES = ( + 'pcapkit.protocols.protocol', + 'pcapkit.protocols.misc.pcap.frame', + 'pcapkit.protocols.misc.pcapng', +) + @unittest.skipUnless(HAS_RUNTIME, 'runtime dependencies not installed') class RegisterClassGuardTests(unittest.TestCase): @@ -109,6 +122,30 @@ def test_valid_class_and_descriptor_still_register(self) -> None: klass.register(code, arg) self.assertIs(klass.__proto__[code], Raw) + def test_raises_names_protocol_base_for_the_subclass_check(self) -> None: + # #2316: the guard checks ``issubclass(protocol, ProtocolBase)``, so the + # ``Raises:`` clause names ``ProtocolBase``; the error message keeps + # naming ``Protocol``, as #1016 ruled. + for label, klass, _ in self._sites(): + with self.subTest(site=label): + doc = inspect.getdoc(klass.register) or '' + self.assertIn('Raises:', doc) + raises = ' '.join(doc.split('Raises:', 1)[1].split('\n\n', 1)[0].split()) + self.assertIn('not a :class:`~pcapkit.protocols.protocol.ProtocolBase` subclass', raises) + self.assertNotIn('Protocol` subclass', raises) + + def test_proto_comment_names_protocol_base(self) -> None: + # #2316: a ``__proto__`` value is checked against ``ProtocolBase`` too. + for module in PROTO_COMMENT_MODULES: + with self.subTest(module=module): + source = inspect.getsource(importlib.import_module(module)) + match = re.search(r'((?:^[ \t]*#:.*\n)+)[ \t]*__proto__\b', source, re.MULTILINE) + self.assertIsNotNone(match) + assert match is not None + comment = ' '.join(line.strip()[2:].strip() for line in match.group(1).splitlines()) + self.assertRegex(comment, r'ProtocolBase` subclass') + self.assertNotIn('Protocol` subclass', comment) + def test_transport_itself_is_still_unsupported(self) -> None: from pcapkit.protocols.transport.transport import Transport from pcapkit.utilities.exceptions import UnsupportedCall