1111
1212import orjson
1313from bs4 import BeautifulSoup
14+ from cachetools .lru import LRUCache
1415
1516from .call_method_parser import InvalidKwarg , parse_call_method_name , parse_kwarg
1617from .components import UnicornField , UnicornView
2627logger .setLevel (logging .DEBUG )
2728
2829
30+ type_hints_cache = LRUCache (maxsize = 100 )
31+
32+
2933def handle_error (view_func ):
34+ """
35+ Returns a JSON response with an error if necessary.
36+ """
37+
3038 def wrapped_view (* args , ** kwargs ):
3139 try :
3240 return view_func (* args , ** kwargs )
@@ -38,6 +46,34 @@ def wrapped_view(*args, **kwargs):
3846 return wraps (view_func )(wrapped_view )
3947
4048
49+ def _get_type_hints (obj ):
50+ """
51+ Get type hints from an object. These get cached in a local memory cache for quicker look-up later.
52+
53+ Returns:
54+ An empty dictionary if no type hints can be retrieved.
55+ """
56+ try :
57+ if obj in type_hints_cache :
58+ return type_hints_cache [obj ]
59+ except TypeError :
60+ # Ignore issues with checking for an object in the cache, e.g. when a Django model is missing a PK
61+ pass
62+
63+ try :
64+ type_hints = get_type_hints (obj )
65+
66+ # Cache the type hints just in case
67+ type_hints_cache [obj ] = type_hints
68+
69+ return type_hints
70+ except TypeError :
71+ # Return an empty dictionary when there is a TypeError. From `get_type_hints`: "TypeError is
72+ # raised if the argument is not of a type that can contain annotations, and an empty dictionary
73+ # is returned if no annotations are present"
74+ return {}
75+
76+
4177@timed
4278def _is_component_field_model_or_unicorn_field (
4379 component_or_field : Union [UnicornView , UnicornField , Model ], name : str ,
@@ -66,7 +102,7 @@ def _is_component_field_model_or_unicorn_field(
66102 component_type_hints = {}
67103
68104 try :
69- component_type_hints = get_type_hints (component_or_field )
105+ component_type_hints = _get_type_hints (component_or_field )
70106
71107 if name in component_type_hints :
72108 is_subclass_of_model = issubclass (component_type_hints [name ], Model )
@@ -81,7 +117,7 @@ def _is_component_field_model_or_unicorn_field(
81117 if is_subclass_of_model or is_subclass_of_unicorn_field :
82118 field = component_type_hints [name ]()
83119 setattr (component_or_field , name , field )
84- except TypeError as e :
120+ except TypeError :
85121 pass
86122
87123 return is_subclass_of_model or is_subclass_of_unicorn_field
@@ -115,6 +151,15 @@ def _set_property_from_data(
115151 else :
116152 _set_property_from_data (field , field .name , value )
117153 else :
154+ type_hints = _get_type_hints (component_or_field )
155+
156+ if name in type_hints :
157+ # Construct the specified type by passing the value in
158+ # Usually the value will be a string (because it is coming from JSON)
159+ # and basic types can be constructed by passing in a string,
160+ # i.e. int("1") or float("1.1")
161+ value = type_hints [name ](value )
162+
118163 if hasattr (component_or_field , "_set_property" ):
119164 # Can assume that `component_or_field` is a component
120165 component_or_field ._set_property (name , value )
0 commit comments