From f474bf4156877503d548f4bc073945e9a8a9d2d9 Mon Sep 17 00:00:00 2001 From: Giovanni Grano Date: Wed, 12 Nov 2025 14:58:17 +0100 Subject: [PATCH 1/3] reproduce --- src/py_avro_schema/_testing.py | 9 +++++++++ tests/test_plain_class.py | 18 +++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/py_avro_schema/_testing.py b/src/py_avro_schema/_testing.py index 0259750..ed4ac24 100644 --- a/src/py_avro_schema/_testing.py +++ b/src/py_avro_schema/_testing.py @@ -13,6 +13,8 @@ """ Test functions """ +from __future__ import annotations + import dataclasses import difflib from typing import Dict, Type, Union @@ -57,3 +59,10 @@ class PyType: """For testing""" field_a: str + +Name = str + +class PyClass: + """For testing""" + + name: Name diff --git a/tests/test_plain_class.py b/tests/test_plain_class.py index c8d21de..7fbc3a4 100644 --- a/tests/test_plain_class.py +++ b/tests/test_plain_class.py @@ -18,7 +18,6 @@ from py_avro_schema._alias import Alias, Opaque, register_type_aliases from py_avro_schema._testing import assert_schema - def test_plain_class_with_type_hints(): @register_type_aliases(aliases=["test_plain_class.OldPyType"]) class PyType: @@ -143,3 +142,20 @@ class PyType: expected = {"fields": [{"name": "details", "type": "string"}], "name": "PyType", "type": "record"} assert_schema(PyType, expected) + + +def test_type_aliases(): + Name = str + + class PyClass: + name: Name + + expected = {"fields": [{"name": "name", "type": "string"}], "name": "PyClass", "type": "record"} + assert_schema(PyClass, expected) + + +def test_type_aliases_future(): + from py_avro_schema._testing import PyClass + + expected = {"fields": [{"name": "name", "type": "string"}], "name": "PyType", "type": "record"} + assert_schema(PyClass, expected) From a83d2f54ab54313ffdc7be39d7d816a3d14e4a11 Mon Sep 17 00:00:00 2001 From: Giovanni Grano Date: Wed, 12 Nov 2025 15:18:59 +0100 Subject: [PATCH 2/3] use get_type_hints to handle forward references --- src/py_avro_schema/_schemas.py | 7 ++++++- tests/test_plain_class.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/py_avro_schema/_schemas.py b/src/py_avro_schema/_schemas.py index 808baea..e6a579f 100644 --- a/src/py_avro_schema/_schemas.py +++ b/src/py_avro_schema/_schemas.py @@ -1156,8 +1156,13 @@ def __init__(self, py_type: Type, namespace: Optional[str] = None, options: Opti super().__init__(py_type, namespace=namespace, options=options) py_type = _type_from_annotated(py_type) + # Try to get resolved type hints, but fall back to raw annotations if there are unresolved forward refs + try: + type_hints = get_type_hints(py_type, include_extras=True) + except NameError: + type_hints = py_type.__annotations__ self.py_fields: list[tuple[str, type]] = [] - for k, v in py_type.__annotations__.items(): + for k, v in type_hints.items(): self.py_fields.append((k, v)) # We store __init__ parameters with default values. They can be used as defaults for the record. self.signature_fields = { diff --git a/tests/test_plain_class.py b/tests/test_plain_class.py index 7fbc3a4..c5a94e2 100644 --- a/tests/test_plain_class.py +++ b/tests/test_plain_class.py @@ -157,5 +157,5 @@ class PyClass: def test_type_aliases_future(): from py_avro_schema._testing import PyClass - expected = {"fields": [{"name": "name", "type": "string"}], "name": "PyType", "type": "record"} + expected = {"fields": [{"name": "name", "type": "string"}], "name": "PyClass", "type": "record"} assert_schema(PyClass, expected) From 7b77befea1536db2b5dde730ed913e1b6cb1c7f9 Mon Sep 17 00:00:00 2001 From: Giovanni Grano Date: Wed, 12 Nov 2025 15:28:01 +0100 Subject: [PATCH 3/3] fix plain classes using __future__ annotations --- src/py_avro_schema/_testing.py | 9 --------- tests/models/__init__.py | 0 tests/models/forward.py | 9 +++++++++ tests/test_plain_class.py | 3 ++- 4 files changed, 11 insertions(+), 10 deletions(-) create mode 100644 tests/models/__init__.py create mode 100644 tests/models/forward.py diff --git a/src/py_avro_schema/_testing.py b/src/py_avro_schema/_testing.py index ed4ac24..0259750 100644 --- a/src/py_avro_schema/_testing.py +++ b/src/py_avro_schema/_testing.py @@ -13,8 +13,6 @@ """ Test functions """ -from __future__ import annotations - import dataclasses import difflib from typing import Dict, Type, Union @@ -59,10 +57,3 @@ class PyType: """For testing""" field_a: str - -Name = str - -class PyClass: - """For testing""" - - name: Name diff --git a/tests/models/__init__.py b/tests/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/forward.py b/tests/models/forward.py new file mode 100644 index 0000000..7a8cba9 --- /dev/null +++ b/tests/models/forward.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +Name = str + + +class PyClass: + """For testing imports with future annotations""" + + name: Name diff --git a/tests/test_plain_class.py b/tests/test_plain_class.py index c5a94e2..4f9b32f 100644 --- a/tests/test_plain_class.py +++ b/tests/test_plain_class.py @@ -18,6 +18,7 @@ from py_avro_schema._alias import Alias, Opaque, register_type_aliases from py_avro_schema._testing import assert_schema + def test_plain_class_with_type_hints(): @register_type_aliases(aliases=["test_plain_class.OldPyType"]) class PyType: @@ -155,7 +156,7 @@ class PyClass: def test_type_aliases_future(): - from py_avro_schema._testing import PyClass + from tests.models.forward import PyClass expected = {"fields": [{"name": "name", "type": "string"}], "name": "PyClass", "type": "record"} assert_schema(PyClass, expected)