SDK and error contracts
Python API reference
Python API reference Package wisent errors , import name wisent errors , under python/ ( pyproject.toml there; hence #subdirectory=python in the pip spec). Python ≥ 3.9, zero dependencies. Two modules: wisent errors (builders, render, chain
Python API reference
Package wisent-errors, import name wisent_errors, under python/
(pyproject.toml there; hence #subdirectory=python in the pip spec).
Python ≥ 3.9, zero dependencies. Two modules: wisent_errors (builders,
render, chain, trims, plus every catalogue function re-exported) and
wisent_errors.codes (the generated catalogue module alone). All refusal
sentences below are the exact strings raised by the code.
Builders
failure(*, failure_point, code, service, impact=None, detail=None, cause=None, context=None) → dict
All arguments are keyword-only. Builds an envelope as a plain dict with
severity, retryable, outage filled from the catalogue. Raises
TypeError on anything malformed:
| condition | exact sentence |
|---|---|
failure_point not a non-empty str |
failure_point must be a non-empty string |
failure_point fails the grammar |
failure_point 'Not A Point' is not a dotted lowercase path ({point!r}) |
code not in the catalogue |
code 'panic' is not in the catalogue; one of config, auth, not_found, rate_limit, timeout, infra_down, unknown ({code!r}) |
service not a non-empty str |
service must be a non-empty string |
detail provided but not a non-empty str |
detail must be a non-empty string |
failure_point and service are stripped. impact accepts any value: it
is coerced through str() and stripped, empty becomes None. detail
omitted or None serializes as None; provided, it must be a non-empty
str and is trimmed to DETAIL_LIMIT (2000). cause must be a Mapping
and is shallow-copied with dict(cause) — unlike JS, a FailureError is
not unwrapped: pass error.envelope. context is shallow-copied and
omitted when empty or None.
failure_or_fallback(**fields) → dict
The same, but it never raises. Coercions, each recorded in context:
| violation | result | context note |
|---|---|---|
failure_point absent/empty |
"unknown" |
wisent_errors.failure_point: "absent" |
failure_point malformed |
kept verbatim | wisent_errors.failure_point: "malformed" |
code is None |
"unknown" |
wisent_errors.error_code: "absent" |
code not a str in the catalogue |
"unknown" |
wisent_errors.error_code: "off-catalogue: <code>" |
service absent/empty |
"unknown" |
wisent_errors.service: "absent" |
Every optional is coerced through str() and stripped; detail is trimmed
to DETAIL_LIMIT. Caller-provided context merges under the notes (notes
win on collision).
raise_failure(**fields) → None
raise FailureError(failure(**fields)).
class FailureError(Exception)
envelope carries a copy of the data (dict(envelope)); the exception
message is <failure_point>: <detail or operator_summary(error_code)> —
or, so an empty or absent detail falls back to the catalogue sentence.
Rendering
render(envelope) → str
One line: <operator_summary> — <whose><retry> <JSON> where <whose> is
our failure / the request or its credentials (from outage) and
<retry> is ; retry later / ; retrying will not help (from
retryable). The JSON half is
json.dumps(envelope, separators=(",", ":"), ensure_ascii=False), so it is
byte-identical to the other runtimes; the separator is an em dash (U+2014).
chain(envelope) → list
One row per cause-chain layer, outermost first:
<failure_point> [<error_code>] <detail or '-'> — or, so an empty detail
renders as - too.
Trims
trim_detail(text, limit=DETAIL_LIMIT) → str
Coerces (str(text), None becomes ""), strips both ends, cuts hard at
limit. The width is an argument because it is a product decision; the
rule is the shared part.
trim_detail_at_word_edge(text, limit=DETAIL_LIMIT, slack=24) → str
The same, cut back to the last space when one falls within slack
characters of the bound; the result is right-stripped. Opt-in, because it
changes emitted bytes. The edge > 0 guard matters: rfind returns -1
when there is no space at all, and for any limit under slack a bare
edge > limit - slack silently dropped the last character.
Catalogue module (wisent_errors.codes)
Predicates and coercions
| function | returns |
|---|---|
code_or_none(text) → str | None |
the honest primitive at a wire boundary, where "nothing was declared" and "something unknown was declared" must stay apart |
code_or_fallback(text) → str |
the code, or FALLBACK ("unknown"); never raises |
There is no is_code: code_or_none(text) is not None is the membership
test. Membership is text in CODES on a tuple of strings — Python has no
prototype chain to guard against.
Derivations
| function | returns |
|---|---|
severity(code) → str |
'warning', 'error', or 'critical', from MEANINGS |
retryable(code) → bool |
from MEANINGS |
outage(code) → bool |
from MEANINGS |
operator_summary(code) → str |
the catalogue's per-code sentence |
http_status(code) → int |
the edge status |
exit_code(code, chosen) → int |
RETRY_EXIT (69) when retryable, else chosen |
from_upstream_status(status) → str |
exact matches, then the inclusive 500–599 range → infra_down, else unknown |
The derivation functions index MEANINGS[code] directly and are not
guarded: handed an off-catalogue string they raise a bare KeyError.
Coerce with code_or_fallback or check with code_or_none first at wire
boundaries.
Constants
| constant | value |
|---|---|
CODES |
tuple of the seven codes, catalogue order |
SEVERITIES |
the three severities in order (annotated tuple[str, ...], generated as a list) |
MEANINGS |
per-code frozen dataclass: operator_summary, retryable, outage, severity, http_status |
FALLBACK |
"unknown" |
FAILURE_POINT_PATTERN |
the grammar, as a string |
RETRY_EXIT |
69 |
DETAIL_LIMIT |
2000 (main module only) |
Executed
Every sentence above is exercised by examples/first_envelope.py; its captured output is in integrate/python.