Skip to content

Commit de2de5d

Browse files
committed
better handling of circular references
1 parent 3a705c7 commit de2de5d

File tree

2 files changed

+39
-2
lines changed

2 files changed

+39
-2
lines changed

src/py_avro_schema/_schemas.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1190,7 +1190,7 @@ def handles_type(cls, py_type: Type) -> bool:
11901190
# If we are subclassing a string, used the "named string" approach
11911191
and (inspect.isclass(py_type) and not issubclass(py_type, str))
11921192
# and any other class with typed annotations
1193-
and bool(get_type_hints(py_type))
1193+
and has_type_hints(py_type)
11941194
)
11951195

11961196
def __init__(self, py_type: Type, namespace: Optional[str] = None, options: Option = Option(0)):
@@ -1345,3 +1345,15 @@ def _type_from_annotated(py_type: Type) -> Type:
13451345
return args[0]
13461346
else:
13471347
return py_type
1348+
1349+
1350+
def has_type_hints(py_type: Type) -> bool:
1351+
"""
1352+
Checks if a type has annotations. ``get_type_hints`` might fail if there are forward references.
1353+
"""
1354+
py_type = _type_from_annotated(py_type)
1355+
try:
1356+
return bool(get_type_hints(py_type))
1357+
except Exception:
1358+
pass
1359+
return hasattr(py_type, "__annotations__")

tests/test_plain_class.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# specific language governing permissions and limitations under the License.
1111

1212
import re
13-
from typing import Annotated, Final
13+
from typing import Annotated, Final, ForwardRef
1414

1515
import pytest
1616

@@ -179,3 +179,28 @@ def __init__(self):
179179
}
180180

181181
assert_schema(PyType, expected)
182+
183+
184+
def test_circular_dependencies():
185+
class PyType:
186+
backend: ForwardRef("Backend")
187+
value: str
188+
189+
class Backend:
190+
py_type: PyType
191+
192+
expected = {
193+
"fields": [
194+
{
195+
"name": "py_type",
196+
"type": {
197+
"fields": [{"name": "backend", "type": "Backend"}, {"name": "value", "type": "string"}],
198+
"name": "PyType",
199+
"type": "record",
200+
},
201+
}
202+
],
203+
"name": "Backend",
204+
"type": "record",
205+
}
206+
assert_schema(Backend, expected)

0 commit comments

Comments
 (0)