Skip to content

Commit 44046af

Browse files
committed
Consolidate implementation around annotationlib
1 parent 82af629 commit 44046af

File tree

4 files changed

+53
-18
lines changed

4 files changed

+53
-18
lines changed

reflex/components/field.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from typing import Annotated, Any, Generic, TypeVar, get_origin
88

99
from reflex.utils import types
10+
from reflex.utils.compat import annotations_from_namespace
1011

1112
FIELD_TYPE = TypeVar("FIELD_TYPE")
1213

@@ -116,12 +117,10 @@ def _collect_inherited_fields(cls, bases: tuple[type]) -> dict[str, Any]:
116117
def _resolve_annotations(
117118
cls, namespace: dict[str, Any], name: str
118119
) -> dict[str, Any]:
119-
if (_annotate := namespace.get("__annotate_func__")) is not None:
120-
# python3.14 style
121-
raw_annotations = _annotate(0)
122-
else:
123-
raw_annotations = namespace.get("__annotations__", {})
124-
return types.resolve_annotations(raw_annotations, namespace["__module__"])
120+
return types.resolve_annotations(
121+
annotations_from_namespace(namespace),
122+
namespace["__module__"],
123+
)
125124

126125
@classmethod
127126
def _process_field_overrides(

reflex/utils/compat.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Compatibility hacks and helpers."""
22

3+
import sys
4+
from collections.abc import Mapping
35
from importlib.util import find_spec
46
from typing import TYPE_CHECKING, Any
57

@@ -31,6 +33,27 @@ async def windows_hot_reload_lifespan_hack():
3133
pass
3234

3335

36+
def annotations_from_namespace(namespace: Mapping[str, Any]) -> dict[str, Any]:
37+
"""Get the annotations from a class namespace.
38+
39+
Args:
40+
namespace: The class namespace.
41+
42+
Returns:
43+
The (forward-ref) annotations from the class namespace.
44+
"""
45+
if sys.version_info >= (3, 14) and "__annotations__" not in namespace:
46+
from annotationlib import (
47+
Format,
48+
call_annotate_function,
49+
get_annotate_from_class_namespace,
50+
)
51+
52+
if annotate := get_annotate_from_class_namespace(namespace):
53+
return call_annotate_function(annotate, format=Format.FORWARDREF)
54+
return namespace.get("__annotations__", {})
55+
56+
3457
if find_spec("pydantic") and find_spec("pydantic.v1"):
3558
from pydantic.v1.main import ModelMetaclass
3659

@@ -49,8 +72,7 @@ def __new__(mcs, name: str, bases: tuple, namespace: dict, **kwargs):
4972
Returns:
5073
The created class.
5174
"""
52-
if (_anotate := namespace.get("__annotate_func__")) is not None:
53-
namespace["__annotations__"] = _anotate(0)
75+
namespace["__annotations__"] = annotations_from_namespace(namespace)
5476
return super().__new__(mcs, name, bases, namespace, **kwargs)
5577
else:
5678
ModelMetaclassLazyAnnotations = type # type: ignore[assignment]
@@ -85,12 +107,8 @@ def sqlmodel_get_annotations(class_dict: dict[str, Any]) -> dict[str, Any]:
85107
"""
86108
from reflex.utils.types import resolve_annotations
87109

88-
if (_annotate := class_dict.get("__annotate_func__")) is not None:
89-
annotations = _annotate(0)
90-
else:
91-
annotations = class_dict.get("__annotations__", {})
92110
return resolve_annotations( # type: ignore[no-any-return]
93-
annotations,
111+
annotations_from_namespace(class_dict),
94112
class_dict.get("__module__"),
95113
)
96114

reflex/utils/types.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,27 @@ def resolve_annotations(
11871187
return annotations
11881188

11891189

1190+
def annotations_from_namespace(namespace: Mapping[str, Any]) -> dict[str, Any]:
1191+
"""Get the annotations from a class namespace.
1192+
1193+
Args:
1194+
namespace: The class namespace.
1195+
1196+
Returns:
1197+
The (forward-ref) annotations from the class namespace.
1198+
"""
1199+
if sys.version_info >= (3, 14) and "__annotations__" not in namespace:
1200+
from annotationlib import (
1201+
Format,
1202+
call_annotate_function,
1203+
get_annotate_from_class_namespace,
1204+
)
1205+
1206+
if annotate := get_annotate_from_class_namespace(namespace):
1207+
return call_annotate_function(annotate, format=Format.FORWARDREF)
1208+
return namespace.get("__annotations__", {})
1209+
1210+
11901211
TYPES_THAT_HAS_DEFAULT_VALUE = (int, float, tuple, list, set, dict, str)
11911212

11921213

reflex/vars/base.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from reflex.constants.compiler import Hooks
4444
from reflex.constants.state import FIELD_MARKER
4545
from reflex.utils import console, exceptions, imports, serializers, types
46+
from reflex.utils.compat import annotations_from_namespace
4647
from reflex.utils.decorator import once
4748
from reflex.utils.exceptions import (
4849
ComputedVarSignatureError,
@@ -3477,12 +3478,8 @@ def __new__(
34773478
# Add the field to the class
34783479
inherited_fields: dict[str, Field] = {}
34793480
own_fields: dict[str, Field] = {}
3480-
if (_annotate := namespace.get("__annotate_func__")) is not None:
3481-
raw_annotations = _annotate(0)
3482-
else:
3483-
raw_annotations = namespace.get("__annotations__", {})
34843481
resolved_annotations = types.resolve_annotations(
3485-
raw_annotations, namespace["__module__"]
3482+
annotations_from_namespace(namespace), namespace["__module__"]
34863483
)
34873484

34883485
for base in bases[::-1]:

0 commit comments

Comments
 (0)