The @prepare decorator's own docstring promises the decorated function receives *args, **kwargs, but its wrapper forwards neither. Anything a caller passes beyond the four known parameters is silently discarded — no error, no warning.
The documented contract
pcapkit/utilities/decorators.py, in prepare's docstring:
Note:
The decorated function should have following signature::
func(cls: 'typing.Type[pcapkit.protocols.schema.schema.Schema]',
data: 'bytes | typing.IO[bytes]',
length: 'Optional[int],
packet: 'Optional[dict[str, Any]',
*args: 'typing.Any', **kwargs: 'Any') -> '...Schema'
What actually happens
The wrapper calls the decorated function with exactly four arguments:
schema = func(cls, data, length, packet)
so the *args and **kwargs the docstring tells implementors to accept can never be populated.
Reproduction, on e2d8ed6d1
@schema_final
class Probe(Schema):
a: 'int' = UInt8Field()
b: 'int' = UInt8Field()
Probe.unpack(b'\x07\x08', 2, {}, 'EXTRA_POSITIONAL', extra_kw='EXTRA_KW')
Both extras vanish. No TypeError for the unexpected positional, no warning for the unused keyword — which is the part that makes this a defect rather than a limitation: a caller passing a misspelled or unsupported argument gets a successful parse and no signal that their argument did nothing.
There is a second, narrower case of the same shape: passing length both positionally and by keyword silently keeps the positional and drops the keyword.
Pre-existing, and not introduced by the @prepare fix
main's wrapper reads args[2]/args[3] unconditionally and also calls func(cls, data, length, packet), so the drop is identical before and after PR #450, which fixes a different fault in the same lines (issue #444 — the optional parameters being mandatory and positional). Confirmed by diffing: #450 changes only how length and packet are obtained, not how func is called.
Fix
Either forward what the docstring promises:
schema = func(cls, data, length, packet, *args[4:], **kwargs)
or, if extras are genuinely not wanted, delete the *args, **kwargs from the documented signature and let Python raise TypeError on an unexpected argument, which is the behaviour a caller would expect. The second is arguably better: nothing in the tree passes extras today, and silently accepting them is what hides a typo.
Whichever way, note that kwargs is currently consumed by kwargs.pop('length'/'packet', …) on #450's branch, so a forwarding fix needs to be careful not to re-forward those two.
Provenance
Found by the reviewer of PR #450 while checking that PR's argument binding, and flagged as out of scope for it. Verified and reproduced independently before filing.
The
@preparedecorator's own docstring promises the decorated function receives*args, **kwargs, but its wrapper forwards neither. Anything a caller passes beyond the four known parameters is silently discarded — no error, no warning.The documented contract
pcapkit/utilities/decorators.py, inprepare's docstring:What actually happens
The wrapper calls the decorated function with exactly four arguments:
so the
*argsand**kwargsthe docstring tells implementors to accept can never be populated.Reproduction, on
e2d8ed6d1Both extras vanish. No
TypeErrorfor the unexpected positional, no warning for the unused keyword — which is the part that makes this a defect rather than a limitation: a caller passing a misspelled or unsupported argument gets a successful parse and no signal that their argument did nothing.There is a second, narrower case of the same shape: passing
lengthboth positionally and by keyword silently keeps the positional and drops the keyword.Pre-existing, and not introduced by the
@preparefixmain's wrapper readsargs[2]/args[3]unconditionally and also callsfunc(cls, data, length, packet), so the drop is identical before and after PR #450, which fixes a different fault in the same lines (issue #444 — the optional parameters being mandatory and positional). Confirmed by diffing: #450 changes only howlengthandpacketare obtained, not howfuncis called.Fix
Either forward what the docstring promises:
or, if extras are genuinely not wanted, delete the
*args, **kwargsfrom the documented signature and let Python raiseTypeErroron an unexpected argument, which is the behaviour a caller would expect. The second is arguably better: nothing in the tree passes extras today, and silently accepting them is what hides a typo.Whichever way, note that
kwargsis currently consumed bykwargs.pop('length'/'packet', …)on #450's branch, so a forwarding fix needs to be careful not to re-forward those two.Provenance
Found by the reviewer of PR #450 while checking that PR's argument binding, and flagged as out of scope for it. Verified and reproduced independently before filing.