1010"""
1111
1212from __future__ import annotations
13+ import inspect
14+ from threading import Lock
1315from azure .functions ._durable_functions import df_dumps , df_loads
1416
15- from typing import Any , override
17+ from typing import Any , Callable , Protocol , TypeGuard , override
1618
1719from durabletask .serialization import JsonDataConverter
1820
1921
22+ class _ConfigurableJsonEncoder (Protocol ):
23+ def __call__ (self , value : Any , * , ensure_ascii : bool = True ) -> str :
24+ ...
25+
26+
27+ _serialization_lock = Lock ()
28+ _ensure_ascii : bool | None = None
29+ _unicode_encoder : _ConfigurableJsonEncoder | None = None
30+
31+
32+ def _supports_ensure_ascii (
33+ encoder : Callable [[Any ], str ]) -> TypeGuard [_ConfigurableJsonEncoder ]:
34+ parameter = inspect .signature (encoder ).parameters .get ("ensure_ascii" )
35+ return parameter is not None and parameter .kind in (
36+ inspect .Parameter .KEYWORD_ONLY ,
37+ inspect .Parameter .POSITIONAL_OR_KEYWORD ,
38+ )
39+
40+
41+ def configure_serialization (* , ensure_ascii : object = True ) -> None :
42+ """Select one JSON encoding policy before durable payload serialization."""
43+ global _ensure_ascii , _unicode_encoder
44+ if not isinstance (ensure_ascii , bool ):
45+ raise TypeError ("ensure_ascii must be a bool" )
46+ with _serialization_lock :
47+ if _ensure_ascii is not None and _ensure_ascii != ensure_ascii :
48+ raise ValueError (
49+ "JSON serialization is already configured with "
50+ f"ensure_ascii={ _ensure_ascii } in this process; configure "
51+ "serialization once before serializing durable payloads" )
52+ if not ensure_ascii :
53+ encoder = df_dumps
54+ if not _supports_ensure_ascii (encoder ):
55+ raise RuntimeError (
56+ "ensure_ascii=False requires azure-functions>=2.4.0b1, whose "
57+ "df_dumps supports the ensure_ascii keyword argument. "
58+ "Install a compatible azure-functions SDK before opting in." )
59+ _unicode_encoder = encoder
60+ _ensure_ascii = ensure_ascii
61+
62+
63+ def serialize_json (value : Any ) -> str :
64+ """Apply the process-wide policy using the shared Functions JSON codec."""
65+ global _ensure_ascii
66+ with _serialization_lock :
67+ if _ensure_ascii is None :
68+ _ensure_ascii = True
69+ ensure_ascii = _ensure_ascii
70+ encoder = _unicode_encoder
71+ if ensure_ascii :
72+ # Older shared SDKs do not accept the optional keyword argument.
73+ return df_dumps (value )
74+ if encoder is None :
75+ raise RuntimeError ("Unicode JSON serialization has no configured encoder" )
76+ return encoder (value , ensure_ascii = False )
77+
78+
2079class FunctionsDataConverter (JsonDataConverter ):
2180 """:class:`DataConverter` that serializes via azure-functions' codec.
2281
@@ -38,7 +97,7 @@ class FunctionsDataConverter(JsonDataConverter):
3897 def serialize (self , value : Any ) -> str | None :
3998 if value is None :
4099 return None
41- return df_dumps (value )
100+ return serialize_json (value )
42101
43102 @override
44103 def deserialize (self , data : str | None , target_type : type | None = None ) -> Any :
0 commit comments