Skip to content

Support for TypedDict schemas #83

Description

@thrau

Motivation

We're looking at py-avro-schema at LocalStack. One thing we would need support for is TypedDicts. It looks fairly easy to achieve given that TypedDicts provide a very simple and predictable data description:

from typing import TypedDict

class MyTypedDict(TypedDict):
    x: int
    y: int
    z: int

# alternative but equivalent declaration:
MyTypedDict = TypedDict("MyTypedDict", {"x": int, "y": int, "z": int})

on both cases MyTypedDict.__annotations__ will return {'x': <class 'int'>, 'y': <class 'int'>, 'z': <class 'int'>}. There can be no right-hand side in TypedDicts, so no default values to worry about.

Implementation suggestion

An implementation based on your already existing RecordSchema should be fairly straight forward:

class TypedDictSchema(RecordSchema):
    """An Avro record schema for a given Python TypedDict class."""

    @classmethod
    def handles_type(cls, py_type: Type) -> bool:
        """Whether this schema class can represent a given Python class"""
        py_type = _type_from_annotated(py_type)
        return isinstance(py_type, type(typing.TypedDict("T", {})))

    def __init__(self, py_type: Type, namespace: Optional[str] = None, options: Option = Option(0)):
        """
        An Avro record schema for a given Python TypedDict.

        :param py_type:   The Python class to generate a schema for.
        :param namespace: The Avro namespace to add to schemas.
        :param options:   Schema generation options.
        """
        super().__init__(py_type, namespace=namespace, options=options)
        py_type = _type_from_annotated(py_type)
        self.py_fields = [(k,v) for k,v, in py_type.__annotations__.items()]
        self.record_fields = [self._record_field(field) for field in self.py_fields]

    def _record_field(self, py_field: tuple[str, Type]) -> RecordField:
        """Return an Avro record field object for a given annotation field"""
        field_obj = RecordField(
            py_type=py_field[1],
            name=py_field[0],
            namespace=self.namespace_override,
            # default=default, # TypedDict annotations cannot have a right-hand side
            options=self.options,
        )

        return field_obj

We would love to see this supported, and I'm happy to raise a PR if you're open to this!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions