Skip to content

fix(registry): ModuleDescriptor.klass leaks a bare AttributeError, so all nine register_* call sites fail a bad class name with a stdlib error #832

Description

@JarryShaw

ModuleDescriptor.klass raises a bare builtins.AttributeError when the class name does not exist in the module, so every register_* function in pcapkit/foundation/registry/protocols.py fails a bad class name with a stdlib exception rather than a pcapkit one.

Measured on ee51366a6 (PR #815's head) and reproducible on main:

register_apptype(port, 'pcapkit.protocols.misc.raw', 'tcp', TransportProtocol.tcp)
  -> AttributeError: module 'pcapkit.protocols.misc.raw' has no attribute 'tcp'

register_apptype(AppType.http, 'pcapkit.protocols.misc.raw')      # class_ omitted
  -> AttributeError: module 'pcapkit.protocols.misc.raw' has no attribute '(null)'

The second is the more telling one: the NULL sentinel '(null)' reaches getattr and is reported as if the caller had asked for a class of that name.

Scope: protocols.py builds ModuleDescriptor(module, class_) in nine places, so this is the whole register_* family, not one function. The exception originates at pcapkit/corekit/module.py:37 — re-derive that line before changing it.

This is the same family as #805/#811 (struct.error escaping FieldBase.length), #828/#829 (ValueError escaping NumberField.__call__) and #831 (the second shift): a caller cannot catch these as pcapkit errors. The fix belongs in ModuleDescriptor.klass, raising ProtocolError from pcapkit.utilities.exceptions and naming both the module and the missing class, so all nine call sites gain it at once.

One coupling to know before changing it. #815 added a test asserting assertRaises(AttributeError) with assertIn("'tcp'", …) — deliberately, to prove that a str third argument is resolved as a class name and never sniffed as a transport. That assertion will need updating in the same change; the property it protects should survive as a ProtocolError whose message still contains the attempted class name.

Also worth folding in: a missing class_ should not reach getattr at all. When module is a str and class_ is still the NULL sentinel, the caller has omitted a required argument, and saying so is more useful than reporting '(null)' as an absent attribute.

Found by #815's cross-review while judging whether that AttributeError was the right failure for the residual. The conclusion there was to leave it — fixing it in one function would make register_apptype inconsistent with its eight siblings — which is what makes this a family-wide issue rather than part of that PR.

Activity

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

    bugfixPull requests that fix a defect (fix: subject prefix)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions