edri.api.headerkit package

Submodules

edri.api.headerkit.core module

class edri.api.headerkit.core.Headers(raw: ~collections.abc.Iterable[tuple[bytes, bytes]], *, registry: ~edri.api.headerkit.registry.Registry | None = None, policy: ~edri.api.headerkit.policy.HeaderPolicy = HeaderPolicy(mode=<ParseMode.STRICT: 'strict'>, unknown=<UnknownHeaderMode.RAW: 'raw'>, max_total_bytes=65536, max_field_lines=256, allow_obs_text=True, strip_ows=True), http_version: str = '1.1')[source]

Bases: object

Lossless, lazy, typed HTTP header accessor for ASGI scopes.

The object keeps the original ASGI byte pairs intact while indexing by lowercase header name. Semantic parsing is opt-in and lazy through the registry.

property accept: Accept
property accept_encoding: tuple[Any, ...]
property accept_language: tuple[Any, ...]
all(name: bytes | str) → tuple[str, ...][source]
property authorization: Authorization | None
property cache_control: CacheControl
combined(name: bytes | str) → bytes | None[source]
property content_length: int | None
property content_type: MediaType | None
property cookies: Cookies
property date: DateHeader | None
decode_value(value: bytes) → str[source]
property etag: EntityTag | None
first(name: bytes | str) → str | None[source]
first(name: bytes | str, default: T) → str | T
classmethod from_scope(scope: ~collections.abc.Mapping[str, ~typing.Any], *, registry: ~edri.api.headerkit.registry.Registry | None = None, policy: ~edri.api.headerkit.policy.HeaderPolicy = HeaderPolicy(mode=<ParseMode.STRICT: 'strict'>, unknown=<UnknownHeaderMode.RAW: 'raw'>, max_total_bytes=65536, max_field_lines=256, allow_obs_text=True, strip_ows=True)) → Headers[source]
property host: str | None
property if_none_match: EntityTagSet
list_members(name: bytes | str) → tuple[str, ...][source]
names() → tuple[bytes, ...][source]
static normalize_name(name: bytes | str) → bytes[source]
parse(name: bytes | str) → Any[source]
property range: Range | None
property raw: tuple[tuple[bytes, bytes], ...]

Original ASGI byte pairs, with original casing/order/duplicates.

raw_all(name: bytes | str) → tuple[bytes, ...][source]
singleton(name: bytes | str) → str | None[source]
singleton_raw(name: bytes | str) → bytes | None[source]

edri.api.headerkit.defaults module

edri.api.headerkit.errors module

exception edri.api.headerkit.errors.DuplicateHeader[source]

Bases: HeaderError

A header was repeated where the selected policy requires a singleton.

exception edri.api.headerkit.errors.HeaderError[source]

Bases: ValueError

Base class for header parsing/access errors.

exception edri.api.headerkit.errors.HeaderTooLarge[source]

Bases: HeaderError

The configured aggregate header-size limit was exceeded.

exception edri.api.headerkit.errors.InvalidHeader[source]

Bases: HeaderError

Header name or value is syntactically invalid at the generic HTTP layer.

exception edri.api.headerkit.errors.ParseError[source]

Bases: HeaderError

A semantically known header failed to parse.

edri.api.headerkit.grammar module

edri.api.headerkit.parsers module

edri.api.headerkit.policy module

class edri.api.headerkit.policy.HeaderPolicy(mode: 'ParseMode' = <ParseMode.STRICT: 'strict'>, unknown: 'UnknownHeaderMode' = <UnknownHeaderMode.RAW: 'raw'>, max_total_bytes: 'int' = 65536, max_field_lines: 'int' = 256, allow_obs_text: 'bool' = True, strip_ows: 'bool' = True)[source]

Bases: object

class edri.api.headerkit.policy.ParseMode(*values)[source]

Bases: str, Enum

COMPAT = 'compat'
STRICT = 'strict'
class edri.api.headerkit.policy.UnknownHeaderMode(*values)[source]

Bases: str, Enum

COMBINE = 'combine'
RAW = 'raw'
REJECT_DUPES = 'reject-dupes'

edri.api.headerkit.registry module

class edri.api.headerkit.registry.FieldSpec(name: 'bytes', combine: 'CombineMode', parser: 'Parser | None' = None, allow_duplicate_identical: 'bool' = False, request: 'bool' = True, response: 'bool' = True, description: 'str' = '')[source]

