No project description provided
Project description
serpyco-rs: a serializer for python dataclasses
What is serpyco-rs ?
Serpyco is a serialization library for Python 3.9+ dataclasses that works just by defining your dataclasses:
import dataclasses
import serpyco_rs
@dataclasses.dataclass
class Example:
name: str
num: int
tags: list[str]
serializer = serpyco_rs.Serializer(Example)
result = serializer.dump(Example(name="foo", num=2, tags=["hello", "world"]))
print(result)
>> {'name': 'foo', 'num': 2, 'tags': ['hello', 'world']}
Inspired by serpyco.
serpyco-rs works by analysing the dataclass fields and can recognize many types : list
, tuple
, Optional
...
You can also embed other dataclasses in a definition.
The main use-case for serpyco-rs is to serialize objects for an API, but it can be helpful whenever you need to transform objects to/from builtin Python types.
Installation
Use pip to install:
$ pip install serpyco-rs
Features
- Serialization and deserialization of dataclasses
- Validation of input data
- Very fast
- Support recursive schemas
- Generate JSON Schema Specification (Draft 2020-12)
- Support custom encoders/decoders for fields
Supported field types
There is support for generic types from the standard typing module:
- Decimal
- UUID
- Time
- Date
- DateTime
- Enum
- List
- Dict
- Bytes (pass through)
- TypedDict
- Mapping
- Sequence
- Tuple (fixed size)
- Literal[str, ...]
- Tagged unions (restricted)
Benchmarks
Linux
Load
Library | Median latency (milliseconds) | Operations per second | Relative (latency) |
---|---|---|---|
serpyco_rs | 0.16 | 6318.1 | 1 |
mashumaro | 0.45 | 2244.4 | 2.81 |
pydantic | 0.57 | 1753.9 | 3.56 |
serpyco | 0.82 | 1228.3 | 5.17 |
marshmallow | 8.49 | 117.4 | 53.35 |
Dump
Library | Median latency (milliseconds) | Operations per second | Relative (latency) |
---|---|---|---|
serpyco_rs | 0.07 | 13798 | 1 |
serpyco | 0.07 | 13622 | 1.02 |
mashumaro | 0.1 | 10219.5 | 1.36 |
pydantic | 0.22 | 4615.5 | 2.99 |
marshmallow | 2 | 497 | 27.69 |
MacOS
macOS Monterey / Apple M1 Pro / 16GB RAM / Python 3.11.0Load
Library | Median latency (milliseconds) | Operations per second | Relative (latency) |
---|---|---|---|
serpyco_rs | 0.1 | 9865.1 | 1 |
mashumaro | 0.2 | 4968 | 2 |
pydantic | 0.34 | 2866.7 | 3.42 |
serpyco | 0.69 | 1444.1 | 6.87 |
marshmallow | 4.14 | 241.8 | 41.05 |
Dump
Library | Median latency (milliseconds) | Operations per second | Relative (latency) |
---|---|---|---|
serpyco_rs | 0.04 | 22602.6 | 1 |
serpyco | 0.05 | 21232.9 | 1.06 |
mashumaro | 0.06 | 15903.4 | 1.42 |
pydantic | 0.16 | 6262.6 | 3.61 |
marshmallow | 1.04 | 962 | 23.5 |
Supported annotations
serpyco-rs
supports changing load/dump behavior with typing.Annotated
.
Currently available:
- Alias
- FieldFormat (CamelCase / NoFormat)
- NoneFormat (OmitNone / KeepNone)
- Discriminator
- Min / Max
- MinLength / MaxLength
- CustomEncoder
- NoneAsDefaultForOptional (ForceDefaultForOptional)
Alias
Alias
is needed to override the field name in the structure used for load
/ dump
.
from dataclasses import dataclass
from typing import Annotated
from serpyco_rs import Serializer
from serpyco_rs.metadata import Alias
@dataclass
class A:
foo: Annotated[int, Alias('bar')]
ser = Serializer(A)
print(ser.load({'bar': 1}))
>> A(foo=1)
print(ser.dump(A(foo=1)))
>> {'bar': 1}
FieldFormat
Used to have response bodies in camelCase while keeping your python code in snake_case.
from dataclasses import dataclass
from typing import Annotated
from serpyco_rs import Serializer
from serpyco_rs.metadata import CamelCase, NoFormat
@dataclass
class B:
buz_filed: str
@dataclass
class A:
foo_filed: int
bar_filed: Annotated[B, NoFormat]
ser = Serializer(Annotated[A, CamelCase]) # or ser = Serializer(A, camelcase_fields=True)
print(ser.dump(A(foo_filed=1, bar_filed=B(buz_filed='123'))))
>> {'fooFiled': 1, 'barFiled': {'buz_filed': '123'}}
print(ser.load({'fooFiled': 1, 'barFiled': {'buz_filed': '123'}}))
>> A(foo_filed=1, bar_filed=B(buz_filed='123'))
NoneFormat
Via OmitNone
we can drop None values for non required fields in the serialized dicts
from dataclasses import dataclass
from serpyco_rs import Serializer
@dataclass
class A:
required_val: bool | None
optional_val: bool | None = None
ser = Serializer(A, omit_none=True) # or Serializer(Annotated[A, OmitNone])
print(ser.dump(A(required_val=None, optional_val=None)))
>>> {'required_val': None}
Tagged unions
Supports tagged joins with discriminator field.
All classes in the union must be dataclasses or attrs with discriminator field Literal[str]
.
The discriminator field is always mandatory.
from typing import Annotated, Literal
from dataclasses import dataclass
from serpyco_rs import Serializer
from serpyco_rs.metadata import Discriminator
@dataclass
class Foo:
type: Literal['foo']
value: int
@dataclass(kw_only=True)
class Bar:
type: Literal['bar'] = 'bar'
value: str
ser = Serializer(list[Annotated[Foo | Bar, Discriminator('type')]])
print(ser.load([{'type': 'foo', 'value': 1}, {'type': 'bar', 'value': 'buz'}]))
>>> [Foo(type='foo', value=1), Bar(type='bar', value='buz')]
Min / Max
Supported for int
/ float
/ Decimal
types and only for validation on load.
from typing import Annotated
from serpyco_rs import Serializer
from serpyco_rs.metadata import Min, Max
ser = Serializer(Annotated[int, Min(1), Max(10)])
ser.load(123)
>> SchemaValidationError: [ErrorItem(message='123 is greater than the maximum of 10', instance_path='')]
MinLength / MaxLength
MinLength
/ MaxLength
can be used to restrict the length of loaded strings.
from typing import Annotated
from serpyco_rs import Serializer
from serpyco_rs.metadata import MinLength
ser = Serializer(Annotated[str, MinLength(5)])
ser.load("1234")
>> SchemaValidationError: [ErrorItem(message='"1234" is shorter than 5 characters', instance_path='')]
NoneAsDefaultForOptional
ForceDefaultForOptional
/ KeepDefaultForOptional
can be used to set None as default value for optional (nullable) fields.
from dataclasses import dataclass
from serpyco_rs import Serializer
@dataclass
class Foo:
val: int # not nullable + required
val1: int | None # nullable + required
val2: int | None = None # nullable + not required
ser_force_default = Serializer(Foo, force_default_for_optional=True) # or Serializer(Annotated[Foo, ForceDefaultForOptional])
ser = Serializer(Foo)
# all fields except val are optional and nullable
assert ser_force_default.load({'val': 1}) == Foo(val=1, val1=None, val2=None)
# val1 field is required and nullable and val1 should be present in the dict
ser.load({'val': 1})
>> SchemaValidationError: [ErrorItem(message='"val1" is a required property', instance_path='')]
Custom encoders for fields
You can provide CustomEncoder with serialize
and deserialize
functions, or serialize_with
and deserialize_with
annotations.
from typing import Annotated
from dataclasses import dataclass
from serpyco_rs import Serializer
from serpyco_rs.metadata import CustomEncoder
@dataclass
class Foo:
val: Annotated[str, CustomEncoder[str, str](serialize=str.upper, deserialize=str.lower)]
ser = Serializer(Foo)
val = ser.dump(Foo(val='bar'))
>> {'val': 'BAR'}
assert ser.load(val) == Foo(val='bar')
Note: CustomEncoder
has no effect to validation and JSON Schema generation.
Bytes fields
serpyco-rs
can loads bytes fields as is (without base64 encoding and validation).
from dataclasses import dataclass
from serpyco_rs import Serializer
@dataclass
class Foo:
val: bytes
ser = Serializer(Foo)
ser.load({'val': b'123'}) == Foo(val=b'123')
Getting JSON Schema
serpyco-rs
can generate JSON Schema for your dataclasses (Draft 2020-12).
from dataclasses import dataclass
from serpyco_rs import Serializer
@dataclass
class A:
"""Description of A"""
foo: int
bar: str
ser = Serializer(A)
print(ser.get_json_schema())
>> {
'$schema': 'https://json-schema.org/draft/2020-12/schema',
'$ref': '#/components/schemas/A[no_format,keep_nones]',
'components': {
'schemas': {
'A[no_format,keep_nones]': {
'properties': {
'foo': {'type': 'integer'},
'bar': {'type': 'string'}
},
'required': ['foo', 'bar'],
'type': 'object',
'description': 'Description of A'
}
}
}
}
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Hashes for serpyco_rs-1.1.0-cp312-none-win_amd64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 10cc97d0ce06ec0f91e1494c315176ea2ca3bf3d72ae449c0f09be34d99e085b |
|
MD5 | 31cbe837af99b2c945561bcfb0a976cb |
|
BLAKE2b-256 | d5dd0e275d5072e7a439703c0cf258b314b52e0b72584ff2380d2ac9c4028ce9 |
Hashes for serpyco_rs-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0432c6d84e4d612334183f4f13b7993a186e125b80dfc4eb1e488ed7cb228c4b |
|
MD5 | e77688edf84e5de5e4ede18d450a8bf5 |
|
BLAKE2b-256 | 5dfbbd291d6a82cd695fc07ff20cce753c80f487105e3ba423e28aa04aba6551 |
Hashes for serpyco_rs-1.1.0-cp312-cp312-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 491304bedb20effc0d016f341892fc615783a8b4ba7a46b4dec4a32cb8206cc8 |
|
MD5 | 2cde36920d312c7720512bdfe4b6ba2e |
|
BLAKE2b-256 | f69479e1f1ca5a0643995374e60fd70400c2852a25ff8628376d10193d8c8d7a |
Hashes for serpyco_rs-1.1.0-cp311-none-win_amd64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 34ecf26a3bb53a103598141a351315ee2bf612f2b64a39aaade4f78dc43acfd8 |
|
MD5 | e3a2a0f5af16d1740618445564fb161e |
|
BLAKE2b-256 | 8f8079c7d8cdcbf7a619a7bf3ea8be03649223fa37bb7cfc1ce8b8b5654f537c |
Hashes for serpyco_rs-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | ab67b5278f9917f9910a170de5e73d7330cd67c9e5d54bafd174edd8ad98f60d |
|
MD5 | 376d4f5b784ac42fa877e6acc856d0ff |
|
BLAKE2b-256 | 017d40e574810a038c3ede1d5d5a22a55e463fb76c187e9a469c057fcc976e28 |
Hashes for serpyco_rs-1.1.0-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 07eb575d9b792c9c22d35877f035a181af2b2b91cfb4da0e09280979afa11ce9 |
|
MD5 | 4468c54ad3ef8de65bf11b647b5666d3 |
|
BLAKE2b-256 | 4b53f9d5a4b0b2c0f27b89bf88eb12b0817ad580dca0a282ca41ddaac0bf5263 |
Hashes for serpyco_rs-1.1.0-cp310-none-win_amd64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 55c0c4ec363ab6f4a3a5677cb07ab7eeb0dd811fd281c61157f517c0c746f0ce |
|
MD5 | 8bbc679a83d951b6f36b141fcfa455f4 |
|
BLAKE2b-256 | 798dc99b1858c3f99907b7883098e77569d1d2c3164d85705e29b88194921c24 |
Hashes for serpyco_rs-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 51ba7755847bd516953fa3831d016ed6b4d3842ff1071958d0ccf3736a21da79 |
|
MD5 | c59fbdc71374e5b74f832d276970ea7a |
|
BLAKE2b-256 | 984fe1e2fcb24dfd8d0428b3ffba537215788056d89c5a58a885910a74598068 |
Hashes for serpyco_rs-1.1.0-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | a8c661c542034f69ee62576cb8fe051ca20d6a7a93cdb66ac721bd7c49f043ad |
|
MD5 | 49e6e10e72ba860811f83b1a7417690a |
|
BLAKE2b-256 | 35cd3adadfc14fbd9cc38b122fafe7d3122d6c8deb4f386c39abeda304b24478 |
Hashes for serpyco_rs-1.1.0-cp39-none-win_amd64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | f12b3843a01da3a06407b5980c4ae2cbe5344b815316b443317079aefd967541 |
|
MD5 | ec2fae97d7f75b5043dadfcd23257531 |
|
BLAKE2b-256 | 258311878f544b66c0fb521faea63a17944a1b79bdae7537f433454b8fd3e9a6 |
Hashes for serpyco_rs-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 43b04ff6785beec10ba4c96202a20ab7ef85f66496ce86f4791ac03d06067419 |
|
MD5 | 1d20bb6a63ca0dcdd8feebd0e96cf4ab |
|
BLAKE2b-256 | 09f1191937b97584e016667533386c2c1545f1d6b5e116f26d08749e0063191c |
Hashes for serpyco_rs-1.1.0-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 69390dc7ea6aac5c96d4f920b1252b43c4cb0599eb4770a2453467158d8e4f37 |
|
MD5 | 8817c7aff37152b6ddd88a7977bc2612 |
|
BLAKE2b-256 | ee6a3193c0c9678d3230ac5ff01cbc71cfe8621ff3c2e81948d6b4c4ad05ddbb |