diff --git a/src/py_avro_schema/_schemas.py b/src/py_avro_schema/_schemas.py index e6a579f..e5c07d9 100644 --- a/src/py_avro_schema/_schemas.py +++ b/src/py_avro_schema/_schemas.py @@ -33,6 +33,7 @@ Annotated, Any, Dict, + Final, ForwardRef, List, Literal, @@ -378,6 +379,31 @@ def data(self, names: NamesType) -> JSONType: return self.literal_value_schema.data(names=names) +@register_schema +class FinalSchema(Schema): + """An Avro schema for Python ``typing.Final``""" + + def __init__(self, py_type: Type, namespace: Optional[str] = None, options: Option = Option(0)): + """An Avro schema for Python ``typing.Final``""" + super().__init__(py_type, namespace, options) + py_type = _type_from_annotated(py_type) + try: + real_type = get_args(py_type)[0] + except IndexError: + raise TypeError("Can't generate Avro schema from Python typing.Final without a type parameter") + self.real_schema = _schema_obj(real_type, namespace=namespace, options=options) + + def data(self, names: NamesType) -> JSONType: + """Return the schema data""" + return self.real_schema.data(names=names) + + @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 get_origin(py_type) is Final or py_type is Final + + @register_schema class DictAsJSONSchema(Schema): """An Avro string schema representing a Python Dict[str, Any] or List[Dict[str, Any]] assuming JSON serialization""" diff --git a/tests/test_plain_class.py b/tests/test_plain_class.py index 4f9b32f..a90da2c 100644 --- a/tests/test_plain_class.py +++ b/tests/test_plain_class.py @@ -10,7 +10,7 @@ # specific language governing permissions and limitations under the License. import re -from typing import Annotated +from typing import Annotated, Final import pytest @@ -160,3 +160,22 @@ def test_type_aliases_future(): expected = {"fields": [{"name": "name", "type": "string"}], "name": "PyClass", "type": "record"} assert_schema(PyClass, expected) + + +def test_typing_final(): + + class PyType: + var: Final[str] + field: Final[dict[str, int]] + + def __init__(self): + self.var = "Hello World" + self.field = {"John": 123} + + expected = { + "fields": [{"name": "var", "type": "string"}, {"name": "field", "type": {"type": "map", "values": "long"}}], + "name": "PyType", + "type": "record", + } + + assert_schema(PyType, expected)