When a Queue is initialized, we always know what the instance type and what the underlying (raw, backend) data types of the queue are.
This means we are able to create a piece of code - a function - that will purposefully convert the clean type to the raw type (serializer), and the other function to do opposite conversion (deserializer).
At initialization time, we can store that function and avoid runtime cost of searching for appropriate [de]serializing function in runtime, which will be especially expensive with constant isinstance() calls.
The search of such functions must always happen in a closed namespace akin to an @lru_cache'd function with two arguments - the two types. That namespace must be easy to provide to any Queue instance. To summarize:
class MyappSerde(platonic_serde.PydanticSerde):
"""We use Pydantic models."""
class ReportsQueue(platonic_queue.sqs.SQSOutputQueue[models.Report]):
url = ...
serde = MyappSerde
This is kind of dependency injection inspired approach. How to get serializers? Something like this:
serializer = MyappSerde[pydantic.BaseModel, Base64[Json]]
deserializer = MyappSerde[Json, pydantic.BaseModel]
Each of these will be __call__'able. It will be easy to type annotate too.