User Defined Exceptions¶
pcapkit.utilities.exceptions holds pcapkit’s exceptions, each
refining a built-in one, grouped below by that built-in.
Loud and Quiet Errors¶
A BaseError is loud by default: it is
logged once at logging.CRITICAL on the pcapkit logger
(logger), and outside development mode an
exception hook is installed on sys.excepthook() and
threading.excepthook(), the first time a loud error needs it, so the user
sees the exception line rather than a walk through pcapkit’s
internals [*]. The hook shortens only a
BaseError; it hands every other exception,
unchanged, to the hook installed before it.
quiet=True marks an error pcapkit raises as internal control flow
and catches itself – the MissingKeyError
behind MultiDict.get is the
archetype. It emits nothing on any channel and touches no process-global state,
but is otherwise an ordinary exception carrying its message.
An error constructed inside a
CaughtByCaller block naming one of its
classes is quiet too. That is how pcapkit marks as control flow an error
raised by code it calls but does not own, such as a generated
_validate_value override, which cannot pass quiet=True itself
(#1845).
beholder() runs the whole call to a next layer in
such a block for ProtocolError, so a refusal it
keeps as Raw is recorded on that layer’s error
alone (#2588).
What decides whether a constructed error is loud:
flowchart TD
NEW["a BaseError<br/>is constructed"] --> QUIET{"quiet=True?"}
QUIET -->|yes| SILENT["quiet: nothing logged,<br/>no hook touched"]
QUIET -->|no| CAUGHT{"inside a CaughtByCaller<br/>block naming one<br/>of its classes?"}
CAUGHT -->|yes| SILENT
CAUGHT -->|no| DEV{"PCAPKIT_DEVMODE?"}
DEV -->|yes| DEVLOG["logger.critical, with<br/>traceback and stack<br/>under PCAPKIT_VERBOSE"]
DEV -->|no| LOG["logger.critical"]
LOG --> HOOK["\_install\_excepthook<br/>sys.excepthook and<br/>threading.excepthook"]
SILENT --> RAISE["an ordinary exception<br/>for the caller to catch"]
DEVLOG --> RAISE
HOOK --> RAISE
click CAUGHT "exceptions.html#pcapkit.utilities.exceptions.CaughtByCaller"
Changed in version 1.5.0: quiet=True logs nothing, where 1.4.x logged at ERROR, and neither path
sets sys.tracebacklimit. In 1.4.x a single MultiDict.get() miss gave an ERROR record – one per
frame when parsing unfragmented IPv6 with reassembly on – and truncated unrelated
tracebacks for the rest of the process, though no such record was a fault. To
observe internal lookup misses, catch the exception rather than read the log
(#362).
- exception pcapkit.utilities.exceptions.BaseError(*args, quiet=False, **kwargs)[source]¶
Bases:
ExceptionBase error class of all kinds.
A loud error – the default – is reported once, at
logging.CRITICALlevel, on thepcapkitlogger (logger). Outside development mode it also installs_excepthook()assys.excepthook()and_threading_excepthook()asthreading.excepthook(), the first time a loud error needs them, so that error – and every loud error reaching the top level after it, on the main thread or any other – prints its one-line exception message rather than a walk throughpcapkit’s internals.A quiet error (
quiet=True) is onepcapkitraises as internal control flow and expects to catch itself, such as theMissingKeyErrorbehindMultiDict.get. It is therefore silent and free of side effects: nothing is logged, and neithersys.excepthook()northreading.excepthook()is touched. It is still a perfectly ordinary exception, carrying its message for whoever catches it.An error is also quiet when it is constructed inside a
CaughtByCallerblock naming one of its classes, which is how apcapkitcaller marks as control flow an error raised by code that does not passquiet=Trueitself. The effect is exactly that ofquiet=True.Important
The terseness is deliberately not done by setting
sys.tracebacklimitto0: that is process-global, so one loud error would truncate the tracebacks of every other, unrelated exception for the rest of the process. Each hook here shortens the printing of aBaseErroronly, and hands every other exception, unchanged, to whatever hookpcapkitfound installed before it first needed its own.Outside development mode, importing
pcapkitreplaces both hooks itself, throughtbtrim.set_trim_rule: its hooks cut the traceback of every exception except aBaseErrorat the firstpcapkitframe, and a hook the program installed before the import is replaced. The hooks installed here go over those, the first time a loud error needs them, and hand every other exception to what they replaced. UnderPCAPKIT_DEVMODEneither pair is installed.The hooks still do not cover every path an exception can take. A traceback a caller formats itself with
traceback– rather than letting it reach the top level uncaught – passes through neither (#719).The
stacklevelof the log record is the relative levelstacklevel()computes, so the record is attributed to the caller whose operation failed rather than to this module. It must not be negated:logging.Logger.findCaller()reads that as “do not walk out at all” and would blameloggingitself for every error.
Changed in version 1.5.0: A quiet error logs nothing, where 1.4.x logged it at
ERROR, and no error setssys.tracebacklimit(#362).
- class pcapkit.utilities.exceptions.CaughtByCaller(*types)[source]¶
Bases:
objectConstruct errors of the given classes quietly for the duration of a block.
A
BaseErrorconstructed inside the block that is an instance of one oftypesbehaves exactly as if it had been givenquiet=True: nothing is logged and no exception hook is installed. Every other error stays loud.Use it around a call into code the caller does not own – a generated
_validate_value()override, say – whose named exceptions the caller then catches itself. An error the callee raises is then the quiet control flow it is at that call site, without the callee having to passquiet=True, which it cannot know to do (#1845). Only the named classes are affected: anything else the callee raises escapes the caller and so stays loud.Given no classes, it declares that nothing is caught, which makes errors loud again inside an enclosing block – how
register()keeps its rejection loud whoever calls it.Re-entrant: on exit a block restores whatever the enclosing block declared. An instance holds the token of the one
withit is entered by, so it is built afresh for each. The declaration is per thread and percontextvarscontext, so a block never quiets an error raised on another thread.A class rather than
contextlib.contextmanager(), becauseEnumLookup.getenters one on every call with a non-strkey, and the generator machinery measured at several times the cost of the lookup it would wrap.- Parameters:
*types (
Type[BaseException]) – Exception classes the caller catches.
Added in version 1.5.0.
TypeError Category¶
- exception pcapkit.utilities.exceptions.DigitError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be (a) number(s).
- exception pcapkit.utilities.exceptions.IntError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be integral.
- exception pcapkit.utilities.exceptions.RealError(*args, quiet=False, **kwargs)[source]¶
-
The function is not defined for real numbers.
- exception pcapkit.utilities.exceptions.ComplexError(*args, quiet=False, **kwargs)[source]¶
-
The function is not defined for complex numbers.
- exception pcapkit.utilities.exceptions.BoolError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be
booltype.
- exception pcapkit.utilities.exceptions.BytesError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be
bytestype.
- exception pcapkit.utilities.exceptions.StringError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be
strtype.
- exception pcapkit.utilities.exceptions.BytearrayError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be
bytearraytype.
- exception pcapkit.utilities.exceptions.DictError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be
dicttype.
- exception pcapkit.utilities.exceptions.ListError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be
listtype.
- exception pcapkit.utilities.exceptions.TupleError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be
tupletype.
- exception pcapkit.utilities.exceptions.IterableError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be iterable.
- exception pcapkit.utilities.exceptions.IOObjError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be file-like object.
- exception pcapkit.utilities.exceptions.ProtocolUnbound(*args, quiet=False, **kwargs)[source]¶
-
Protocol slice unbound.
- exception pcapkit.utilities.exceptions.CallableError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be callable.
- exception pcapkit.utilities.exceptions.InfoError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be
Infoinstance.
- exception pcapkit.utilities.exceptions.IPError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be IP address.
- exception pcapkit.utilities.exceptions.EnumError(*args, quiet=False, **kwargs)[source]¶
-
The argument(s) must be enumeration protocol type.
- exception pcapkit.utilities.exceptions.ComparisonError(*args, quiet=False, **kwargs)[source]¶
-
Rich comparison not supported between instances.
AttributeError Category¶
- exception pcapkit.utilities.exceptions.FormatError(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,AttributeErrorUnknown format(s).
- exception pcapkit.utilities.exceptions.UnsupportedCall(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,AttributeErrorUnsupported function or property call.
IOError Category¶
FileExistsError Category¶
- exception pcapkit.utilities.exceptions.FileExists(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,FileExistsError[Errno 17] File already exists.
FileNotFoundError Category¶
- exception pcapkit.utilities.exceptions.FileNotFound(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,FileNotFoundError[Errno 2] File not found.
IndexError Category¶
- exception pcapkit.utilities.exceptions.ProtocolNotFound(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,IndexErrorProtocol not found in ProtoChain.
ValueError Category¶
- exception pcapkit.utilities.exceptions.VersionError(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,ValueErrorUnknown IP version.
- exception pcapkit.utilities.exceptions.IndexNotFound(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,ValueErrorProtocol not in ProtoChain.
- exception pcapkit.utilities.exceptions.ProtocolError(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,ValueErrorInvalid protocol format.
- exception pcapkit.utilities.exceptions.EndianError(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,ValueErrorInvalid endian (byte order).
- exception pcapkit.utilities.exceptions.KeyExists(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,ValueErrorKey already exists.
- exception pcapkit.utilities.exceptions.NoDefaultValue(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,ValueErrorNo default value.
- exception pcapkit.utilities.exceptions.EnumValueError(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,ValueErrorNo member of an enumeration carries this value.
The value-miss half of the pair whose name-miss half is
EnumKeyError; see that one for the stdlibEnumshape both follow, and for why the two halves derive from different builtins.
- exception pcapkit.utilities.exceptions.FieldValueError(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,ValueErrorInvalid field value.
- exception pcapkit.utilities.exceptions.SchemaError(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,ValueErrorInvalid schema.
- exception pcapkit.utilities.exceptions.SeekError(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,ValueErrorInvalid seek position.
- exception pcapkit.utilities.exceptions.TruncateError(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,ValueErrorInvalid truncate size.
- exception pcapkit.utilities.exceptions.VendorPathNotFound(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,ValueErrorCrawler module is not inside the
vendorpackage root.
NotImplementedError Category¶
- exception pcapkit.utilities.exceptions.ProtocolNotImplemented(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,NotImplementedErrorProtocol not implemented.
- exception pcapkit.utilities.exceptions.VendorNotImplemented(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,NotImplementedErrorVendor not implemented.
struct.error Category¶
EOFError Category¶
- exception pcapkit.utilities.exceptions.StreamEOFError(*args, quiet=False, **kwargs)[source]¶
-
Underlying stream exhausted; no data left to read.
Raised by
prepare()when the length of a schema’s read was derived by measuring what is actually left in the stream – rather than declared by the caller – and that measurement came back zero. This is the frame reader’s “no more packets” signal, so it subclassesEOFErrorrather than replacing it: anexcept (EOFError, StopIteration)handler catches it, and a caller that wants to be more specific can catch this instead.A declared zero length – a nested schema legitimately sized to have nothing to read – is a different situation and does not raise this.
FrameandPCAPNGraise it too, quietly, when fewer octets are left than a record header or a block needs.Note
prepare()always raises this withquiet=True: reaching end of stream is the frame reader’s ordinary way of finding out there is nothing left to parse, not a fault to log – the same conventionStructErrorfollows for the same situation via its owneof=True.
KeyError Category¶
- exception pcapkit.utilities.exceptions.MissingKeyError(*args, quiet=False, **kwargs)[source]¶
-
Key not found.
- exception pcapkit.utilities.exceptions.FragmentError(*args, quiet=False, **kwargs)[source]¶
-
Invalid fragment dict.
- exception pcapkit.utilities.exceptions.PacketError(*args, quiet=False, **kwargs)[source]¶
-
Invalid packet dict.
- exception pcapkit.utilities.exceptions.EnumKeyError(*args, quiet=False, **kwargs)[source]¶
-
No member of an enumeration carries this name.
The name-miss half of the pair whose value-miss half is
EnumValueError, and the split between them follows stdlibEnumrather than this package’s own taste:E['nosuch']raisesKeyErrorandE(999)raisesValueError, so a lookup that misses by name isKeyError-derived and one that misses by value isValueError-derived. That is a ruling given in review of #877’s phase-2 re-parenting, carried out by #923: raise whichever of the two stdlibEnumwould raise in the same circumstances, and raise it from this module rather than as a builtin.Deriving from
KeyErroris what makes that ruling cheap to carry out: the in-library call sites catchget()’s name miss asKeyError–Method’sgetcatches it in order to build an unregistered member, so for that one a failed name lookup is part of a successful call – so every one of them catches this as well.Note
Distinct from
MissingKeyError, which is deliberately not reused here: that one reports an absent mapping key, asMultiDictand thepcapkit.toolkitextractors raise it, and conflating the two would leave a caller unable to tell a registry that has no such member from a packet dict that has no such field.Added in version 1.5.0.
ModuleNotFoundError Category¶
- exception pcapkit.utilities.exceptions.ModuleNotFound(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,ModuleNotFoundErrorModule not found.
io.UnsupportedOperation Category¶
- exception pcapkit.utilities.exceptions.UnsupportedOperation(*args, quiet=False, **kwargs)[source]¶
Bases:
BaseError,UnsupportedOperationUnsupported operation.
Error Handling Utilities¶
- pcapkit.utilities.exceptions.stacklevel()[source]¶
Stack level of the innermost frame outside
pcapkit.The value is a relative level, in the sense both
warnings.warn()and theloggingmodule use: level1is the frame that calledstacklevel(), level2its caller, and so on outwards. Handing it to either of them attributes the complaint to the caller who reached intopcapkit, rather than to whicheverpcapkitinternal happened to notice the problem – which is the whole point of the function, since the internal frames are noise to the user reading the report.The arithmetic, since it is easy to get backwards. Number the frames outwards from this one, so that a number is the relative level a consumer wants:
level 0 stacklevel() itself level 1 whoever called stacklevel() ... level boundary the outermost frame inside pcapkit ... level outermost the interpreter entry point
The frame to name is the first one past the boundary, hence
boundary + 1.boundaryis measured from the inside out: it depends only on how deep thepcapkitframes run, never on how deep the caller’s own stack is. Numbering from the outside in would grow with the outer stack, so the frame named would drift one further out for every extra frame above the boundary – under pytest, dozens of them.Both bounds are enforced, as neither consumer copes with a level outside them:
Never below
1.0and negative values mean “do not walk out at all” tologging.Logger.findCaller(), which then attributes the record tologgingitself;warnings.warn()treats them as1.Never above
outermost. That bound is reached when the whole stack is insidepcapkit, e.g. running the package as a script, and walking past the outermost frame makeswarnings.warn()fall back to blaming thesysmodule.
The walk goes through
inspect.currentframe()andf_backrather thantraceback.extract_stack(): it skips building theFrameSummaryobjects and thelinecachelookupstraceback.extract_stack()does for every frame, which is worth avoiding on a function called once per warning. It is also immune tosys.tracebacklimit, whichtraceback.StackSummary.extract()honours: a limit of0would makeextract_stack()return an empty list and silently break the attribution of every warning.BaseErrordoes not set that limit (it prints its terse line through an exception hook instead), but user code may.Important
The level is relative to the caller of
stacklevel(). A function that forwards it towarnings.warn()on its caller’s behalf has to add one for its own frame – seepcapkit.utilities.warnings.warn(), which does exactly that.- Return type:
- Returns:
Number of frames from the caller of
stacklevel()outwards to the innermost frame whose path does not contain/pcapkit/.
- pcapkit.utilities.exceptions._install_excepthook()[source]¶
Install
_excepthook()and_threading_excepthook(), once each.Called from
BaseError’s constructor, outside development mode, the first time a loud error is raised – not at import. What the import leaves in both slots, outside development mode, is the pairtbtrim.set_trim_ruleinstalls frompcapkit’s__init__; this replaces that pair, and the matching_previous_*records it.Idempotent for this module instance, independently for each hook: if a given hook slot already holds this instance’s own function – checked by identity,
is, not by theinstalled_by_pcapkitmarker – installing it again does nothing, so a loud error later in the same run never wraps either hook in another copy of itself. That distinction matters because the identical check is wrong one level up: a hook slot already holding some function of the same name from an earlier loaded copy of this module – a genuineimportlib.reload(), or a test harness re-executing the file fresh – is not this instance’s own hook, and must not be skipped over. A fresh instance installs over it exactly as it would over a host application’s own hook, capturing it as the matching_previous_*and delegating to it for whatever that older instance’s own hook does not claim as one of its ownBaseErrorinstances – which, correctly, includes aBaseErrorraised by that older instance, sinceisinstancedoes not hold across the reload and delegating down the chain is what lets the older instance’s own hook recognise it instead.
- pcapkit.utilities.exceptions._excepthook(etype, value, tb)[source]¶
Print a loud
BaseErrortersely; chain through everything else.Installed by
_install_excepthook(), which is also what records_previous_excepthook. ABaseErrorreaching here is printed withlimit=0against its real traceback – not a bare one-liner built by discarding the traceback outright, which is close but not the same thing.traceback.print_exception()is also what formats a chained exception (raise ... from err, or a bareraiseinside anexceptblock), and it does so by walking from the outermost cause or context inward, applyinglimitto each link’s own frames as it goes. Passingtb=Noneonly ever hands it the top exception’s own traceback – the chain’s inner links keep their real__cause__/__context__traceback attributes regardless, untouched by this call, and get printed in full.limit=0against the realtbis what reaches every link, giving the one-line answer for a plainBaseErrorand a chain without frames for a chained one.Anything else is handed to
_previous_excepthookexactly as received: the hooksys.excepthook()held when_install_excepthook()ran – outside development mode,tbtrim’s from the import, unless the program replaced it after the import – orsys.__excepthook__when none was recorded.
- pcapkit.utilities.exceptions._threading_excepthook(args)[source]¶
Thread analogue of
_excepthook(); same contract, one argument.threading.excepthook()is called with a singlethreading.ExceptHookArgs, carryingexc_type,exc_value,exc_tracebackandthread– not the three positional parameterssys.excepthook()takes. Everything else here mirrors_excepthook()as closely as the default thread hook’s own output allows: a loudBaseErrorprints the"Exception in thread ...:"header the default hook always prints first – the one piece of that outputsys.tracebacklimitdoes not touch, since it only bounds the traceback – then its own message tersely, withlimit=0against the real traceback so a chained exception still comes out right. Anything else is handed to_previous_threading_excepthook– ordefault_threading_excepthook, the interpreter’s own default, if nothing was previously installed – unchanged, header included, since that path does not touch the default hook’s own printing at all.This hook is needed because the interpreter’s default
threading.excepthook()consultssys.tracebacklimit, whichBaseErrordoes not set; without it a loudBaseErrorraised on a worker thread would print a full default traceback.
Footnotes