Bases: object

class edri.api.headerkit.registry.Registry(specs: list[FieldSpec] | None = None)[source]

Bases: object

copy() → Registry[source]
get(name: bytes) → FieldSpec | None[source]
register(spec: FieldSpec) → None[source]

edri.api.headerkit.response module

class edri.api.headerkit.response.ResponseHeaders(raw: Iterable[tuple[bytes, bytes]] = ())[source]

Bases: object

Builder for ASGI response header byte pairs.

It deliberately treats Set-Cookie as append-only and never coalesces it.

append(name: bytes | str, value: bytes | str | int) → None[source]
asgi() → list[tuple[bytes, bytes]][source]
extend(raw: Iterable[tuple[bytes, bytes]]) → None[source]
remove(name: bytes | str) → None[source]
set(name: bytes | str, value: bytes | str | int) → None[source]
set_content_type(media_type: str, **params: str) → None[source]
set_date(name: str, value: datetime | None = None) → None[source]

edri.api.headerkit.types module

class edri.api.headerkit.types.Accept(items: 'tuple[AcceptItem, ...]')[source]

Bases: object

best_match(offers: Iterable[str], default: str | None = None) → str | None[source]
class edri.api.headerkit.types.AcceptItem(media: 'MediaType', q: 'float' = 1.0, accept_ext: 'Mapping[str, str] | None' = None, order: 'int' = 0)[source]

Bases: object

property acceptable: bool
property specificity: int
property value: str
class edri.api.headerkit.types.Authorization(scheme: 'str', credentials: 'str')[source]

Bases: object

class edri.api.headerkit.types.ByteRange(start: 'int | None', end: 'int | None')[source]

Bases: object

class edri.api.headerkit.types.CacheControl(directives: 'Mapping[str, str | bool]')[source]

Bases: object

get(key: str, default: str | bool | None = None) → str | bool | None[source]
property max_age: int | None
property no_cache: bool
property no_store: bool
class edri.api.headerkit.types.CombineMode(*values)[source]

Bases: str, Enum

COMMA_LIST = 'comma-list'
COOKIE = 'cookie'
NEVER_COMBINE = 'never-combine'
OPAQUE = 'opaque'
SINGLETON = 'singleton'
class edri.api.headerkit.types.Cookies(pairs: 'tuple[tuple[str, str], ...]')[source]

Bases: object

as_dict() → dict[str, str][source]
dechunked() → Cookies[source]

Reassemble Auth.js / NextAuth chunked cookies.

When a cookie value exceeds the ~4 KB browser limit, Auth.js splits it into name.0, name.1, … and the browser sends each chunk back as a separate cookie. This rejoins a contiguous family starting at .0 into a single name cookie (values concatenated in index order).

A non-chunked cookie of the same base name takes precedence (its chunk siblings are then left untouched). Chunk families with a missing .0 or a gap keep their unconsumed members as literal cookies, so nothing is silently dropped.

get(name: str, default: str | None = None) → str | None[source]
get_all(name: str) → tuple[str, ...][source]
class edri.api.headerkit.types.DateHeader(value: 'datetime')[source]

Bases: object

class edri.api.headerkit.types.EntityTag(tag: 'str', weak: 'bool' = False)[source]

Bases: object

class edri.api.headerkit.types.EntityTagSet(any: 'bool', tags: 'tuple[EntityTag, ...]' = ())[source]

Bases: object

class edri.api.headerkit.types.Forwarded(elements: 'tuple[ForwardedElement, ...]')[source]

Bases: object

class edri.api.headerkit.types.ForwardedElement(params: 'Mapping[str, str]')[source]

Bases: object

class edri.api.headerkit.types.MediaType(type: 'str', subtype: 'str', params: 'Mapping[str, str]')[source]

Bases: object

property essence: str
matches(candidate: str) → bool[source]
class edri.api.headerkit.types.Range(unit: 'str', ranges: 'tuple[ByteRange, ...]')[source]

Bases: object

class edri.api.headerkit.types.Weighted(value: 'str', q: 'float' = 1.0, params: 'Mapping[str, str] | None' = None, order: 'int' = 0)[source]

Bases: object

property acceptable: bool

Module contents