Skip to content

Commit 89da8be

Browse files
committed
Changes to support running the client under future annotations
In order to support self-referencial type annotations in python 3.11 (via from __future__ import annotations ) There are a few changes needed for code to run. The main scenarios are things like: @dataclass_json(letter_case=LetterCase.CAMEL, undefined=Undefined.EXCLUDE) @DataClass class PagedResults: current_page: int = 0 total_results: int = 0 page_size: int = 0 T = TypeVar('T') @dataclass_json(letter_case=LetterCase.CAMEL, undefined=Undefined.EXCLUDE) @DataClass class PagedResults1(Generic[T], PagedResults): results: Optional[List[T]] = None @dataclass_json(letter_case=LetterCase.CAMEL, undefined=Undefined.EXCLUDE) @DataClass class AResponseWithPagedResults: paged_results: Optional[PagedResults1[SomeDto]] = None @dataclass_json(letter_case=LetterCase.CAMEL, undefined=Undefined.EXCLUDE) @DataClass class HierarchicalDto: children: Optional[List[HierarchicalDto]] = None
1 parent 57577d9 commit 89da8be

File tree

1 file changed

+19
-9
lines changed

1 file changed

+19
-9
lines changed

servicestack/reflection.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import pathlib
1010
import platform
1111
import typing
12+
import sys
1213
from dataclasses import fields, is_dataclass
1314
from datetime import datetime, timedelta
1415
from enum import Enum, IntEnum, EnumMeta
@@ -58,7 +59,10 @@ def _get_type_vars_map(cls: Type, type_map=None):
5859
generic_type_args = get_args(cls)
5960
i = 0
6061
for t in generic_def.__parameters__:
61-
type_map[t] = generic_type_args[i]
62+
k = t
63+
if type(k) == TypeVar:
64+
k = k.__name__
65+
type_map[k] = generic_type_args[i]
6266
i += 1
6367
return type_map
6468

@@ -232,9 +236,11 @@ def _resolve_forwardref(cls: Type, orig: Type = None):
232236
return globals()[type_name]
233237

234238

235-
def unwrap(cls: Type):
239+
def unwrap(cls: Type, module:str):
236240
if type(cls) == ForwardRef:
237241
cls = _resolve_forwardref(cls)
242+
if isinstance(cls, str):
243+
cls = eval(cls, {}, vars(sys.modules[module]))
238244
if is_optional(cls):
239245
return generic_arg(cls)
240246
return cls
@@ -282,16 +288,20 @@ def enum_get(cls: Union[Enum, Type], key: Union[str, int]):
282288
raise TypeError(f"{key} is not a member of {nameof(Enum)}")
283289

284290

285-
def _resolve_type(cls: Type, substitute_types: Dict[Type, type]):
291+
def _resolve_type(cls: Type, substitute_types: Dict[Type, type] ):
292+
if type(cls) == TypeVar:
293+
# TypeVar('T') == TypeVar('T') is false,
294+
# I think this should work
295+
cls = cls.__name__
286296
if substitute_types is None:
287297
return cls
288298
return substitute_types[cls] if cls in substitute_types else cls
289299

290300

291-
def convert(into: Type, obj: Any, substitute_types: Dict[Type, type] = None):
301+
def convert(into: Type, obj: Any, substitute_types: Dict[Type, type] = None, module = None):
292302
if obj is None:
293303
return None
294-
into = unwrap(into)
304+
into = unwrap(into, module)
295305
into = _resolve_type(into, substitute_types)
296306
if Log.debug_enabled():
297307
Log.debug(f"convert({into}, {substitute_types}, {obj})")
@@ -310,14 +320,14 @@ def convert(into: Type, obj: Any, substitute_types: Dict[Type, type] = None):
310320
for f in fields(into):
311321
val = dict_get(f.name, obj)
312322
# print(f"to[{f.name}] = convert({f.type}, {val}, {substitute_types})")
313-
to[f.name] = convert(f.type, val, substitute_types)
323+
to[f.name] = convert(f.type, val, substitute_types, into.__module__)
314324
# print(f"to[{f.name}] = {to[f.name]}")
315325
return into(**to)
316326
elif is_list(into):
317327
el_type = _resolve_type(generic_arg(into), substitute_types)
318328
to = []
319329
for item in obj:
320-
to.append(convert(el_type, item, substitute_types))
330+
to.append(convert(el_type, item, substitute_types, into.__module__))
321331
return to
322332
elif is_dict(into):
323333
key_type, val_type = generic_args(into)
@@ -327,8 +337,8 @@ def convert(into: Type, obj: Any, substitute_types: Dict[Type, type] = None):
327337
if not hasattr(obj, 'items'):
328338
Log.warn(f"dict {obj} ({type(type)}) does not have items()")
329339
for key, val in obj.items():
330-
to_key = convert(key_type, key, substitute_types)
331-
to_val = convert(val_type, val, substitute_types)
340+
to_key = convert(key_type, key, substitute_types, into.__module__)
341+
to_val = convert(val_type, val, substitute_types, into.__module__)
332342
to[to_key] = to_val
333343
return to
334344
else:

0 commit comments

Comments
 (0)