Skip to content
Pyrula

Core

Generated from the type stubs and docstrings. Do not edit by hand.

Avro serde with Confluent Schema Registry integration.

Wire format: [0x00 magic][schema_id: 4 bytes BE][avro payload]

Schema registration is lazy - happens on first serialize() call. Reader schemas are fetched from the registry on first deserialize() per schema ID and cached in memory for the lifetime of this serde.

TLS/mTLS: pass ca_path to trust a custom CA, or cert_path + key_path for mutual TLS (client certificate authentication). Both cert_path and key_path must be set together. TLS args apply only when registry_url is https://.

Encrypted (password-protected) private keys are not supported - key_path must point to an unencrypted PEM. (No equivalent to confluent’s ssl.key.password.)

Usage: serde = AvroRegistrySerde.for_topic(“http://localhost:8081”, “my-topic”, SCHEMA_JSON) result = serde.serialize({“id”: 1, “name”: “alice”}) # registers + framing

serde2 = AvroRegistrySerde("http://localhost:8081", "my-topic-value", SCHEMA_JSON)
# With custom CA + client cert (mTLS):
serde3 = AvroRegistrySerde(
"https://registry.example.com", "my-topic-value", SCHEMA_JSON,
username="user", password="pass",
ca_path="/path/to/ca.pem",
cert_path="/path/to/client.pem",
key_path="/path/to/client-key.pem",
)
for_topic(registry_url: str, topic: str, schema: str, is_key: bool = False, username: Optional[str] = None, password: Optional[str] = None, ca_path: Optional[str] = None, cert_path: Optional[str] = None, key_path: Optional[str] = None) -> AvroRegistrySerde
register(self) -> Ok[int] | Err[SerdeError]
subject(self) -> str
serialize(self, obj: Any) -> Ok[bytes] | Err[SerdeError]
deserialize(self, data: bytes) -> Ok[Any] | Err[SerdeError]
serialize_many(self, items: Iterable[Any]) -> IList
serialize_many_bytes(self, items: Iterable[Any]) -> BytesList
deserialize_many(self, bytes_list: Iterable[bytes] | BytesList) -> IList
deserialize_many_objects(self, bytes_list: Iterable[bytes] | BytesList) -> IList
deserialize_many_from_packed(self, packed: bytes, count: int) -> IList
check_compatibility(self, schema: str, version: Optional[str] = None) -> Ok[bool] | Err[SerdeError]
list_versions(self) -> Ok[list[int]] | Err[SerdeError]
get_schema_by_version(self, version: int) -> Ok[str] | Err[SerdeError]
delete_version(self, version: int) -> Ok[bool] | Err[SerdeError]

serialize(self, obj: Any) -> Ok[bytes] | Err[SerdeError]
deserialize(self, data: bytes) -> Ok[Any] | Err[SerdeError]
serialize_many(self, items: Iterable[Any]) -> IList
serialize_many_bytes(self, items: Iterable[Any]) -> BytesList
deserialize_many(self, bytes_list: Iterable[bytes] | BytesList) -> IList

empty(cls) -> BytesList
from_packed(buf: bytes, count: int) -> BytesList
get(self, index: int) -> Some[bytes] | _Nothing
append(self, item: bytes) -> BytesList
to_list(self) -> list[bytes]
to_packed(self) -> bytes
to_ilist(self) -> IList[bytes]
length(self) -> int
is_empty(self) -> bool

A single per-row decode failure - the dead-letter story.

index is the input index; raw is the original payload bytes, ready to route straight to a DLQ without re-serialization.

Fields:

  • index: int
  • message: str
  • raw: bytes


A typed time-span value backed by an i64 nanosecond count.

Mirrors Scala’s FiniteDuration - a precise, immutable quantity of time with unit-converting constructors and full arithmetic support.

nanos(n: int) -> Duration
micros(n: int) -> Duration
millis(n: int) -> Duration
seconds(n: float) -> Duration
minutes(n: float) -> Duration
hours(n: float) -> Duration
days(n: float) -> Duration
from_timedelta(td: Any) -> Duration
to_nanos(self) -> int
to_millis(self) -> int
to_seconds(self) -> float
to_timedelta(self) -> Any

A value (Ok) or an error (Err). Subclassed by Ok and Err, the same way Scala’s Either is by Right/Left, so Either[A, E] is a real generic and isinstance(x, Either) holds for both. Abstract: build values through the factories below (or Ok/Err directly), not Either(...).

ok(value: T) -> Ok[T]
err(error: E) -> Err[E]
try_of(f: Callable[[], T]) -> Either[T, Exception]
cond(condition: bool, ok: T, err: E) -> Either[T, E]
sequence(values: Iterable[Either[T, E]]) -> Either[list[T], E]
map(self, f: Callable[..., Any]) -> Either[Any, E]
flat_map(self, f: Callable[..., Any]) -> Either[Any, E]
map_err(self, f: Callable[..., Any]) -> Either[T, Any]
recover(self, f: Callable[[E], Any]) -> Either[Any, Any]
recover_with(self, f: Callable[[E], Either[Any, Any]]) -> Either[Any, Any]
or_else(self, alt: Either[Any, Any] | Callable[[], Either[Any, Any]]) -> Either[Any, Any]
fold(self, err: Callable[..., Any], ok: Callable[..., Any]) -> Any
get_or_else(self, default: Any) -> Any
unwrap_or_raise(self, exc: Any = None) -> Any
to_option(self) -> Option[T]
is_ok(self) -> bool
is_err(self) -> bool

Fields:

  • error: E
map(self, f: Callable[..., Any]) -> Err[E]
flat_map(self, f: Callable[..., Any]) -> Err[E]
recover(self, f: Callable[[E], Any]) -> Either[Any, Any]
recover_with(self, f: Callable[[E], Either[Any, Any]]) -> Either[Any, Any]
map_err(self, f: Callable[..., Any]) -> Err[Any]
or_else(self, alt: Either[Any, Any] | Callable[[], Either[Any, Any]]) -> Either[Any, Any]
fold(self, err: Callable[..., Any], ok: Callable[..., Any]) -> Any
get_or_else(self, default: Any) -> Any
unwrap_or_raise(self, exc: Any = None) -> Any
to_option(self) -> _Nothing
is_ok(self) -> bool
is_err(self) -> bool

Fields:

  • error: BaseException
map(self, f: Callable[..., Any]) -> Failure
flat_map(self, f: Callable[..., Any]) -> Failure
recover(self, f: Callable[[Any], Any]) -> Try[Any]
recover_with(self, f: Callable[[Any], Try[Any]]) -> Try[Any]
or_else(self, alt: Try[Any] | Callable[[], Try[Any]]) -> Try[Any]
fold(self, failure: Callable[..., Any], success: Callable[..., Any]) -> Any
get_or_else(self, default: Any) -> Any
to_either(self) -> Err[Any]
is_success(self) -> bool
is_failure(self) -> bool

empty(cls) -> FloatList
map_float(self, f: Callable[[float], float]) -> FloatList
filter(self, pred: Callable[[float], bool]) -> FloatList
fold_left(self, initial: Any, f: Callable[..., Any]) -> Any
add_scalar(self, n: float) -> FloatList
sub_scalar(self, n: float) -> FloatList
mul_scalar(self, n: float) -> FloatList
div_scalar(self, n: float) -> FloatList
abs_vals(self) -> FloatList
clamp_vals(self, lo: float, hi: float) -> FloatList
zip_add(self, other: FloatList) -> FloatList
zip_mul(self, other: FloatList) -> FloatList
sum(self) -> float
min(self) -> float
max(self) -> float
mean(self) -> float
std_dev(self) -> float
to_list(self) -> list[float]
to_ilist(self) -> IList[float]
length(self) -> int
is_empty(self) -> bool

empty(cls) -> IList[Any]
map(self, f: Callable[..., Any]) -> IList[Any]
flat_map(self, f: Callable[..., Any]) -> IList[Any]
filter(self, pred: Callable[..., bool]) -> IList[T]
fold_left(self, initial: Any, f: Callable[..., Any]) -> Any
head(self) -> Some[T] | _Nothing
last(self) -> Some[T] | _Nothing
tail(self) -> IList[T]
find(self, pred: Callable[..., bool]) -> Some[T] | _Nothing
take(self, n: int) -> IList[T]
drop(self, n: int) -> IList[T]
distinct(self) -> IList[T]
zip(self, other: IList[Any]) -> IList[tuple[T, Any]]
append(self, item: T) -> IList[T]
prepend(self, item: T) -> IList[T]
concat(self, other: IList[T]) -> IList[T]
to_list(self) -> list[T]
collect_ok(self) -> IList[Any]
collect_err(self) -> IList[Any]
partition_either(self) -> tuple[IList[Any], IList[Any]]
length(self) -> int
is_empty(self) -> bool

empty(cls) -> IMap[Any, Any]
get(self, key: K) -> Some[V] | _Nothing
set(self, key: K, value: V) -> IMap[K, V]
remove(self, key: K) -> IMap[K, V]
contains(self, key: K) -> bool
to_dict(self) -> dict
keys(self) -> IList[K]
values(self) -> IList[V]
items(self) -> IList[tuple[K, V]]
map_values(self, f: Callable[[V], Any]) -> IMap[K, Any]
filter_values(self, pred: Callable[[V], bool]) -> IMap[K, V]
merge(self, other: IMap[K, V]) -> IMap[K, V]
length(self) -> int
is_empty(self) -> bool

empty(cls) -> ISet[Any]
add(self, item: T) -> ISet[T]
remove(self, item: T) -> ISet[T]
contains(self, item: T) -> bool
union(self, other: ISet[T]) -> ISet[T]
intersection(self, other: ISet[T]) -> ISet[T]
difference(self, other: ISet[T]) -> ISet[T]
is_subset_of(self, other: ISet[T]) -> bool
map(self, f: Callable[[T], Any]) -> ISet[Any]
filter(self, pred: Callable[[T], bool]) -> ISet[T]
to_set(self) -> set[T]
to_ilist(self) -> IList[T]
size(self) -> int
is_empty(self) -> bool

empty(cls) -> IntList
map_int(self, f: Callable[[int], int]) -> IntList
filter(self, pred: Callable[[int], bool]) -> IntList
fold_left(self, initial: Any, f: Callable[..., Any]) -> Any
add_scalar(self, n: int) -> IntList
sub_scalar(self, n: int) -> IntList
mul_scalar(self, n: int) -> IntList
div_scalar(self, n: int) -> IntList
abs_vals(self) -> IntList
clamp_vals(self, lo: int, hi: int) -> IntList
zip_add(self, other: IntList) -> IntList
zip_mul(self, other: IntList) -> IntList
sum(self) -> int
min(self) -> int
max(self) -> int
mean(self) -> float
std_dev(self) -> float
to_list(self) -> list[int]
to_ilist(self) -> IList[int]
length(self) -> int
is_empty(self) -> bool

empty(cls) -> IntSet
add(self, value: int) -> IntSet
remove(self, value: int) -> IntSet
contains(self, value: int) -> bool
union(self, other: IntSet) -> IntSet
intersection(self, other: IntSet) -> IntSet
difference(self, other: IntSet) -> IntSet
symmetric_difference(self, other: IntSet) -> IntSet
is_subset(self, other: IntSet) -> bool
is_disjoint(self, other: IntSet) -> bool
cardinality(self) -> int
to_list(self) -> list[int]
to_bytes(self) -> bytes
from_bytes(data: bytes) -> IntSet
is_empty(self) -> bool

Fields:

  • errors: list[E]

JSON Schema serde with Confluent Schema Registry integration.

Wire format: [0x00 magic][schema_id: 4 bytes BE][json payload]

Same magic byte as Avro; content is JSON. NOTE: payloads are NOT validated against the JSON Schema - this serde provides Confluent wire framing ([0x00][schema_id:4B BE][json]) and registry registration only. (confluent’s JSONSerializer validates; pyrula does not yet.)

for_topic(registry_url: str, topic: str, schema: str, is_key: bool = False, username: Optional[str] = None, password: Optional[str] = None, ca_path: Optional[str] = None, cert_path: Optional[str] = None, key_path: Optional[str] = None) -> JsonSchemaRegistrySerde
register(self) -> Ok[int] | Err[SerdeError]
subject(self) -> str
serialize(self, obj: Any) -> Ok[bytes] | Err[SerdeError]
deserialize(self, data: bytes) -> Ok[Any] | Err[SerdeError]
serialize_many(self, items: Iterable[Any]) -> IList
serialize_many_bytes(self, items: Iterable[Any]) -> BytesList
deserialize_many(self, bytes_list: Iterable[bytes] | BytesList) -> IList
deserialize_many_objects(self, bytes_list: Iterable[bytes] | BytesList) -> IList
deserialize_many_from_packed(self, packed: bytes, count: int) -> IList
check_compatibility(self, schema: str, version: Optional[str] = None) -> Ok[bool] | Err[SerdeError]
list_versions(self) -> Ok[list[int]] | Err[SerdeError]
get_schema_by_version(self, version: int) -> Ok[str] | Err[SerdeError]
delete_version(self, version: int) -> Ok[bool] | Err[SerdeError]

serialize(self, obj: Any) -> Ok[bytes] | Err[SerdeError]
deserialize(self, data: bytes) -> Ok[Any] | Err[SerdeError]
serialize_many(self, items: Iterable[Any]) -> IList
serialize_many_raw(self, items: Iterable[Any]) -> BytesList
deserialize_many(self, bytes_list: Iterable[bytes] | BytesList) -> IList
deserialize_many_raw(self, bytes_list: Iterable[bytes] | BytesList) -> IList

A list with at least one element.

head, last, and reduce are total - they return T, not Option[T], because the non-empty invariant is enforced at construction time. from_list is the safe boundary: it returns Option[NonEmptyList[T]].

of(head: T, *tail: T) -> NonEmptyList[T]
from_list(items: IList[T] | list[T]) -> Option[NonEmptyList[T]]
head(self) -> T
tail(self) -> IList[T]
last(self) -> T
map(self, f: Callable[[T], Any]) -> NonEmptyList[Any]
flat_map(self, f: Callable[[T], NonEmptyList[Any]]) -> NonEmptyList[Any]
reduce(self, f: Callable[[T, T], T]) -> T
fold_left(self, init: Any, f: Callable[[Any, T], Any]) -> Any
append(self, item: T) -> NonEmptyList[T]
prepend(self, item: T) -> NonEmptyList[T]
to_ilist(self) -> IList[T]
to_list(self) -> list[T]
length(self) -> int

Fields:

  • value: T
map(self, f: Callable[..., Any]) -> Ok[Any]
flat_map(self, f: Callable[..., Any]) -> Any
map_err(self, f: Callable[..., Any]) -> Ok[T]
recover(self, f: Callable[[Any], Any]) -> Ok[T]
recover_with(self, f: Callable[[Any], Either[Any, Any]]) -> Ok[T]
or_else(self, alt: Either[Any, Any] | Callable[[], Either[Any, Any]]) -> Ok[T]
fold(self, err: Callable[..., Any], ok: Callable[..., Any]) -> Any
get_or_else(self, default: Any) -> T
unwrap_or_raise(self, exc: Any = None) -> T
to_option(self) -> Some[T]
is_ok(self) -> bool
is_err(self) -> bool

Some(value) or Nothing; subclassed by both like Scala’s Option.

of(value: Optional[T]) -> Option[T]
when(condition: bool, value: T) -> Option[T]
from_callable(f: Callable[[], T]) -> Option[T]
sequence(values: Iterable[Option[T]]) -> Option[list[T]]
map(self, f: Callable[..., Any]) -> Option[Any]
flat_map(self, f: Callable[..., Any]) -> Option[Any]
filter(self, pred: Callable[..., bool]) -> Option[T]
get_or_else(self, default: Any) -> Any
or_else(self, alt: Option[Any] | Callable[[], Option[Any]]) -> Option[Any]
fold(self, nothing_val: Any, some_fn: Callable[..., Any]) -> Any
to_either(self, left: Any) -> Either[T, Any]
to_list(self) -> IList[T]
is_some(self) -> bool
is_nothing(self) -> bool
is_empty(self) -> bool

Protobuf serde with Confluent Schema Registry integration.

Wire format: [0x00 magic][schema_id: 4 bytes BE][msg-index zigzag varint array][protobuf payload]

Accepts a serialized FileDescriptorSet and a fully-qualified message name. Deserialization decodes to Python dicts using prost-reflect; no compiled message types needed. Registration requires proto_source (the .proto source text) so the Confluent SR can parse and validate the schema.

LIMITATION: the Confluent message-index is always written as [0] (first message). Only correct when message_name is the first top-level message in the descriptor; multi-message / nested .proto files will not interoperate with Confluent consumers that rely on the message index. (Tracked: K10 follow-up C3a.)

NOTE: proto3 implicit-presence scalar fields at their default (0, "", false) are omitted on deserialize - absent from the returned dict (standard protobuf semantics). Use optional fields if presence must be preserved.

NOTE: enum fields accept the enum value name (str) or number (int) on serialize; deserialize returns the number (int).

for_topic(registry_url: str, topic: str, file_descriptor_set: bytes, message_name: str, is_key: bool = False, proto_source: Optional[str] = None, username: Optional[str] = None, password: Optional[str] = None, ca_path: Optional[str] = None, cert_path: Optional[str] = None, key_path: Optional[str] = None) -> ProtobufRegistrySerde
register(self) -> Ok[int] | Err[SerdeError]
subject(self) -> str
serialize(self, obj: Any) -> Ok[bytes] | Err[SerdeError]
serialize_with_id(self, obj: Any, schema_id: int) -> Ok[bytes] | Err[SerdeError]
deserialize(self, data: bytes) -> Ok[Any] | Err[SerdeError]
serialize_many(self, items: Iterable[Any]) -> IList
serialize_many_bytes(self, items: Iterable[Any]) -> BytesList
deserialize_many(self, bytes_list: Iterable[bytes] | BytesList) -> IList
deserialize_many_objects(self, bytes_list: Iterable[bytes] | BytesList) -> IList
deserialize_many_from_packed(self, packed: bytes, count: int) -> IList
check_compatibility(self, proto_source: str, version: Optional[str] = None) -> Ok[bool] | Err[SerdeError]
list_versions(self) -> Ok[list[int]] | Err[SerdeError]
get_schema_by_version(self, version: int) -> Ok[str] | Err[SerdeError]
delete_version(self, version: int) -> Ok[bool] | Err[SerdeError]

A lazy view of one row in a :class:RecordList.

Field access materializes exactly one value - attribute for ergonomics (rec.price), subscript for certainty (rec["price"] reaches fields that collide with method names or are non-identifiers). to_instance rebuilds the whole originating object.

to_instance(self) -> Any

Immutable columnar batch of typed records (struct-of-arrays).

Records live in Rust; fields become Python objects only when a callable touches one. Built by RecordSchema.deserialize_many (JSON) or RecordList.from_instances (Python objects).

Fields:

  • schema: RecordSchema
from_instances(first: Any, second: Any = ...) -> RecordList
concat(lists: Any) -> RecordList
from_arrow(obj: Any, tp: Any = ...) -> RecordList
from_csv(data: bytes | str, tp: Any, delimiter: str = ..., quote: str = ..., header: bool | list[str] = ...) -> RecordList
map(self, fn: Any) -> Any
filter(self, fn: Any) -> RecordList
fold(self, field_or_fn: Any, monoid: Any) -> Any
serialize_many(self) -> BytesList
column(self, name: str) -> Any
to_ilist(self) -> IList[Record]
to_instances(self) -> list[Any]
to_scalars(self) -> list[Any]
to_arrow(self, json_as_string: bool = ...) -> ArrowTable

Ordered typed fields for a columnar record batch.

Build from a Python type via schema_for (binds the type for to_instance), or hand-build recursive schemas with of.

Fields:

  • fields: list[tuple[str, str, bool]]
of(fields: list[tuple]) -> RecordSchema
has_default(self, name: str) -> bool
deserialize_many(self, data: Any) -> Ok[RecordList] | Err[DeserializationError]
deserialize_many_partial(self, data: Any) -> tuple[RecordList, IList[DecodeFailure]]
deserialize_csv_many(self, lines: Any, delimiter: str = ..., quote: str = ..., header: bool | list[str] = ...) -> Ok[RecordList] | Err[DeserializationError]
deserialize_csv_many_partial(self, lines: Any, delimiter: str = ..., quote: str = ..., header: bool | list[str] = ...) -> tuple[RecordList, IList[DecodeFailure]]



Fields:

  • value: T
map(self, f: Callable[..., Any]) -> Some[Any]
flat_map(self, f: Callable[..., Any]) -> Any
filter(self, pred: Callable[..., bool]) -> Any
get_or_else(self, default: Any) -> T
or_else(self, alt: Option[Any] | Callable[[], Option[Any]]) -> Some[T]
fold(self, nothing_val: Any, some_fn: Callable[..., Any]) -> Any
to_either(self, left: Any) -> Ok[T]
to_list(self) -> IList[T]
is_some(self) -> bool
is_nothing(self) -> bool
is_empty(self) -> bool

Fields:

  • value: T
map(self, f: Callable[..., Any]) -> Any
flat_map(self, f: Callable[..., Any]) -> Any
recover(self, f: Callable[[Any], Any]) -> Success[T]
recover_with(self, f: Callable[[Any], Try[Any]]) -> Success[T]
or_else(self, alt: Try[Any] | Callable[[], Try[Any]]) -> Success[T]
fold(self, failure: Callable[..., Any], success: Callable[..., Any]) -> Any
get_or_else(self, default: Any) -> T
to_either(self) -> Ok[T]
is_success(self) -> bool
is_failure(self) -> bool

Success(value) or Failure(exc); subclassed by both like Scala’s Try.

apply(f: Callable[[], T]) -> Try[T]
of(f: Callable[[], T]) -> Try[T]
sequence(values: Iterable[Try[T]]) -> Try[list[T]]
map(self, f: Callable[..., Any]) -> Try[Any]
flat_map(self, f: Callable[..., Any]) -> Try[Any]
recover(self, f: Callable[[Any], Any]) -> Try[Any]
recover_with(self, f: Callable[[Any], Try[Any]]) -> Try[Any]
or_else(self, alt: Try[Any] | Callable[[], Try[Any]]) -> Try[Any]
fold(self, failure: Callable[..., Any], success: Callable[..., Any]) -> Any
get_or_else(self, default: Any) -> Any
to_either(self) -> Either[T, Any]
is_success(self) -> bool
is_failure(self) -> bool

Fields:

  • value: T

Like Either, but zip accumulates errors instead of short-circuiting.

valid(value: T) -> Validated[T, E]
invalid(error: E) -> Validated[T, E]
from_either(either: Either[T, E]) -> Validated[T, E]
cond(condition: bool, value: T, error: E) -> Validated[T, E]
map(self, f: Callable[[T], Any]) -> Validated[Any, E]
map_err(self, f: Callable[[list[E]], Any]) -> Validated[T, Any]
zip(self, other: Validated[Any, E]) -> Validated[Any, E]
fold(self, invalid: Callable[[list[E]], Any], valid: Callable[[T], Any]) -> Any
get_or_else(self, default: T) -> T
to_either(self) -> Either[T, list[E]]
is_valid(self) -> bool
is_invalid(self) -> bool
  • Nothing: _Nothing = nothing()
case(cls: type[T]) -> type[T]
do(func: Callable[..., Any]) -> Callable[..., Any]
do_async(func: Callable[..., Any]) -> Callable[..., Any]
flow(*funcs: Callable[..., Any]) -> Callable[..., Any]
match(value: Any) -> Callable[..., Any]
newtype(name: str, base_type: type[T]) -> type[T]
newtype_strict(name: str, base_type: type[T], validate: Optional[Callable[..., bool]] = None, err_msg: Optional[str] = None) -> type[T]
on(typ: type[T], handler: Callable[..., Any]) -> Any
partial(f: Callable[..., Any], *args: Any, **kwargs: Any) -> Callable[..., Any]
pipe(value: Any, *funcs: Callable[..., Any]) -> Any
schema_for(tp: type) -> RecordSchema

Derive a :class:RecordSchema from a dataclass, pydantic v2 model, TypedDict, or NamedTuple.

The result is cached per type (schema_for(T) is schema_for(T)) and binds the originating type onto the schema so Record.to_instance can rebuild it.

See the module docstring for the full set of supported/rejected annotation forms; unsupported types raise :class:SchemaResolutionError (a TypeError).

sealed(cls: type[T]) -> type[T]