Numerical Fields¶
Sized Fields¶
- class pcapkit.corekit.fields.numbers.Int32Field(length=None, default=<pcapkit.corekit.fields.field.NoValueType object>, signed=None, byteorder='big', bit_length=None, callback=<function NumberField.<lambda>>)[source]¶
Bases:
NumberFieldInteger value for protocol fields.
- Parameters:
length (
int|Callable[[dict[str,Any]],int] |None) – Field size (in bytes).default (
int|NoValueType) – Field default value, if any.signed (
bool|None) – Whether the field is signed; fixed asTruehere, so a contradictingFalseis rejected rather than ignored.byteorder (
Literal['little','big']) – Field byte order.callback (
Callable[[Self,dict[str,Any]],None]) – Callback function to be called uponself.__call__.
- Raises:
FieldValueError – If
signedis given asFalse, contradicting the sign this class fixes.
- class pcapkit.corekit.fields.numbers.UInt32Field(length=None, default=<pcapkit.corekit.fields.field.NoValueType object>, signed=None, byteorder='big', bit_length=None, callback=<function NumberField.<lambda>>)[source]¶
Bases:
NumberFieldUnsigned integer value for protocol fields.
- Parameters:
length (
int|Callable[[dict[str,Any]],int] |None) – Field size (in bytes).default (
int|NoValueType) – Field default value, if any.signed (
bool|None) – Whether the field is signed; fixed asFalsehere, so a contradictingTrueis rejected rather than ignored.byteorder (
Literal['little','big']) – Field byte order.callback (
Callable[[Self,dict[str,Any]],None]) – Callback function to be called uponself.__call__.
- Raises:
FieldValueError – If
signedis given asTrue, contradicting the sign this class fixes.
- class pcapkit.corekit.fields.numbers.Int16Field(length=None, default=<pcapkit.corekit.fields.field.NoValueType object>, signed=None, byteorder='big', bit_length=None, callback=<function NumberField.<lambda>>)[source]¶
Bases:
NumberFieldShort integer value for protocol fields.
- Parameters:
length (
int|Callable[[dict[str,Any]],int] |None) – Field size (in bytes).default (
int|NoValueType) – Field default value, if any.signed (
bool|None) – Whether the field is signed; fixed asTruehere, so a contradictingFalseis rejected rather than ignored.byteorder (
Literal['little','big']) – Field byte order.callback (
Callable[[Self,dict[str,Any]],None]) – Callback function to be called uponself.__call__.
- Raises:
FieldValueError – If
signedis given asFalse, contradicting the sign this class fixes.
- class pcapkit.corekit.fields.numbers.UInt16Field(length=None, default=<pcapkit.corekit.fields.field.NoValueType object>, signed=None, byteorder='big', bit_length=None, callback=<function NumberField.<lambda>>)[source]¶
Bases:
NumberFieldUnsigned short integer value for protocol fields.
- Parameters:
length (
int|Callable[[dict[str,Any]],int] |None) – Field size (in bytes).default (
int|NoValueType) – Field default value, if any.signed (
bool|None) – Whether the field is signed; fixed asFalsehere, so a contradictingTrueis rejected rather than ignored.byteorder (
Literal['little','big']) – Field byte order.callback (
Callable[[Self,dict[str,Any]],None]) – Callback function to be called uponself.__call__.
- Raises:
FieldValueError – If
signedis given asTrue, contradicting the sign this class fixes.
- class pcapkit.corekit.fields.numbers.Int64Field(length=None, default=<pcapkit.corekit.fields.field.NoValueType object>, signed=None, byteorder='big', bit_length=None, callback=<function NumberField.<lambda>>)[source]¶
Bases:
NumberFieldLong integer value for protocol fields.
- Parameters:
length (
int|Callable[[dict[str,Any]],int] |None) – Field size (in bytes).default (
int|NoValueType) – Field default value, if any.signed (
bool|None) – Whether the field is signed; fixed asTruehere, so a contradictingFalseis rejected rather than ignored.byteorder (
Literal['little','big']) – Field byte order.callback (
Callable[[Self,dict[str,Any]],None]) – Callback function to be called uponself.__call__.
- Raises:
FieldValueError – If
signedis given asFalse, contradicting the sign this class fixes.
- class pcapkit.corekit.fields.numbers.UInt64Field(length=None, default=<pcapkit.corekit.fields.field.NoValueType object>, signed=None, byteorder='big', bit_length=None, callback=<function NumberField.<lambda>>)[source]¶
Bases:
NumberFieldUnsigned long integer value for protocol fields.
- Parameters:
length (
int|Callable[[dict[str,Any]],int] |None) – Field size (in bytes).default (
int|NoValueType) – Field default value, if any.signed (
bool|None) – Whether the field is signed; fixed asFalsehere, so a contradictingTrueis rejected rather than ignored.byteorder (
Literal['little','big']) – Field byte order.callback (
Callable[[Self,dict[str,Any]],None]) – Callback function to be called uponself.__call__.
- Raises:
FieldValueError – If
signedis given asTrue, contradicting the sign this class fixes.
- class pcapkit.corekit.fields.numbers.Int8Field(length=None, default=<pcapkit.corekit.fields.field.NoValueType object>, signed=None, byteorder='big', bit_length=None, callback=<function NumberField.<lambda>>)[source]¶
Bases:
NumberFieldByte value for protocol fields.
- Parameters:
length (
int|Callable[[dict[str,Any]],int] |None) – Field size (in bytes).default (
int|NoValueType) – Field default value, if any.signed (
bool|None) – Whether the field is signed; fixed asTruehere, so a contradictingFalseis rejected rather than ignored.byteorder (
Literal['little','big']) – Field byte order.callback (
Callable[[Self,dict[str,Any]],None]) – Callback function to be called uponself.__call__.
- Raises:
FieldValueError – If
signedis given asFalse, contradicting the sign this class fixes.
- class pcapkit.corekit.fields.numbers.UInt8Field(length=None, default=<pcapkit.corekit.fields.field.NoValueType object>, signed=None, byteorder='big', bit_length=None, callback=<function NumberField.<lambda>>)[source]¶
Bases:
NumberFieldUnsigned byte value for protocol fields.
- Parameters:
length (
int|Callable[[dict[str,Any]],int] |None) – Field size (in bytes).default (
int|NoValueType) – Field default value, if any.signed (
bool|None) – Whether the field is signed; fixed asFalsehere, so a contradictingTrueis rejected rather than ignored.byteorder (
Literal['little','big']) – Field byte order.callback (
Callable[[Self,dict[str,Any]],None]) – Callback function to be called uponself.__call__.
- Raises:
FieldValueError – If
signedis given asTrue, contradicting the sign this class fixes.
Enumeration Fields¶
- class pcapkit.corekit.fields.numbers.EnumField(length, default=<pcapkit.corekit.fields.field.NoValueType object>, signed=None, byteorder='big', bit_length=None, namespace=None, callback=<function EnumField.<lambda>>)[source]¶
Bases:
NumberField[IntEnum|IntEnum]Enumerated value for protocol fields.
- Parameters:
length (
int|Callable[[dict[str,Any]],int]) – Field size (in bytes); if a callable is given, it should return an integer value and accept the current packet as its only argument.default (
IntEnum|IntEnum|NoValueType) – Field default value, if any.signed (
bool|None) – Whether the field is signed;Nonedefers to the class-level__signed__, which this class leaves unset and so means unsigned.byteorder (
Literal['little','big']) – Field byte order.namespace (
Type[IntEnum] |Type[IntEnum] |None) – Field namespace (aenum.IntEnumclass).callback (
Callable[[Self,dict[str,Any]],None]) – Callback function to be called uponself.__call__.
Notes
A wire value the
namespaceregistry has no member for resolves to a nameless pseudo-member rather than failing the parse – seepost_process().- post_process(value, packet)[source]¶
Process field value after parsing (unpacked).
- Parameters:
- Return type:
IntEnum|IntEnum- Returns:
Processed field value – the registry member declared for the value, or a nameless pseudo-member carrying the value itself when the registry declares none.
- Raises:
BaseError – Whatever in-library error the registry raised for the value, re-raised untouched.
Notes
The registry is consulted through its constructor, which raises for a value no member and no
_missing_rule accounts for. That raise used to propagate, and it isaenum’s own bareValueError: not one ofpcapkit.utilities.exceptions, so a caller cannot tell it from a bug of its own, and not anEOFError, soExtractor.record_framesdoes not catch it. One unassigned code therefore cost the whole extraction.It also made the “unknown” reader the formats require unreachable for any genuinely unassigned code – PCAP-NG’s
UnknownBlock, and theunassignedoption readers of IPv4, TCP, HOPOPT, MH and HIP – because the lookup failed several frames before the dispatch that would have selected it. PCAP-NG repeats a block’s total length at both ends precisely so that a reader can skip a block type it does not recognise; that skip is what this fallback restores. See GitHub issue #701.The fallback is the same nameless pseudo-member this method already builds for a field carrying no registry at all, so it is a value shape the package already produces and the dump layer already renders – as
<unknown>::<unassigned> [28], throughrender_enum(), not through thename is Nonebranch #648 added, which a member named<unassigned>never takes – and one anint-keyed dispatch registry looks up by value like any declared member. It is built per value rather than grafted onto the registry withaenum.extend_enum(), for two reasons: a capture carrying many distinct unassigned codes would otherwise grow a process-global registry without bound, which is the growthProtocolBase._lookup_registryexists to avoid; and a stdlibenum.IntEnumregistry is then handled exactly like anaenum.IntEnumone.Only a foreign rejection is absorbed. A registry rejecting a value with one of
pcapkit.utilities.exceptionshas made a deliberate decision that this layer – which sees only that the value arrived in a field of some width – is in no position to overrule, so an in-library error propagates unchanged and it is onlyaenum’s andenum’s “no member has this value” that becomes a pseudo-member. That is what keeps the fallback from being an unconditionalexcept ValueError: pass.No registry under
pcapkit.constraises an in-library error from its guard today, and deliberately so: a generated guard raises a bare, unloggedValueErrorprecisely because the generatedget()’sexcept ValueErrorfallback has to keep catching it (GitHub issues #584 and #647). The registries that do bound themselves to a width and reject outside it are the bit-flag ones –pcapkit.const.tcp.flags.Flagsamong them – and none of those is named as the namespace of a plainEnumFieldanywhere in the package, so no in-library guard loses its force through this method. The distinction is therefore for a registry registered from outsidepcapkit.const, which has no such obligation to stay quiet.
- _pseudo_member(value)[source]¶
Build the bounded nameless pseudo-member this method falls back to when
valueis a foreign miss rather than an in-library rejection.- Return type:
IntEnum|IntEnum- Returns:
A single-member, throwaway
enum.IntEnuminstance, built fresh per call rather thanextend_enum()-ed ontoself._namespace, per this method’s own docstring above.
- static _unregistered_member(namespace, value, name='<unassigned>', **attrs)[source]¶
Build a member of
namespace, absent from every one of its own lookup tables, for a value a parse – rather than a direct call to the registry’s ownget()– resolved without anyone asking for a name.GitHub issue #575: the owner’s ruling is that an unassigned wire value should resolve to a real member of the registry the field names –
isinstanceagainst it and every ancestor holds, and it renders and dispatches exactly like a declared one – provided building it never grows the registry, which is the whole reason the field stopped callingget()unconditionally in the first place. This is what gets there: it callsnamespace’s own storage base’s__new__directly –strorint, whichevernamespacederives from – which skipsnamespace’s own__new__entirely, and with it thecls.__registry__.add(...)/cls.__members_ns__[...] = ...line every registry in this package uses to record a member it mints. No entry is added to_member_map_or_value2member_map_either, since those are only ever touched by the metaclass machineryaenum.extend_enum()drives, which this bypasses completely.Note
Building a member this way, rather than as some other type altogether, is why
post_process()and its siblings need this rather than_pseudo_member(): the result answersisinstance(result, AppType)truthfully, which matters to at least sevenisinstancesites elsewhere inpcapkit.protocols(seetest_a_member_is_still_an_apptypeintests/const/test_const_apptype_split_unit.py), and a value that fails all of them would be a second defect standing in for the one this fix removes.The member this returns is absent from
_value2member_map_, so a value-keyed lookup on it –self._namespace(value)– still raises exactly as it did before this existed. Nothing on the parse or reconstruction path does that to a value it just resolved this way, which is what keeps this safe to return frompost_process. A direct call toget()for the same port is a different matter and deliberately unchanged: asking the registry for a name is an explicit request for a named member, so it still mints one – measured on this tree,AppType.get(54321, proto=tcp)returnsPORT_54321_tcpand takesTCP.__members__from 6147 to 6148, and a second call with the same port returns that member rather than raising. Two unregistered members for the same value also compare equal without being identical, sinceAppTypeandOptionTypeboth define__eq__/__hash__off an attribute (.port/.opt_value) rather than object identity – harmless for every reader in this package, since none compares one withisor keys a mapping on it expecting identity, but worth knowing before reusing this elsewhere.pickleandcopy.copy()/copy.deepcopy()all reduce anEnummember throughEnum.__reduce_ex__, which returns(cls, (value,))– the one lookup this member is deliberately absent from. Left alone that is a genuine regression rather than a pre-existing limitation, because the call sites used to mint, so the member was registered and a round-trip worked. Measured on CPython 3.14.7, resolving port 53406 throughPortEnumField: on83b58ebdapickle.loads(pickle.dumps(member))returned the member, and with the mint removed and nothing in its place it raisedValueError: 'unknown [53406 - tcp]' is not a valid TCP– whilepickle.dumpsstill succeeded, so the failure surfaced only on read-back rather than where it was caused.So
__reduce_ex__is set on the member itself, reducing it to_rebuild_unregistered_member()instead of to a value lookup. Bothpickleandcopyfetch that attribute withgetattr()on the object rather than on its type, so a per-instance override is honoured: verified against the Cpickleaccelerator on every protocol from 0 to 5, against the pure-Pythonpickle._Pickler, and againstcopy.copy()/copy.deepcopy()both as they are on 3.11+ and with CPython’sEnum.__copy__/__deepcopy__deleted to emulate 3.10, where those two do not exist. Rebuilding re-enters this method rather thannamespace.__new__, so an unpickled member is unregistered exactly as the original was and the registry does not grow –TCP.__members__measured at 6147 before and after. On 3.11+copy/deepcopystill return the member itself, sinceEnum.__copy__short-circuits ahead of any reduction; on 3.10 they return an equal rebuilt one, which is the same answer for an immutable value.- Parameters:
namespace (
Type[IntEnum] |Type[IntEnum]) – The concrete registry class to build the member as an instance of.isinstanceholds against it and every ancestor; it never gains an entry in any of its own tables.value (
Any) – The valuenamespace’s own constructor would have wrapped – e.g. the crafted string__new__()builds from a name, a port and a transport, or__new__()’s equivalent – kept the same shape here so a rendered or re-keyed member reads the same either way.name (
str) – The member’s own.name;'<unassigned>'matches every other nameless value this package produces.**attrs (
Any) – Extra attributes to set on the returned member, matching the shape the caller’s registry gives its real members –AppType’s.port,.svcand.proto, orOptionType’s.opt_nameand.opt_value.
- Return type:
- Returns:
The unregistered member.
- Raises:
TypeError – If
namespacederives from neitherstrnorint– every registry this package builds does one or the other, and guessing wrong for some future one would ship a member silently missing whatever its storage base provides, rather than saying plainly that this needs extending first.
Internal Definitions¶
- class pcapkit.corekit.fields.numbers.NumberField(length=None, default=<pcapkit.corekit.fields.field.NoValueType object>, signed=None, byteorder='big', bit_length=None, callback=<function NumberField.<lambda>>)[source]¶
Bases:
Field[int],Generic[_T]Numerical value for protocol fields.
- Parameters:
length (
int|Callable[[dict[str,Any]],int] |None) – Field size (in bytes); if a callable is given, it should return an integer value and accept the current packet as its only argument.default (
int|NoValueType) – Field default value, if any.signed (
bool|None) – Whether the field is signed;Nonedefers to the class-level__signed__, which this class leaves unset and so means unsigned.byteorder (
Literal['little','big']) – Field byte order.callback (
Callable[[Self,dict[str,Any]],None]) – Callback function to be called uponself.__call__.
- Raises:
IntError – If no
lengthis given and__length__fixes none either.FieldValueError – If
signedcontradicts a sign already fixed by__signed__– never from this class, which fixes none.ProtocolError – If
bit_lengthis given negative. Left alone,(1 << bit_length) - 1raises a bare, uncatchableValueError(negative shift count) here, before__call__()’s own negative-lengthguard (#828/#829) orlength’s (#805) ever see anything – this one fires at construction time, on the argument itself rather than on a resolved wire length. See GitHub issue #831.
Notes
A subclass such as
UInt32Fieldfixes the sign through__signed__, sosignedthere is at best redundant. It used to be discarded outright, in both directions, which meantUInt32Field(signed=True)handed back an unsigned field whose values only looked wrong once the high bit was set – see GitHub issue #545. A contradicting value is now rejected instead; omitting it, or passing the sign the class already fixes, stays legal.- __call__(packet)[source]¶
Update field attributes.
- Parameters:
- Return type:
Self- Returns:
New instance of
NumberField.
This method will return a new instance of
NumberFieldinstead of updating the current instance.- Raises:
ProtocolError – If the resolved
lengthis negative – e.g. alengthcallback such aslambda pkt: pkt['len'] - 4resolving below zero once the wire value it reads is smaller than the subtrahend. Left alone,1 << (length * 8)raises a bare, uncatchableValueError(negative shift count) whenbit_lengthwas not supplied, beforelength(see its ownProtocolErrorguard, #805/#811/#827) orbuild_template()ever sees the value: this method setsself._bit_lengthfrom the resolved length eagerly, as a cache, and shifts by it immediately, so the crash happens on this line rather than on the later, already-guarded ones. See GitHub issue #828. This guard runs regardless of whetherbit_lengthwas supplied, so a field constructed with a fixedbit_lengthand a callablelengththat resolves negative raises the identical message as one with nobit_lengthat all, rather than falling through to atemplate='...-1s'ProtocolErrorfromlengthlater – see GitHub issue #831. A resolved length of exactly0is a legitimate empty field (e.g.len=4above resolving to0) and is left alone.
Notes
Rebuilding the template here is what applies a callable
length, andbuild_template()recomputesself._need_processas it goes, so the flag and the template always describe the same width. They did not always: see GitHub issue #591.
- build_template(length, signed)[source]¶
Build template for field.
- Parameters:
- Return type:
- Returns:
Template for field.
Notes
self._need_processis assigned here rather than only ever raised, so that it always describes thelengththis template was built for. It used to be setTruein the fall-through branch and never put back, which made it a latch: a callablelengthis a placeholder of-1at construction,-1takes the fall-through branch, and the flag then survived the rebuild in__call__()that resolved the real width.pre_process()consequently handedbytesto a template that had become>Q– or>I,>H,>B– andstruct.pack()refused it. See GitHub issue #591.Assigning it is what tells a placeholder apart from a width that genuinely needs byte packing, without having to remember that a placeholder was ever in play: the answer for
-1isTrue, the answer for8isFalse, and whichever width is in force now is the one that decides. A callable resolving to, say,3still takes the fall-through branch and still getsTrue, because for3that is the correct answer rather than a leftover one.
- pre_process(value, packet)[source]¶
Process field value before construction (packing).
- Parameters:
- Return type:
- Returns:
Processed field value.
Notes
Masking against
self._bit_masktruncates the value to the field’s bit length, but it also turns a negative value into its unsigned two’s-complement pattern, which neitherstruct.pack()norint.to_bytes()accepts for a signed field. A signed field therefore maps the pattern back into its signed range afterwards, so that e.g. a PCAP-NG section length of-1(section length not specified) can be written out.A field packed without having been resolved – so with
_lengthstill negative – has its width derived from the value instead, and that rebuild can land on a widthstruct()has a native integer code for. The flag is therefore consulted after the rebuild rather than before it, since deciding first and rebuilding second is how the template and the value being returned came to disagree in the first place. C.f. #591.That width is a ceiling of the bit length over eight, and it is written as one. It used to read
math.ceil(value.bit_length() // 8), which is not a ceiling at all:math.ceil()of anintis thatint, so the//had already floored the quotient and the outer call did nothing. Every value whose bit length is not an exact multiple of eight was therefore sized one octet short –256at one octet,65536at two, and1itself at zero – whichint.to_bytes()andstruct.pack()both refuse. See GitHub issue #599.
- pcapkit.corekit.fields.numbers._rebuild_unregistered_member(namespace, value, name, attrs)[source]¶
Rebuild the member
EnumField._unregistered_member()returned.This is what
pickleandcopyreconstruct through, in place of the value lookupEnum.__reduce_ex__would otherwise have reduced the member to. It is a module-level function rather than a method so that everypickleprotocol can name it: protocols below 4 cannot reference a callable nested inside a class.- Parameters:
- Return type:
- Returns:
A member equal to the original and, like it, absent from every one of
namespace’s lookup tables – rebuilding goes back throughEnumField._unregistered_member()and never throughnamespace.__new__, so it cannot register anything either.
- pcapkit.corekit.fields.numbers._reduce_unregistered_member(namespace, value, name, attrs, protocol)[source]¶
The
__reduce_ex__EnumField._unregistered_member()installs.Bound to its first four arguments with
functools.partial(), so that the reducer holds the ingredients of the member rather than the member itself.- Parameters:
namespace (
Type[IntEnum] |Type[IntEnum]) – The registry class the member is an instance of.value (
Any) – The member’s_value_.name (
str) – The member’s_name_.attrs (
dict[str,Any]) – The extra attributes the member carries.protocol (
int) – Thepickleprotocol version, ignored – the reduction is the same for all of them, andcopypasses 4 here.
- Return type:
- Returns:
A two-tuple of
_rebuild_unregistered_member()and its arguments.