Info Class¶
pcapkit.corekit.infoclass contains the dict-like class
Info, modelled on
dataclasses.dataclass() (PEP 557), and the immutable multi-mapping
classes MultiInfo and
OrderedMultiInfo that a finalised
Info holds its option lists in.
- class pcapkit.corekit.infoclass.Info(dict_=None, **kwargs)[source]¶
Bases:
Mapping[str,VT],Generic[VT]Turn dictionaries into
objectlike instances.Infoobjects are iterable, and support the read-onlydictinterfaceInfoobjects are immutable, thus cannot set or delete attributes after initialisation
Important
Infowill attempt to rename keys with the same names as the class’s builtin methods, and store the mapping information in the__map__and__map_reverse__attributes. However, when accessing such renamed keys, the original key name should always be used, i.e., such renaming is totally transparent to the user.Changed in version 1.3.3: Iterating an instance skips the names listed in
__excluded__.- Parameters:
*args – Arbitrary positional arguments.
**kwargs – Arbitrary keyword arguments.
- classmethod __init_subclass__(*args, **kwargs)[source]¶
Refuse to derive from a finalised info class.
final()is a promise to the type checker and nothing more: it records__final__on the class and leaves the interpreter free to subclass it anyway. Every classinfo_final()finalises carries a generated__init__built from the annotations that were visible at that moment, so a subclass added afterwards inherits a constructor that does not know about its own fields – silently, and with the__builtin__and__excluded__sets of its parent. This turns that promise into a rule the interpreter keeps.
- static __new__(cls, *args, **kwargs)[source]¶
Create a new instance.
The class will try to automatically generate
__init__method with the same signature as specified in class variables’ type annotations, which is inspired by PEP 557 (dataclasses).Note
Whether
clsstill needs finalising is read from its own__dict__, never inherited (GitHub issue #1490). Each class is finalised on its first construction, whatever its ancestors’ state, so a subclass of aFinalisedState.BASEorFinalisedState.FINALancestor still gets its own__builtin__,__excluded__and generated__init__, and a@finalclass under aBASEancestor is refused like any other.- Parameters:
- Raises:
InfoError – If
clswas marked withfinal()but never finalised byinfo_final(), i.e. it carries__final__in its own__dict__withoutFinalisedState.FINAL.- Return type:
Self
- classmethod from_dict(dict_=None, **kwargs)[source]¶
Create a new instance.
If
dict_is present and has a.keys()method, then does:for k in dict_: self[k] = dict_[k].If
dict_is present and has no.keys()method, then does:for k, v in dict_: self[k] = v.If
dict_is not present, then does:for k, v in kwargs.items(): self[k] = v.If
dict_is aMultiDict, a key it holds more than once keeps every value, asto_dict()returns them: the first is the value of the key, anditems(multi=True)yields all of them.
A mapping given for a key whose type annotation resolves at runtime to an
Infosubclass (or toOptionalof one) is rebuilt into that class, socls.from_dict(info.to_dict())restores such nested values. A key without such an annotation keeps the mapping as is – seeto_dict().Every
MultiDictorOrderedMultiDictvalue is stored as aMultiInfoorOrderedMultiInforespectively, or as the subclass of either that the key’s annotation names, such asSettings.The rebuild is as unchecked as the rest of this method: a mapping whose keys are not the nested class’s own is accepted and becomes an instance carrying those keys, exactly as a direct call to the nested class’s own
from_dict()would. Validating it here would reject data thatInfo(**kwargs)has always accepted, so the shape of the mapping stays the caller’s responsibility.
- to_dict()[source]¶
-
The
dictis anOrderedMultiDictof the fields, in order – a_OrderedMultiDict, i.e. one whose!=is the negation of its==, so two equal exports compare!=False. A key held more than once – as given by aMultiDicttofrom_dict(), e.g. a repeated IPv6 extension header – keeps every value, in place, foritems(multi=True)andgetlist(), while indexing sees the first. A nestedInfobecomes its ownto_dict(): anOrderedMultiDictfor a field model or anOrderedMultiInfo, and aMultiDictfor aMultiInfo; the values those two hold are not converted.Note
dict(result),{**result}andjson.dumps()see the first value of each key only, and silently drop the rest of a repeated key; read every value withitems(multi=True)orgetlist(). The result compares equal to anotherMultiDictonly, never to a plaindict.Important
We only convert nested
Infoobjects intodictif they are the direct value of theInfoobject’s attribute. Should suchInfoobjects be nested within other data, types, such aslist,tuple,set, etc., we shall not convert them intodictand remain them intact.Note
from_dict()reverses the flattening only for keys whose type annotation resolves at runtime to anInfosubclass. For any other key – every key of a bareInfo, and annotations naming a type imported only underTYPE_CHECKING– the conversion is one-way, and the nested value comes back as theOrderedMultiInfothatfrom_dict()stores theOrderedMultiDictit was written as in.Changed in version 1.5.0: Returns an
OrderedMultiDictrather than adict, so a key held more than once keeps every value (#1484).- Return type:
OrderedMultiDict[str,TypeVar(VT)]
- @pcapkit.corekit.infoclass.info_final(cls, *, _finalised=True)[source]¶
Finalise info class.
This decorator function is used to generate necessary attributes and methods for the decorated
Infoclass. It can be useful to reduce runtime generation time as well as caching already generated attributes.Notes
The decorator should only be used on the final class. Applying it with
_finalised=Trueseals the class against subclassing, whichInfo.__init_subclass__()enforces, and marks it withfinal()– so@info_finalimplies@finaland there is never a reason to write both. Writing both is harmless in either order, though; what is not is@finalwithout this decorator, whichInfo.__new__()refuses – provided@finalisfinal()(or, on Python 3.11 and up,typing.final()directly). Below 3.11, plaintyping.final()does not set__final__at all (gh-90500), so a class marked with a user’s ownfrom typing import finalon 3.10 is not refused: there is nothing on the class forInfo.__new__()to read.Applying this decorator to the same class a second time only warns: the first application already did the work, so the duplicate is redundant rather than wrong, and the class comes back finalised and usable.
- Parameters:
- Return type:
TypeVar(ST, bound= Type[Info])- Returns:
Finalised info class.
- Warns:
pcapkit.utilities.warnings.InfoWarning – If
clshas already been finalised by this function, i.e. it carriesFinalisedState.FINALin its own__dict__. The class is returned untouched.
Changed in version 1.5.0:
@finalis enforced at runtime: a class marked with it but never passed through this decorator is refused at first construction (#778).
- class pcapkit.corekit.infoclass.MultiInfo(mapping=None)[source]¶
Bases:
_MultiInfo[KT,VT],MultiDict[KT,VT]An immutable
MultiDictthat is anInfo.It holds every value of a key, as a
MultiDictdoes, but refuses every mutation, asInfodoes, withUnsupportedCall(see_MultiInfofor how it is both).A finalised
Infoholds everyMultiDictvalue as one of these, andto_dict()– its own and its holder’s – turns it back into aMultiDict.- Parameters:
mapping (
Mapping[Any,Any] |Iterable[tuple[Any,Any]] |None) – The initial value, as forMultiDict.
Added in version 1.5.0.
- class pcapkit.corekit.infoclass.OrderedMultiInfo(mapping=None)[source]¶
Bases:
_MultiInfo[KT,VT],OrderedMultiDict[KT,VT]An immutable
OrderedMultiDictthat is anInfo.As
MultiInfo, but keeping the order of every value across keys, as anOrderedMultiDictdoes – i.e. wire order for an option list.A finalised
Infoholds everyOrderedMultiDictvalue as one of these, andto_dict()– its own and its holder’s – turns it back into anOrderedMultiDict.- Parameters:
mapping (
Mapping[Any,Any] |Iterable[tuple[Any,Any]] |None) – The initial value, as forOrderedMultiDict.
Added in version 1.5.0.
Internal Definitions¶
- class pcapkit.corekit.infoclass.InfoMeta(name: str, bases: tuple[type, ...], attrs: dict[str, Any], **kwargs: Any)[source]¶
Bases:
ABCMetaMeta class to add dynamic support to
Info.This meta class is used to generate necessary attributes for the
Infoclass. It can be useful to reduce runtime generation cost as well as caching already generated attributes.Info.__additional__andInfo.__excluded__are lists of additional and excluded field names, which are used to determine certain names to be included or excluded from the field dictionary. They will be automatically populated from the class attributes of theInfoclass and its base classes.Note
This is implemented thru the
__new__()method, which will inherit the additional and excluded field names from the base classes, as well as populating the additional and excluded field from the subclass attributes.class A(Info): __additional__ = ['a', 'b'] class B(A): __additional__ = ['c', 'd'] class C(B): __additional__ = ['e', 'f'] print(A.__additional__) # ['a', 'b'] print(B.__additional__) # ['a', 'b', 'c', 'd'] print(C.__additional__) # ['a', 'b', 'c', 'd', 'e', 'f']
- class pcapkit.corekit.infoclass._MultiInfo(mapping=None)[source]¶
Bases:
Info[VT],Generic[KT,VT]An
Infothat holds its values as a multi-mapping.The common base of
MultiInfoandOrderedMultiInfo. Each of those also derives from themultidictclass it is named after, afterInfo, so it is anInfo–isinstance()holds, and it inheritsInfo.__new__()’s finalisation,Info.from_dict(),Info.__init_subclass__()andInfo.__additional__/Info.__excluded__– while its values live in the multi-mapping rather than in__dict__.That is the whole carve-out from
Info, and it is confined to this class and the two below it:__new__()gives the multi-mapping its empty storage, and__update__()adds every pair to it, so that the inheritedInfo.from_dict()builds one;info_final()generates no__init__for such a class (it has no declared fields);__setattr__()admits the multi-mapping’s own bookkeeping, and only while one of those two is writing;every mutator of the multi-mapping raises
UnsupportedCall, as assignment to anInfodoes;to_dict()exports the plain multi-mapping, so thatcls.from_dict(info.to_dict()) == info;MultiInfoandOrderedMultiInfobind the mapping protocol back to their multi-mapping class, whichInfoandMappingprecede in the MRO.
A key also reads as an attribute, giving its first value: a
strkey by its name, anEnumkey by its member name. A name the class itself defines, such asitems, is a method and not a key.
- class pcapkit.corekit.infoclass._OrderedMultiDict(mapping=None)[source]¶
Bases:
OrderedMultiDict[KT,VT]The
OrderedMultiDictthatInfo.to_dict()returns.It adds one method,
__ne__()(_ne()). Themultidictclasses are Werkzeug’s, kept verbatim, and Werkzeug’sOrderedMultiDictdefines__eq__but no__ne__:!=falls back todict’s, which compares the internal buckets, so two equal exports compared!=as well as==– and, since__eq__compares the values with!=, two exports holding a nested export compared unequal (#1484). Its copies, deep copies and pickles stay of this class, as Werkzeug’s build them fromtype(self), and__repr__()writes it as theOrderedMultiDictit is.
Type Variables¶
- pcapkit.corekit.infoclass.ST: Type[pcapkit.corekit.infoclass.Info]¶