Skip to main content

A toolkit for marshalling, unmarshalling, and runtime validation leveraging type annotations.

Project description

Welcome to typelib

Version License: MIT Python Versions Code Size CI Coverage Code Style

Python's Typing Toolkit

typelib provides a sensible, non-invasive, production-ready toolkit for leveraging Python type annotations at runtime.

Quickstart

Installation

poetry add 'typelib[json]'

Bring Your Own Models

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.

The How and the Where

How: The High-Level API

We have a simple high-level API which should handle most production use-cases:

from __future__ import annotations

import dataclasses
import datetime
import decimal


import typelib

@dataclasses.dataclass(slots=True, weakref_slot=True, kw_only=True)
class BusinessModel:
    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. ///

Where: At the Edges of Your Code

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__ import annotations

import dataclasses
import datetime
import decimal
import operator
import random

import typelib


class ClientRPC:
    def __init__(self):
        self.codec = typelib.codec(BusinessModel)

    def call(self, inp: bytes) -> bytes:
        model = self.receive(inp)
        done = self.op(model)
        return self.send(done)

    @staticmethod
    def op(model: BusinessModel) -> BusinessModel:
        op = getattr(operator, model.op)
        return dataclasses.replace(
            model,
            value=op(model.value, model.value),
            id=random.getrandbits(64),
            created_at=datetime.datetime.now(tz=datetime.UTC)
        )

    def send(self, model: BusinessModel) -> bytes:
        return self.codec.encode(model)

    def receive(self, data: bytes) -> BusinessModel:
        return self.codec.decode(data)


@dataclasses.dataclass(slots=True, weakref_slot=True, kw_only=True)
class BusinessModel:
    op: str
    value: decimal.Decimal
    id: int | None = None
    created_at: datetime.datetime | None = None

Where: Between Layers in Your Code

You can integrate this library to ease the translation of one type to another:

from __future__ import annotations

import dataclasses
import datetime
import decimal
import typing as t


import typelib

@dataclasses.dataclass(slots=True, weakref_slot=True, kw_only=True)
class BusinessModel:
    op: str
    value: decimal.Decimal
    id: int | None = None
    created_at: datetime.datetime | None = None
    

class ClientRepr(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:

  1. The instance you pass in has the same overlap of required fields.
  2. The values in the overlapping fields can be translated to the target type. ///

Why typelib

typelib provides a simple, non-invasive API to make everyday data wrangling in your production applications easy and reliable.

We DO

  1. Provide an API for marshalling and unmarshalling data based upon type annotations.
  2. Provide an API for integrating our marshalling with over-the-wire serialization and deserialization.
  3. Provide fine-grained, high-performance, runtime introspection of Python types.
  4. Provide future-proofing to allow for emerging type annotation syntax.

We DON'T

  1. Require you to inherit from a custom base class.
  2. Require you to use custom class decorators.
  3. Rely upon generated code.

How It Works

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 discussion here. ///

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

typelib-0.1.9.tar.gz (46.5 kB view details)

Uploaded Source

Built Distribution

typelib-0.1.9-py3-none-any.whl (51.8 kB view details)

Uploaded Python 3

File details

Details for the file typelib-0.1.9.tar.gz.

File metadata

  • Download URL: typelib-0.1.9.tar.gz
  • Upload date:
  • Size: 46.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.1 CPython/3.12.7

File hashes

Hashes for typelib-0.1.9.tar.gz
Algorithm Hash digest
SHA256 ecf18daff8b03b17a5eaf921b99be6ee7922ec2a975976d1c45a0e58e47f8b8a
MD5 a963761fbee4feca6e6c3afa6bbbe1b4
BLAKE2b-256 efc810effcfe55c14f6c99d80aa31136bae5d3b3afc1f641e4b6a977ddc4801a

See more details on using hashes here.

Provenance

The following attestation bundles were made for typelib-0.1.9.tar.gz:

Publisher: publish.yml on seandstewart/python-typelib

Attestations:

File details

Details for the file typelib-0.1.9-py3-none-any.whl.

File metadata

  • Download URL: typelib-0.1.9-py3-none-any.whl
  • Upload date:
  • Size: 51.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.1 CPython/3.12.7

File hashes

Hashes for typelib-0.1.9-py3-none-any.whl
Algorithm Hash digest
SHA256 abb60a343db1432bd01d14af571d01d617da52e74dd9fdf77dc6b0c40269690e
MD5 a22d22974b04cd9bcc2bcd977b389a1f
BLAKE2b-256 e297d80276ce4f1c2e2a5893f7ff075024fb86aa69f6e269e5cf3b94cff80321

See more details on using hashes here.

Provenance

The following attestation bundles were made for typelib-0.1.9-py3-none-any.whl:

Publisher: publish.yml on seandstewart/python-typelib

Attestations:

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page