typelib
provides a sensible, non-invasive, production-ready toolkit for leveraging
Python type annotations at runtime.
poetry add'typelib[json]'
We don't care how your data model is implemented - you can use [dataclasses
][],
[TypedDict
][typing.TypedDict], [NamedTuple
][typing.NamedTuple], a plain collection,
a custom class, or any other modeling library. As long as your type is valid at runtime,
we'll support it.
We have a simple high-level API which should handle most production use-cases:
from__future__importannotations
importdataclasses
importdatetime
importdecimal
importtypelib
@dataclasses.dataclass(slots=True,weakref_slot=True,kw_only=True)
classBusinessModel:
op:str
value:decimal.Decimal
id:int|None=None
created_at:datetime.datetime|None=None
codec=typelib.codec(BusinessModel)
instance=codec.decode(b'{ "op": "add", "value": "1.0" }')
print(instance)
#> BusinessModel(op='add', value=decimal.Decimal('1.0'), id=None, created_at=None)
encoded=codec.encode(instance)
print(encoded)
#> b'{ "op": "add", "value": "1.0", "id":null, "created_at":null}'
/// tip Looking for more? Check out our [API Reference][typelib] for the high-level API. ///
You can integrate this library at the "edges" of your code - e.g., at the integration points between your application and your client or you application and your data-store:
from__future__importannotations
importdataclasses
importdatetime
importdecimal
importoperator
importrandom
importtypelib
classClientRPC:
def__init__(self):
self.codec=typelib.codec(BusinessModel)
defcall(self,inp:bytes)->bytes:
model=self.receive(inp)
done=self.op(model)
returnself.send(done)
@staticmethod
defop(model:BusinessModel)->BusinessModel:
op=getattr(operator,model.op)
returndataclasses.replace(
model,
value=op(model.value,model.value),
id=random.getrandbits(64),
created_at=datetime.datetime.now(tz=datetime.UTC)
)
defsend(self,model:BusinessModel)->bytes:
returnself.codec.encode(model)
defreceive(self,data:bytes)->BusinessModel:
returnself.codec.decode(data)
@dataclasses.dataclass(slots=True,weakref_slot=True,kw_only=True)
classBusinessModel:
op:str
value:decimal.Decimal
id:int|None=None
created_at:datetime.datetime|None=None
You can integrate this library to ease the translation of one type to another:
from__future__importannotations
importdataclasses
importdatetime
importdecimal
importtypingast
importtypelib
@dataclasses.dataclass(slots=True,weakref_slot=True,kw_only=True)
classBusinessModel:
op:str
value:decimal.Decimal
id:int|None=None
created_at:datetime.datetime|None=None
classClientRepr(t.TypedDict):
op:str
value:str
id:str|None
created_at:datetime.datetime|None
business_codec=typelib.codec(BusinessModel)
client_codec=typelib.codec(ClientRepr)
# Initialize your business model directly from your input.
instance=business_codec.decode(
b'{ "op": "add", "value": "1.0", "id": "10", "created_at": "1970-01-01T00:00:00+0000}'
)
print(instance)
#> BusinessModel(op='add', value=Decimal('1.0'), id=10, created_at=datetime.datetime(1970, 1, 1, 0, 0, fold=1, tzinfo=Timezone('UTC')))
# Encode your business model into the format defined by your ClientRepr.
encoded=client_codec.encode(instance)
print(encoded)
#> b'{ "op": "add", "value": "1.0", "id": "10", "created_at": "1970-01-01T00:00:00+00:00" }'
/// tip There's no need to initialize your ClientRepr instance to leverage its codec, as long as:
- The instance you pass in has the same overlap of required fields.
- The values in the overlapping fields can be translated to the target type. ///
typelib
provides asimple, non-invasive APIto make everyday data wrangling in
your production applications easy and reliable.
- Provide an API for marshalling and unmarshalling data based upon type annotations.
- Provide an API for integrating our marshalling with over-the-wire serialization and deserialization.
- Provide fine-grained, high-performance, runtime introspection of Python types.
- Provide future-proofing to allow for emerging type annotation syntax.
- Require you to inherit from a custom base class.
- Require you to use custom class decorators.
- Rely upon generated code.
typelib
's implementation is unique among runtime type analyzers - we use an iterative,
graph-based resolver to build a predictable, static ordering of the types represented by
an annotation. We have implemented our type-resolution algorithm in isolation from our
logic for marshalling and unmarshalling as a simple iterative loop, making the logic
simple to reason about.
/// tip Read a detailed discussionhere. ///