azure-ai-evaluation 1.0.1__py3-none-any.whl → 1.13.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of azure-ai-evaluation might be problematic. Click here for more details.

Files changed (277) hide show
  1. azure/ai/evaluation/__init__.py +85 -14
  2. azure/ai/evaluation/_aoai/__init__.py +10 -0
  3. azure/ai/evaluation/_aoai/aoai_grader.py +140 -0
  4. azure/ai/evaluation/_aoai/label_grader.py +68 -0
  5. azure/ai/evaluation/_aoai/python_grader.py +86 -0
  6. azure/ai/evaluation/_aoai/score_model_grader.py +94 -0
  7. azure/ai/evaluation/_aoai/string_check_grader.py +66 -0
  8. azure/ai/evaluation/_aoai/text_similarity_grader.py +80 -0
  9. azure/ai/evaluation/_azure/__init__.py +3 -0
  10. azure/ai/evaluation/_azure/_clients.py +204 -0
  11. azure/ai/evaluation/_azure/_envs.py +207 -0
  12. azure/ai/evaluation/_azure/_models.py +227 -0
  13. azure/ai/evaluation/_azure/_token_manager.py +129 -0
  14. azure/ai/evaluation/_common/__init__.py +9 -1
  15. azure/ai/evaluation/_common/constants.py +124 -2
  16. azure/ai/evaluation/_common/evaluation_onedp_client.py +169 -0
  17. azure/ai/evaluation/_common/onedp/__init__.py +32 -0
  18. azure/ai/evaluation/_common/onedp/_client.py +166 -0
  19. azure/ai/evaluation/_common/onedp/_configuration.py +72 -0
  20. azure/ai/evaluation/_common/onedp/_model_base.py +1232 -0
  21. azure/ai/evaluation/_common/onedp/_patch.py +21 -0
  22. azure/ai/evaluation/_common/onedp/_serialization.py +2032 -0
  23. azure/ai/evaluation/_common/onedp/_types.py +21 -0
  24. azure/ai/evaluation/_common/onedp/_utils/__init__.py +6 -0
  25. azure/ai/evaluation/_common/onedp/_utils/model_base.py +1232 -0
  26. azure/ai/evaluation/_common/onedp/_utils/serialization.py +2032 -0
  27. azure/ai/evaluation/_common/onedp/_validation.py +66 -0
  28. azure/ai/evaluation/_common/onedp/_vendor.py +50 -0
  29. azure/ai/evaluation/_common/onedp/_version.py +9 -0
  30. azure/ai/evaluation/_common/onedp/aio/__init__.py +29 -0
  31. azure/ai/evaluation/_common/onedp/aio/_client.py +168 -0
  32. azure/ai/evaluation/_common/onedp/aio/_configuration.py +72 -0
  33. azure/ai/evaluation/_common/onedp/aio/_patch.py +21 -0
  34. azure/ai/evaluation/_common/onedp/aio/operations/__init__.py +49 -0
  35. azure/ai/evaluation/_common/onedp/aio/operations/_operations.py +7143 -0
  36. azure/ai/evaluation/_common/onedp/aio/operations/_patch.py +21 -0
  37. azure/ai/evaluation/_common/onedp/models/__init__.py +358 -0
  38. azure/ai/evaluation/_common/onedp/models/_enums.py +447 -0
  39. azure/ai/evaluation/_common/onedp/models/_models.py +5963 -0
  40. azure/ai/evaluation/_common/onedp/models/_patch.py +21 -0
  41. azure/ai/evaluation/_common/onedp/operations/__init__.py +49 -0
  42. azure/ai/evaluation/_common/onedp/operations/_operations.py +8951 -0
  43. azure/ai/evaluation/_common/onedp/operations/_patch.py +21 -0
  44. azure/ai/evaluation/_common/onedp/py.typed +1 -0
  45. azure/ai/evaluation/_common/onedp/servicepatterns/__init__.py +1 -0
  46. azure/ai/evaluation/_common/onedp/servicepatterns/aio/__init__.py +1 -0
  47. azure/ai/evaluation/_common/onedp/servicepatterns/aio/operations/__init__.py +25 -0
  48. azure/ai/evaluation/_common/onedp/servicepatterns/aio/operations/_operations.py +34 -0
  49. azure/ai/evaluation/_common/onedp/servicepatterns/aio/operations/_patch.py +20 -0
  50. azure/ai/evaluation/_common/onedp/servicepatterns/buildingblocks/__init__.py +1 -0
  51. azure/ai/evaluation/_common/onedp/servicepatterns/buildingblocks/aio/__init__.py +1 -0
  52. azure/ai/evaluation/_common/onedp/servicepatterns/buildingblocks/aio/operations/__init__.py +22 -0
  53. azure/ai/evaluation/_common/onedp/servicepatterns/buildingblocks/aio/operations/_operations.py +29 -0
  54. azure/ai/evaluation/_common/onedp/servicepatterns/buildingblocks/aio/operations/_patch.py +20 -0
  55. azure/ai/evaluation/_common/onedp/servicepatterns/buildingblocks/operations/__init__.py +22 -0
  56. azure/ai/evaluation/_common/onedp/servicepatterns/buildingblocks/operations/_operations.py +29 -0
  57. azure/ai/evaluation/_common/onedp/servicepatterns/buildingblocks/operations/_patch.py +20 -0
  58. azure/ai/evaluation/_common/onedp/servicepatterns/operations/__init__.py +25 -0
  59. azure/ai/evaluation/_common/onedp/servicepatterns/operations/_operations.py +34 -0
  60. azure/ai/evaluation/_common/onedp/servicepatterns/operations/_patch.py +20 -0
  61. azure/ai/evaluation/_common/rai_service.py +578 -69
  62. azure/ai/evaluation/_common/raiclient/__init__.py +34 -0
  63. azure/ai/evaluation/_common/raiclient/_client.py +128 -0
  64. azure/ai/evaluation/_common/raiclient/_configuration.py +87 -0
  65. azure/ai/evaluation/_common/raiclient/_model_base.py +1235 -0
  66. azure/ai/evaluation/_common/raiclient/_patch.py +20 -0
  67. azure/ai/evaluation/_common/raiclient/_serialization.py +2050 -0
  68. azure/ai/evaluation/_common/raiclient/_version.py +9 -0
  69. azure/ai/evaluation/_common/raiclient/aio/__init__.py +29 -0
  70. azure/ai/evaluation/_common/raiclient/aio/_client.py +130 -0
  71. azure/ai/evaluation/_common/raiclient/aio/_configuration.py +87 -0
  72. azure/ai/evaluation/_common/raiclient/aio/_patch.py +20 -0
  73. azure/ai/evaluation/_common/raiclient/aio/operations/__init__.py +25 -0
  74. azure/ai/evaluation/_common/raiclient/aio/operations/_operations.py +981 -0
  75. azure/ai/evaluation/_common/raiclient/aio/operations/_patch.py +20 -0
  76. azure/ai/evaluation/_common/raiclient/models/__init__.py +60 -0
  77. azure/ai/evaluation/_common/raiclient/models/_enums.py +18 -0
  78. azure/ai/evaluation/_common/raiclient/models/_models.py +651 -0
  79. azure/ai/evaluation/_common/raiclient/models/_patch.py +20 -0
  80. azure/ai/evaluation/_common/raiclient/operations/__init__.py +25 -0
  81. azure/ai/evaluation/_common/raiclient/operations/_operations.py +1238 -0
  82. azure/ai/evaluation/_common/raiclient/operations/_patch.py +20 -0
  83. azure/ai/evaluation/_common/raiclient/py.typed +1 -0
  84. azure/ai/evaluation/_common/utils.py +505 -27
  85. azure/ai/evaluation/_constants.py +147 -0
  86. azure/ai/evaluation/_converters/__init__.py +3 -0
  87. azure/ai/evaluation/_converters/_ai_services.py +899 -0
  88. azure/ai/evaluation/_converters/_models.py +467 -0
  89. azure/ai/evaluation/_converters/_sk_services.py +495 -0
  90. azure/ai/evaluation/_eval_mapping.py +87 -0
  91. azure/ai/evaluation/_evaluate/_batch_run/__init__.py +10 -2
  92. azure/ai/evaluation/_evaluate/_batch_run/_run_submitter_client.py +176 -0
  93. azure/ai/evaluation/_evaluate/_batch_run/batch_clients.py +82 -0
  94. azure/ai/evaluation/_evaluate/_batch_run/code_client.py +18 -12
  95. azure/ai/evaluation/_evaluate/_batch_run/eval_run_context.py +19 -6
  96. azure/ai/evaluation/_evaluate/_batch_run/proxy_client.py +47 -22
  97. azure/ai/evaluation/_evaluate/_batch_run/target_run_context.py +18 -2
  98. azure/ai/evaluation/_evaluate/_eval_run.py +32 -46
  99. azure/ai/evaluation/_evaluate/_evaluate.py +1809 -142
  100. azure/ai/evaluation/_evaluate/_evaluate_aoai.py +992 -0
  101. azure/ai/evaluation/_evaluate/_telemetry/__init__.py +5 -90
  102. azure/ai/evaluation/_evaluate/_utils.py +237 -42
  103. azure/ai/evaluation/_evaluator_definition.py +76 -0
  104. azure/ai/evaluation/_evaluators/_bleu/_bleu.py +80 -28
  105. azure/ai/evaluation/_evaluators/_code_vulnerability/__init__.py +5 -0
  106. azure/ai/evaluation/_evaluators/_code_vulnerability/_code_vulnerability.py +119 -0
  107. azure/ai/evaluation/_evaluators/_coherence/_coherence.py +40 -4
  108. azure/ai/evaluation/_evaluators/_common/__init__.py +2 -0
  109. azure/ai/evaluation/_evaluators/_common/_base_eval.py +430 -29
  110. azure/ai/evaluation/_evaluators/_common/_base_multi_eval.py +63 -0
  111. azure/ai/evaluation/_evaluators/_common/_base_prompty_eval.py +269 -12
  112. azure/ai/evaluation/_evaluators/_common/_base_rai_svc_eval.py +74 -9
  113. azure/ai/evaluation/_evaluators/_common/_conversation_aggregators.py +49 -0
  114. azure/ai/evaluation/_evaluators/_content_safety/_content_safety.py +73 -53
  115. azure/ai/evaluation/_evaluators/_content_safety/_hate_unfairness.py +35 -5
  116. azure/ai/evaluation/_evaluators/_content_safety/_self_harm.py +26 -5
  117. azure/ai/evaluation/_evaluators/_content_safety/_sexual.py +35 -5
  118. azure/ai/evaluation/_evaluators/_content_safety/_violence.py +34 -4
  119. azure/ai/evaluation/_evaluators/_document_retrieval/__init__.py +7 -0
  120. azure/ai/evaluation/_evaluators/_document_retrieval/_document_retrieval.py +442 -0
  121. azure/ai/evaluation/_evaluators/_eci/_eci.py +6 -3
  122. azure/ai/evaluation/_evaluators/_f1_score/_f1_score.py +97 -70
  123. azure/ai/evaluation/_evaluators/_fluency/_fluency.py +39 -3
  124. azure/ai/evaluation/_evaluators/_gleu/_gleu.py +80 -25
  125. azure/ai/evaluation/_evaluators/_groundedness/_groundedness.py +230 -20
  126. azure/ai/evaluation/_evaluators/_groundedness/groundedness_with_query.prompty +30 -29
  127. azure/ai/evaluation/_evaluators/_groundedness/groundedness_without_query.prompty +19 -14
  128. azure/ai/evaluation/_evaluators/_intent_resolution/__init__.py +7 -0
  129. azure/ai/evaluation/_evaluators/_intent_resolution/_intent_resolution.py +196 -0
  130. azure/ai/evaluation/_evaluators/_intent_resolution/intent_resolution.prompty +275 -0
  131. azure/ai/evaluation/_evaluators/_meteor/_meteor.py +89 -36
  132. azure/ai/evaluation/_evaluators/_protected_material/_protected_material.py +22 -4
  133. azure/ai/evaluation/_evaluators/_qa/_qa.py +94 -35
  134. azure/ai/evaluation/_evaluators/_relevance/_relevance.py +100 -4
  135. azure/ai/evaluation/_evaluators/_relevance/relevance.prompty +154 -56
  136. azure/ai/evaluation/_evaluators/_response_completeness/__init__.py +7 -0
  137. azure/ai/evaluation/_evaluators/_response_completeness/_response_completeness.py +202 -0
  138. azure/ai/evaluation/_evaluators/_response_completeness/response_completeness.prompty +84 -0
  139. azure/ai/evaluation/_evaluators/_retrieval/_retrieval.py +39 -3
  140. azure/ai/evaluation/_evaluators/_rouge/_rouge.py +166 -26
  141. azure/ai/evaluation/_evaluators/_service_groundedness/_service_groundedness.py +38 -7
  142. azure/ai/evaluation/_evaluators/_similarity/_similarity.py +81 -85
  143. azure/ai/evaluation/_evaluators/_task_adherence/__init__.py +7 -0
  144. azure/ai/evaluation/_evaluators/_task_adherence/_task_adherence.py +226 -0
  145. azure/ai/evaluation/_evaluators/_task_adherence/task_adherence.prompty +101 -0
  146. azure/ai/evaluation/_evaluators/_task_completion/__init__.py +7 -0
  147. azure/ai/evaluation/_evaluators/_task_completion/_task_completion.py +177 -0
  148. azure/ai/evaluation/_evaluators/_task_completion/task_completion.prompty +220 -0
  149. azure/ai/evaluation/_evaluators/_task_navigation_efficiency/__init__.py +7 -0
  150. azure/ai/evaluation/_evaluators/_task_navigation_efficiency/_task_navigation_efficiency.py +384 -0
  151. azure/ai/evaluation/_evaluators/_tool_call_accuracy/__init__.py +9 -0
  152. azure/ai/evaluation/_evaluators/_tool_call_accuracy/_tool_call_accuracy.py +298 -0
  153. azure/ai/evaluation/_evaluators/_tool_call_accuracy/tool_call_accuracy.prompty +166 -0
  154. azure/ai/evaluation/_evaluators/_tool_call_success/__init__.py +7 -0
  155. azure/ai/evaluation/_evaluators/_tool_call_success/_tool_call_success.py +306 -0
  156. azure/ai/evaluation/_evaluators/_tool_call_success/tool_call_success.prompty +321 -0
  157. azure/ai/evaluation/_evaluators/_tool_input_accuracy/__init__.py +9 -0
  158. azure/ai/evaluation/_evaluators/_tool_input_accuracy/_tool_input_accuracy.py +263 -0
  159. azure/ai/evaluation/_evaluators/_tool_input_accuracy/tool_input_accuracy.prompty +76 -0
  160. azure/ai/evaluation/_evaluators/_tool_output_utilization/__init__.py +7 -0
  161. azure/ai/evaluation/_evaluators/_tool_output_utilization/_tool_output_utilization.py +225 -0
  162. azure/ai/evaluation/_evaluators/_tool_output_utilization/tool_output_utilization.prompty +221 -0
  163. azure/ai/evaluation/_evaluators/_tool_selection/__init__.py +9 -0
  164. azure/ai/evaluation/_evaluators/_tool_selection/_tool_selection.py +266 -0
  165. azure/ai/evaluation/_evaluators/_tool_selection/tool_selection.prompty +104 -0
  166. azure/ai/evaluation/_evaluators/_ungrounded_attributes/__init__.py +5 -0
  167. azure/ai/evaluation/_evaluators/_ungrounded_attributes/_ungrounded_attributes.py +102 -0
  168. azure/ai/evaluation/_evaluators/_xpia/xpia.py +20 -4
  169. azure/ai/evaluation/_exceptions.py +24 -1
  170. azure/ai/evaluation/_http_utils.py +7 -5
  171. azure/ai/evaluation/_legacy/__init__.py +3 -0
  172. azure/ai/evaluation/_legacy/_adapters/__init__.py +7 -0
  173. azure/ai/evaluation/_legacy/_adapters/_check.py +17 -0
  174. azure/ai/evaluation/_legacy/_adapters/_configuration.py +45 -0
  175. azure/ai/evaluation/_legacy/_adapters/_constants.py +10 -0
  176. azure/ai/evaluation/_legacy/_adapters/_errors.py +29 -0
  177. azure/ai/evaluation/_legacy/_adapters/_flows.py +28 -0
  178. azure/ai/evaluation/_legacy/_adapters/_service.py +16 -0
  179. azure/ai/evaluation/_legacy/_adapters/client.py +51 -0
  180. azure/ai/evaluation/_legacy/_adapters/entities.py +26 -0
  181. azure/ai/evaluation/_legacy/_adapters/tracing.py +28 -0
  182. azure/ai/evaluation/_legacy/_adapters/types.py +15 -0
  183. azure/ai/evaluation/_legacy/_adapters/utils.py +31 -0
  184. azure/ai/evaluation/_legacy/_batch_engine/__init__.py +9 -0
  185. azure/ai/evaluation/_legacy/_batch_engine/_config.py +48 -0
  186. azure/ai/evaluation/_legacy/_batch_engine/_engine.py +477 -0
  187. azure/ai/evaluation/_legacy/_batch_engine/_exceptions.py +88 -0
  188. azure/ai/evaluation/_legacy/_batch_engine/_openai_injector.py +132 -0
  189. azure/ai/evaluation/_legacy/_batch_engine/_result.py +107 -0
  190. azure/ai/evaluation/_legacy/_batch_engine/_run.py +127 -0
  191. azure/ai/evaluation/_legacy/_batch_engine/_run_storage.py +128 -0
  192. azure/ai/evaluation/_legacy/_batch_engine/_run_submitter.py +262 -0
  193. azure/ai/evaluation/_legacy/_batch_engine/_status.py +25 -0
  194. azure/ai/evaluation/_legacy/_batch_engine/_trace.py +97 -0
  195. azure/ai/evaluation/_legacy/_batch_engine/_utils.py +97 -0
  196. azure/ai/evaluation/_legacy/_batch_engine/_utils_deprecated.py +131 -0
  197. azure/ai/evaluation/_legacy/_common/__init__.py +3 -0
  198. azure/ai/evaluation/_legacy/_common/_async_token_provider.py +117 -0
  199. azure/ai/evaluation/_legacy/_common/_logging.py +292 -0
  200. azure/ai/evaluation/_legacy/_common/_thread_pool_executor_with_context.py +17 -0
  201. azure/ai/evaluation/_legacy/prompty/__init__.py +36 -0
  202. azure/ai/evaluation/_legacy/prompty/_connection.py +119 -0
  203. azure/ai/evaluation/_legacy/prompty/_exceptions.py +139 -0
  204. azure/ai/evaluation/_legacy/prompty/_prompty.py +430 -0
  205. azure/ai/evaluation/_legacy/prompty/_utils.py +663 -0
  206. azure/ai/evaluation/_legacy/prompty/_yaml_utils.py +99 -0
  207. azure/ai/evaluation/_model_configurations.py +26 -0
  208. azure/ai/evaluation/_safety_evaluation/__init__.py +3 -0
  209. azure/ai/evaluation/_safety_evaluation/_generated_rai_client.py +0 -0
  210. azure/ai/evaluation/_safety_evaluation/_safety_evaluation.py +917 -0
  211. azure/ai/evaluation/_user_agent.py +32 -1
  212. azure/ai/evaluation/_vendor/rouge_score/rouge_scorer.py +0 -4
  213. azure/ai/evaluation/_vendor/rouge_score/scoring.py +0 -4
  214. azure/ai/evaluation/_vendor/rouge_score/tokenize.py +0 -4
  215. azure/ai/evaluation/_version.py +2 -1
  216. azure/ai/evaluation/red_team/__init__.py +22 -0
  217. azure/ai/evaluation/red_team/_agent/__init__.py +3 -0
  218. azure/ai/evaluation/red_team/_agent/_agent_functions.py +261 -0
  219. azure/ai/evaluation/red_team/_agent/_agent_tools.py +461 -0
  220. azure/ai/evaluation/red_team/_agent/_agent_utils.py +89 -0
  221. azure/ai/evaluation/red_team/_agent/_semantic_kernel_plugin.py +228 -0
  222. azure/ai/evaluation/red_team/_attack_objective_generator.py +268 -0
  223. azure/ai/evaluation/red_team/_attack_strategy.py +49 -0
  224. azure/ai/evaluation/red_team/_callback_chat_target.py +115 -0
  225. azure/ai/evaluation/red_team/_default_converter.py +21 -0
  226. azure/ai/evaluation/red_team/_evaluation_processor.py +505 -0
  227. azure/ai/evaluation/red_team/_mlflow_integration.py +430 -0
  228. azure/ai/evaluation/red_team/_orchestrator_manager.py +803 -0
  229. azure/ai/evaluation/red_team/_red_team.py +1717 -0
  230. azure/ai/evaluation/red_team/_red_team_result.py +661 -0
  231. azure/ai/evaluation/red_team/_result_processor.py +1708 -0
  232. azure/ai/evaluation/red_team/_utils/__init__.py +37 -0
  233. azure/ai/evaluation/red_team/_utils/_rai_service_eval_chat_target.py +128 -0
  234. azure/ai/evaluation/red_team/_utils/_rai_service_target.py +601 -0
  235. azure/ai/evaluation/red_team/_utils/_rai_service_true_false_scorer.py +114 -0
  236. azure/ai/evaluation/red_team/_utils/constants.py +72 -0
  237. azure/ai/evaluation/red_team/_utils/exception_utils.py +345 -0
  238. azure/ai/evaluation/red_team/_utils/file_utils.py +266 -0
  239. azure/ai/evaluation/red_team/_utils/formatting_utils.py +365 -0
  240. azure/ai/evaluation/red_team/_utils/logging_utils.py +139 -0
  241. azure/ai/evaluation/red_team/_utils/metric_mapping.py +73 -0
  242. azure/ai/evaluation/red_team/_utils/objective_utils.py +46 -0
  243. azure/ai/evaluation/red_team/_utils/progress_utils.py +252 -0
  244. azure/ai/evaluation/red_team/_utils/retry_utils.py +218 -0
  245. azure/ai/evaluation/red_team/_utils/strategy_utils.py +218 -0
  246. azure/ai/evaluation/simulator/_adversarial_scenario.py +6 -0
  247. azure/ai/evaluation/simulator/_adversarial_simulator.py +187 -80
  248. azure/ai/evaluation/simulator/_constants.py +1 -0
  249. azure/ai/evaluation/simulator/_conversation/__init__.py +138 -11
  250. azure/ai/evaluation/simulator/_conversation/_conversation.py +6 -2
  251. azure/ai/evaluation/simulator/_conversation/constants.py +1 -1
  252. azure/ai/evaluation/simulator/_direct_attack_simulator.py +37 -24
  253. azure/ai/evaluation/simulator/_helpers/_language_suffix_mapping.py +1 -0
  254. azure/ai/evaluation/simulator/_indirect_attack_simulator.py +56 -28
  255. azure/ai/evaluation/simulator/_model_tools/__init__.py +2 -1
  256. azure/ai/evaluation/simulator/_model_tools/_generated_rai_client.py +225 -0
  257. azure/ai/evaluation/simulator/_model_tools/_identity_manager.py +12 -10
  258. azure/ai/evaluation/simulator/_model_tools/_proxy_completion_model.py +100 -45
  259. azure/ai/evaluation/simulator/_model_tools/_rai_client.py +101 -3
  260. azure/ai/evaluation/simulator/_model_tools/_template_handler.py +31 -11
  261. azure/ai/evaluation/simulator/_model_tools/models.py +20 -17
  262. azure/ai/evaluation/simulator/_simulator.py +43 -19
  263. {azure_ai_evaluation-1.0.1.dist-info → azure_ai_evaluation-1.13.5.dist-info}/METADATA +378 -27
  264. azure_ai_evaluation-1.13.5.dist-info/RECORD +305 -0
  265. {azure_ai_evaluation-1.0.1.dist-info → azure_ai_evaluation-1.13.5.dist-info}/WHEEL +1 -1
  266. azure/ai/evaluation/_evaluators/_multimodal/__init__.py +0 -20
  267. azure/ai/evaluation/_evaluators/_multimodal/_content_safety_multimodal.py +0 -132
  268. azure/ai/evaluation/_evaluators/_multimodal/_content_safety_multimodal_base.py +0 -55
  269. azure/ai/evaluation/_evaluators/_multimodal/_hate_unfairness.py +0 -100
  270. azure/ai/evaluation/_evaluators/_multimodal/_protected_material.py +0 -124
  271. azure/ai/evaluation/_evaluators/_multimodal/_self_harm.py +0 -100
  272. azure/ai/evaluation/_evaluators/_multimodal/_sexual.py +0 -100
  273. azure/ai/evaluation/_evaluators/_multimodal/_violence.py +0 -100
  274. azure/ai/evaluation/simulator/_tracing.py +0 -89
  275. azure_ai_evaluation-1.0.1.dist-info/RECORD +0 -119
  276. {azure_ai_evaluation-1.0.1.dist-info → azure_ai_evaluation-1.13.5.dist-info/licenses}/NOTICE.txt +0 -0
  277. {azure_ai_evaluation-1.0.1.dist-info → azure_ai_evaluation-1.13.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1235 @@
1
+ # pylint: disable=too-many-lines
2
+ # coding=utf-8
3
+ # --------------------------------------------------------------------------
4
+ # Copyright (c) Microsoft Corporation. All rights reserved.
5
+ # Licensed under the MIT License. See License.txt in the project root for
6
+ # license information.
7
+ # --------------------------------------------------------------------------
8
+ # pylint: disable=protected-access, broad-except
9
+
10
+ import copy
11
+ import calendar
12
+ import decimal
13
+ import functools
14
+ import sys
15
+ import logging
16
+ import base64
17
+ import re
18
+ import typing
19
+ import enum
20
+ import email.utils
21
+ from datetime import datetime, date, time, timedelta, timezone
22
+ from json import JSONEncoder
23
+ import xml.etree.ElementTree as ET
24
+ from typing_extensions import Self
25
+ import isodate
26
+ from azure.core.exceptions import DeserializationError
27
+ from azure.core import CaseInsensitiveEnumMeta
28
+ from azure.core.pipeline import PipelineResponse
29
+ from azure.core.serialization import _Null
30
+
31
+ if sys.version_info >= (3, 9):
32
+ from collections.abc import MutableMapping
33
+ else:
34
+ from typing import MutableMapping
35
+
36
+ _LOGGER = logging.getLogger(__name__)
37
+
38
+ __all__ = ["SdkJSONEncoder", "Model", "rest_field", "rest_discriminator"]
39
+
40
+ TZ_UTC = timezone.utc
41
+ _T = typing.TypeVar("_T")
42
+
43
+
44
+ def _timedelta_as_isostr(td: timedelta) -> str:
45
+ """Converts a datetime.timedelta object into an ISO 8601 formatted string, e.g. 'P4DT12H30M05S'
46
+
47
+ Function adapted from the Tin Can Python project: https://github.com/RusticiSoftware/TinCanPython
48
+
49
+ :param timedelta td: The timedelta to convert
50
+ :rtype: str
51
+ :return: ISO8601 version of this timedelta
52
+ """
53
+
54
+ # Split seconds to larger units
55
+ seconds = td.total_seconds()
56
+ minutes, seconds = divmod(seconds, 60)
57
+ hours, minutes = divmod(minutes, 60)
58
+ days, hours = divmod(hours, 24)
59
+
60
+ days, hours, minutes = list(map(int, (days, hours, minutes)))
61
+ seconds = round(seconds, 6)
62
+
63
+ # Build date
64
+ date_str = ""
65
+ if days:
66
+ date_str = "%sD" % days
67
+
68
+ if hours or minutes or seconds:
69
+ # Build time
70
+ time_str = "T"
71
+
72
+ # Hours
73
+ bigger_exists = date_str or hours
74
+ if bigger_exists:
75
+ time_str += "{:02}H".format(hours)
76
+
77
+ # Minutes
78
+ bigger_exists = bigger_exists or minutes
79
+ if bigger_exists:
80
+ time_str += "{:02}M".format(minutes)
81
+
82
+ # Seconds
83
+ try:
84
+ if seconds.is_integer():
85
+ seconds_string = "{:02}".format(int(seconds))
86
+ else:
87
+ # 9 chars long w/ leading 0, 6 digits after decimal
88
+ seconds_string = "%09.6f" % seconds
89
+ # Remove trailing zeros
90
+ seconds_string = seconds_string.rstrip("0")
91
+ except AttributeError: # int.is_integer() raises
92
+ seconds_string = "{:02}".format(seconds)
93
+
94
+ time_str += "{}S".format(seconds_string)
95
+ else:
96
+ time_str = ""
97
+
98
+ return "P" + date_str + time_str
99
+
100
+
101
+ def _serialize_bytes(o, format: typing.Optional[str] = None) -> str:
102
+ encoded = base64.b64encode(o).decode()
103
+ if format == "base64url":
104
+ return encoded.strip("=").replace("+", "-").replace("/", "_")
105
+ return encoded
106
+
107
+
108
+ def _serialize_datetime(o, format: typing.Optional[str] = None):
109
+ if hasattr(o, "year") and hasattr(o, "hour"):
110
+ if format == "rfc7231":
111
+ return email.utils.format_datetime(o, usegmt=True)
112
+ if format == "unix-timestamp":
113
+ return int(calendar.timegm(o.utctimetuple()))
114
+
115
+ # astimezone() fails for naive times in Python 2.7, so make make sure o is aware (tzinfo is set)
116
+ if not o.tzinfo:
117
+ iso_formatted = o.replace(tzinfo=TZ_UTC).isoformat()
118
+ else:
119
+ iso_formatted = o.astimezone(TZ_UTC).isoformat()
120
+ # Replace the trailing "+00:00" UTC offset with "Z" (RFC 3339: https://www.ietf.org/rfc/rfc3339.txt)
121
+ return iso_formatted.replace("+00:00", "Z")
122
+ # Next try datetime.date or datetime.time
123
+ return o.isoformat()
124
+
125
+
126
+ def _is_readonly(p):
127
+ try:
128
+ return p._visibility == ["read"]
129
+ except AttributeError:
130
+ return False
131
+
132
+
133
+ class SdkJSONEncoder(JSONEncoder):
134
+ """A JSON encoder that's capable of serializing datetime objects and bytes."""
135
+
136
+ def __init__(self, *args, exclude_readonly: bool = False, format: typing.Optional[str] = None, **kwargs):
137
+ super().__init__(*args, **kwargs)
138
+ self.exclude_readonly = exclude_readonly
139
+ self.format = format
140
+
141
+ def default(self, o): # pylint: disable=too-many-return-statements
142
+ if _is_model(o):
143
+ if self.exclude_readonly:
144
+ readonly_props = [p._rest_name for p in o._attr_to_rest_field.values() if _is_readonly(p)]
145
+ return {k: v for k, v in o.items() if k not in readonly_props}
146
+ return dict(o.items())
147
+ try:
148
+ return super(SdkJSONEncoder, self).default(o)
149
+ except TypeError:
150
+ if isinstance(o, _Null):
151
+ return None
152
+ if isinstance(o, decimal.Decimal):
153
+ return float(o)
154
+ if isinstance(o, (bytes, bytearray)):
155
+ return _serialize_bytes(o, self.format)
156
+ try:
157
+ # First try datetime.datetime
158
+ return _serialize_datetime(o, self.format)
159
+ except AttributeError:
160
+ pass
161
+ # Last, try datetime.timedelta
162
+ try:
163
+ return _timedelta_as_isostr(o)
164
+ except AttributeError:
165
+ # This will be raised when it hits value.total_seconds in the method above
166
+ pass
167
+ return super(SdkJSONEncoder, self).default(o)
168
+
169
+
170
+ _VALID_DATE = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}" + r"\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?")
171
+ _VALID_RFC7231 = re.compile(
172
+ r"(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s\d{2}\s"
173
+ r"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d{4}\s\d{2}:\d{2}:\d{2}\sGMT"
174
+ )
175
+
176
+
177
+ def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
178
+ """Deserialize ISO-8601 formatted string into Datetime object.
179
+
180
+ :param str attr: response string to be deserialized.
181
+ :rtype: ~datetime.datetime
182
+ :returns: The datetime object from that input
183
+ """
184
+ if isinstance(attr, datetime):
185
+ # i'm already deserialized
186
+ return attr
187
+ attr = attr.upper()
188
+ match = _VALID_DATE.match(attr)
189
+ if not match:
190
+ raise ValueError("Invalid datetime string: " + attr)
191
+
192
+ check_decimal = attr.split(".")
193
+ if len(check_decimal) > 1:
194
+ decimal_str = ""
195
+ for digit in check_decimal[1]:
196
+ if digit.isdigit():
197
+ decimal_str += digit
198
+ else:
199
+ break
200
+ if len(decimal_str) > 6:
201
+ attr = attr.replace(decimal_str, decimal_str[0:6])
202
+
203
+ date_obj = isodate.parse_datetime(attr)
204
+ test_utc = date_obj.utctimetuple()
205
+ if test_utc.tm_year > 9999 or test_utc.tm_year < 1:
206
+ raise OverflowError("Hit max or min date")
207
+ return date_obj
208
+
209
+
210
+ def _deserialize_datetime_rfc7231(attr: typing.Union[str, datetime]) -> datetime:
211
+ """Deserialize RFC7231 formatted string into Datetime object.
212
+
213
+ :param str attr: response string to be deserialized.
214
+ :rtype: ~datetime.datetime
215
+ :returns: The datetime object from that input
216
+ """
217
+ if isinstance(attr, datetime):
218
+ # i'm already deserialized
219
+ return attr
220
+ match = _VALID_RFC7231.match(attr)
221
+ if not match:
222
+ raise ValueError("Invalid datetime string: " + attr)
223
+
224
+ return email.utils.parsedate_to_datetime(attr)
225
+
226
+
227
+ def _deserialize_datetime_unix_timestamp(attr: typing.Union[float, datetime]) -> datetime:
228
+ """Deserialize unix timestamp into Datetime object.
229
+
230
+ :param str attr: response string to be deserialized.
231
+ :rtype: ~datetime.datetime
232
+ :returns: The datetime object from that input
233
+ """
234
+ if isinstance(attr, datetime):
235
+ # i'm already deserialized
236
+ return attr
237
+ return datetime.fromtimestamp(attr, TZ_UTC)
238
+
239
+
240
+ def _deserialize_date(attr: typing.Union[str, date]) -> date:
241
+ """Deserialize ISO-8601 formatted string into Date object.
242
+ :param str attr: response string to be deserialized.
243
+ :rtype: date
244
+ :returns: The date object from that input
245
+ """
246
+ # This must NOT use defaultmonth/defaultday. Using None ensure this raises an exception.
247
+ if isinstance(attr, date):
248
+ return attr
249
+ return isodate.parse_date(attr, defaultmonth=None, defaultday=None) # type: ignore
250
+
251
+
252
+ def _deserialize_time(attr: typing.Union[str, time]) -> time:
253
+ """Deserialize ISO-8601 formatted string into time object.
254
+
255
+ :param str attr: response string to be deserialized.
256
+ :rtype: datetime.time
257
+ :returns: The time object from that input
258
+ """
259
+ if isinstance(attr, time):
260
+ return attr
261
+ return isodate.parse_time(attr)
262
+
263
+
264
+ def _deserialize_bytes(attr):
265
+ if isinstance(attr, (bytes, bytearray)):
266
+ return attr
267
+ return bytes(base64.b64decode(attr))
268
+
269
+
270
+ def _deserialize_bytes_base64(attr):
271
+ if isinstance(attr, (bytes, bytearray)):
272
+ return attr
273
+ padding = "=" * (3 - (len(attr) + 3) % 4) # type: ignore
274
+ attr = attr + padding # type: ignore
275
+ encoded = attr.replace("-", "+").replace("_", "/")
276
+ return bytes(base64.b64decode(encoded))
277
+
278
+
279
+ def _deserialize_duration(attr):
280
+ if isinstance(attr, timedelta):
281
+ return attr
282
+ return isodate.parse_duration(attr)
283
+
284
+
285
+ def _deserialize_decimal(attr):
286
+ if isinstance(attr, decimal.Decimal):
287
+ return attr
288
+ return decimal.Decimal(str(attr))
289
+
290
+
291
+ def _deserialize_int_as_str(attr):
292
+ if isinstance(attr, int):
293
+ return attr
294
+ return int(attr)
295
+
296
+
297
+ _DESERIALIZE_MAPPING = {
298
+ datetime: _deserialize_datetime,
299
+ date: _deserialize_date,
300
+ time: _deserialize_time,
301
+ bytes: _deserialize_bytes,
302
+ bytearray: _deserialize_bytes,
303
+ timedelta: _deserialize_duration,
304
+ typing.Any: lambda x: x,
305
+ decimal.Decimal: _deserialize_decimal,
306
+ }
307
+
308
+ _DESERIALIZE_MAPPING_WITHFORMAT = {
309
+ "rfc3339": _deserialize_datetime,
310
+ "rfc7231": _deserialize_datetime_rfc7231,
311
+ "unix-timestamp": _deserialize_datetime_unix_timestamp,
312
+ "base64": _deserialize_bytes,
313
+ "base64url": _deserialize_bytes_base64,
314
+ }
315
+
316
+
317
+ def get_deserializer(annotation: typing.Any, rf: typing.Optional["_RestField"] = None):
318
+ if annotation is int and rf and rf._format == "str":
319
+ return _deserialize_int_as_str
320
+ if rf and rf._format:
321
+ return _DESERIALIZE_MAPPING_WITHFORMAT.get(rf._format)
322
+ return _DESERIALIZE_MAPPING.get(annotation) # pyright: ignore
323
+
324
+
325
+ def _get_type_alias_type(module_name: str, alias_name: str):
326
+ types = {
327
+ k: v
328
+ for k, v in sys.modules[module_name].__dict__.items()
329
+ if isinstance(v, typing._GenericAlias) # type: ignore
330
+ }
331
+ if alias_name not in types:
332
+ return alias_name
333
+ return types[alias_name]
334
+
335
+
336
+ def _get_model(module_name: str, model_name: str):
337
+ models = {k: v for k, v in sys.modules[module_name].__dict__.items() if isinstance(v, type)}
338
+ module_end = module_name.rsplit(".", 1)[0]
339
+ models.update({k: v for k, v in sys.modules[module_end].__dict__.items() if isinstance(v, type)})
340
+ if isinstance(model_name, str):
341
+ model_name = model_name.split(".")[-1]
342
+ if model_name not in models:
343
+ return model_name
344
+ return models[model_name]
345
+
346
+
347
+ _UNSET = object()
348
+
349
+
350
+ class _MyMutableMapping(MutableMapping[str, typing.Any]): # pylint: disable=unsubscriptable-object
351
+ def __init__(self, data: typing.Dict[str, typing.Any]) -> None:
352
+ self._data = data
353
+
354
+ def __contains__(self, key: typing.Any) -> bool:
355
+ return key in self._data
356
+
357
+ def __getitem__(self, key: str) -> typing.Any:
358
+ return self._data.__getitem__(key)
359
+
360
+ def __setitem__(self, key: str, value: typing.Any) -> None:
361
+ self._data.__setitem__(key, value)
362
+
363
+ def __delitem__(self, key: str) -> None:
364
+ self._data.__delitem__(key)
365
+
366
+ def __iter__(self) -> typing.Iterator[typing.Any]:
367
+ return self._data.__iter__()
368
+
369
+ def __len__(self) -> int:
370
+ return self._data.__len__()
371
+
372
+ def __ne__(self, other: typing.Any) -> bool:
373
+ return not self.__eq__(other)
374
+
375
+ def keys(self) -> typing.KeysView[str]:
376
+ """
377
+ :returns: a set-like object providing a view on D's keys
378
+ :rtype: ~typing.KeysView
379
+ """
380
+ return self._data.keys()
381
+
382
+ def values(self) -> typing.ValuesView[typing.Any]:
383
+ """
384
+ :returns: an object providing a view on D's values
385
+ :rtype: ~typing.ValuesView
386
+ """
387
+ return self._data.values()
388
+
389
+ def items(self) -> typing.ItemsView[str, typing.Any]:
390
+ """
391
+ :returns: set-like object providing a view on D's items
392
+ :rtype: ~typing.ItemsView
393
+ """
394
+ return self._data.items()
395
+
396
+ def get(self, key: str, default: typing.Any = None) -> typing.Any:
397
+ """
398
+ Get the value for key if key is in the dictionary, else default.
399
+ :param str key: The key to look up.
400
+ :param any default: The value to return if key is not in the dictionary. Defaults to None
401
+ :returns: D[k] if k in D, else d.
402
+ :rtype: any
403
+ """
404
+ try:
405
+ return self[key]
406
+ except KeyError:
407
+ return default
408
+
409
+ @typing.overload
410
+ def pop(self, key: str) -> typing.Any: ...
411
+
412
+ @typing.overload
413
+ def pop(self, key: str, default: _T) -> _T: ...
414
+
415
+ @typing.overload
416
+ def pop(self, key: str, default: typing.Any) -> typing.Any: ...
417
+
418
+ def pop(self, key: str, default: typing.Any = _UNSET) -> typing.Any:
419
+ """
420
+ Removes specified key and return the corresponding value.
421
+ :param str key: The key to pop.
422
+ :param any default: The value to return if key is not in the dictionary
423
+ :returns: The value corresponding to the key.
424
+ :rtype: any
425
+ :raises KeyError: If key is not found and default is not given.
426
+ """
427
+ if default is _UNSET:
428
+ return self._data.pop(key)
429
+ return self._data.pop(key, default)
430
+
431
+ def popitem(self) -> typing.Tuple[str, typing.Any]:
432
+ """
433
+ Removes and returns some (key, value) pair
434
+ :returns: The (key, value) pair.
435
+ :rtype: tuple
436
+ :raises KeyError: if D is empty.
437
+ """
438
+ return self._data.popitem()
439
+
440
+ def clear(self) -> None:
441
+ """
442
+ Remove all items from D.
443
+ """
444
+ self._data.clear()
445
+
446
+ def update(self, *args: typing.Any, **kwargs: typing.Any) -> None:
447
+ """
448
+ Updates D from mapping/iterable E and F.
449
+ :param any args: Either a mapping object or an iterable of key-value pairs.
450
+ """
451
+ self._data.update(*args, **kwargs)
452
+
453
+ @typing.overload
454
+ def setdefault(self, key: str, default: None = None) -> None: ...
455
+
456
+ @typing.overload
457
+ def setdefault(self, key: str, default: typing.Any) -> typing.Any: ...
458
+
459
+ def setdefault(self, key: str, default: typing.Any = _UNSET) -> typing.Any:
460
+ """
461
+ Same as calling D.get(k, d), and setting D[k]=d if k not found
462
+ :param str key: The key to look up.
463
+ :param any default: The value to set if key is not in the dictionary
464
+ :returns: D[k] if k in D, else d.
465
+ :rtype: any
466
+ """
467
+ if default is _UNSET:
468
+ return self._data.setdefault(key)
469
+ return self._data.setdefault(key, default)
470
+
471
+ def __eq__(self, other: typing.Any) -> bool:
472
+ try:
473
+ other_model = self.__class__(other)
474
+ except Exception:
475
+ return False
476
+ return self._data == other_model._data
477
+
478
+ def __repr__(self) -> str:
479
+ return str(self._data)
480
+
481
+
482
+ def _is_model(obj: typing.Any) -> bool:
483
+ return getattr(obj, "_is_model", False)
484
+
485
+
486
+ def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-many-return-statements
487
+ if isinstance(o, list):
488
+ return [_serialize(x, format) for x in o]
489
+ if isinstance(o, dict):
490
+ return {k: _serialize(v, format) for k, v in o.items()}
491
+ if isinstance(o, set):
492
+ return {_serialize(x, format) for x in o}
493
+ if isinstance(o, tuple):
494
+ return tuple(_serialize(x, format) for x in o)
495
+ if isinstance(o, (bytes, bytearray)):
496
+ return _serialize_bytes(o, format)
497
+ if isinstance(o, decimal.Decimal):
498
+ return float(o)
499
+ if isinstance(o, enum.Enum):
500
+ return o.value
501
+ if isinstance(o, int):
502
+ if format == "str":
503
+ return str(o)
504
+ return o
505
+ try:
506
+ # First try datetime.datetime
507
+ return _serialize_datetime(o, format)
508
+ except AttributeError:
509
+ pass
510
+ # Last, try datetime.timedelta
511
+ try:
512
+ return _timedelta_as_isostr(o)
513
+ except AttributeError:
514
+ # This will be raised when it hits value.total_seconds in the method above
515
+ pass
516
+ return o
517
+
518
+
519
+ def _get_rest_field(
520
+ attr_to_rest_field: typing.Dict[str, "_RestField"], rest_name: str
521
+ ) -> typing.Optional["_RestField"]:
522
+ try:
523
+ return next(rf for rf in attr_to_rest_field.values() if rf._rest_name == rest_name)
524
+ except StopIteration:
525
+ return None
526
+
527
+
528
+ def _create_value(rf: typing.Optional["_RestField"], value: typing.Any) -> typing.Any:
529
+ if not rf:
530
+ return _serialize(value, None)
531
+ if rf._is_multipart_file_input:
532
+ return value
533
+ if rf._is_model:
534
+ return _deserialize(rf._type, value)
535
+ if isinstance(value, ET.Element):
536
+ value = _deserialize(rf._type, value)
537
+ return _serialize(value, rf._format)
538
+
539
+
540
+ class Model(_MyMutableMapping):
541
+ _is_model = True
542
+ # label whether current class's _attr_to_rest_field has been calculated
543
+ # could not see _attr_to_rest_field directly because subclass inherits it from parent class
544
+ _calculated: typing.Set[str] = set()
545
+
546
+ def __init__(self, *args: typing.Any, **kwargs: typing.Any) -> None:
547
+ class_name = self.__class__.__name__
548
+ if len(args) > 1:
549
+ raise TypeError(f"{class_name}.__init__() takes 2 positional arguments but {len(args) + 1} were given")
550
+ dict_to_pass = {
551
+ rest_field._rest_name: rest_field._default
552
+ for rest_field in self._attr_to_rest_field.values()
553
+ if rest_field._default is not _UNSET
554
+ }
555
+ if args: # pylint: disable=too-many-nested-blocks
556
+ if isinstance(args[0], ET.Element):
557
+ existed_attr_keys = []
558
+ model_meta = getattr(self, "_xml", {})
559
+
560
+ for rf in self._attr_to_rest_field.values():
561
+ prop_meta = getattr(rf, "_xml", {})
562
+ xml_name = prop_meta.get("name", rf._rest_name)
563
+ xml_ns = prop_meta.get("ns", model_meta.get("ns", None))
564
+ if xml_ns:
565
+ xml_name = "{" + xml_ns + "}" + xml_name
566
+
567
+ # attribute
568
+ if prop_meta.get("attribute", False) and args[0].get(xml_name) is not None:
569
+ existed_attr_keys.append(xml_name)
570
+ dict_to_pass[rf._rest_name] = _deserialize(rf._type, args[0].get(xml_name))
571
+ continue
572
+
573
+ # unwrapped element is array
574
+ if prop_meta.get("unwrapped", False):
575
+ # unwrapped array could either use prop items meta/prop meta
576
+ if prop_meta.get("itemsName"):
577
+ xml_name = prop_meta.get("itemsName")
578
+ xml_ns = prop_meta.get("itemNs")
579
+ if xml_ns:
580
+ xml_name = "{" + xml_ns + "}" + xml_name
581
+ items = args[0].findall(xml_name) # pyright: ignore
582
+ if len(items) > 0:
583
+ existed_attr_keys.append(xml_name)
584
+ dict_to_pass[rf._rest_name] = _deserialize(rf._type, items)
585
+ continue
586
+
587
+ # text element is primitive type
588
+ if prop_meta.get("text", False):
589
+ if args[0].text is not None:
590
+ dict_to_pass[rf._rest_name] = _deserialize(rf._type, args[0].text)
591
+ continue
592
+
593
+ # wrapped element could be normal property or array, it should only have one element
594
+ item = args[0].find(xml_name)
595
+ if item is not None:
596
+ existed_attr_keys.append(xml_name)
597
+ dict_to_pass[rf._rest_name] = _deserialize(rf._type, item)
598
+
599
+ # rest thing is additional properties
600
+ for e in args[0]:
601
+ if e.tag not in existed_attr_keys:
602
+ dict_to_pass[e.tag] = _convert_element(e)
603
+ else:
604
+ dict_to_pass.update(
605
+ {k: _create_value(_get_rest_field(self._attr_to_rest_field, k), v) for k, v in args[0].items()}
606
+ )
607
+ else:
608
+ non_attr_kwargs = [k for k in kwargs if k not in self._attr_to_rest_field]
609
+ if non_attr_kwargs:
610
+ # actual type errors only throw the first wrong keyword arg they see, so following that.
611
+ raise TypeError(f"{class_name}.__init__() got an unexpected keyword argument '{non_attr_kwargs[0]}'")
612
+ dict_to_pass.update(
613
+ {
614
+ self._attr_to_rest_field[k]._rest_name: _create_value(self._attr_to_rest_field[k], v)
615
+ for k, v in kwargs.items()
616
+ if v is not None
617
+ }
618
+ )
619
+ super().__init__(dict_to_pass)
620
+
621
+ def copy(self) -> "Model":
622
+ return Model(self.__dict__)
623
+
624
+ def __new__(cls, *args: typing.Any, **kwargs: typing.Any) -> Self:
625
+ if f"{cls.__module__}.{cls.__qualname__}" not in cls._calculated:
626
+ # we know the last nine classes in mro are going to be 'Model', '_MyMutableMapping', 'MutableMapping',
627
+ # 'Mapping', 'Collection', 'Sized', 'Iterable', 'Container' and 'object'
628
+ mros = cls.__mro__[:-9][::-1] # ignore parents, and reverse the mro order
629
+ attr_to_rest_field: typing.Dict[str, _RestField] = { # map attribute name to rest_field property
630
+ k: v for mro_class in mros for k, v in mro_class.__dict__.items() if k[0] != "_" and hasattr(v, "_type")
631
+ }
632
+ annotations = {
633
+ k: v
634
+ for mro_class in mros
635
+ if hasattr(mro_class, "__annotations__")
636
+ for k, v in mro_class.__annotations__.items()
637
+ }
638
+ for attr, rf in attr_to_rest_field.items():
639
+ rf._module = cls.__module__
640
+ if not rf._type:
641
+ rf._type = rf._get_deserialize_callable_from_annotation(annotations.get(attr, None))
642
+ if not rf._rest_name_input:
643
+ rf._rest_name_input = attr
644
+ cls._attr_to_rest_field: typing.Dict[str, _RestField] = dict(attr_to_rest_field.items())
645
+ cls._calculated.add(f"{cls.__module__}.{cls.__qualname__}")
646
+
647
+ return super().__new__(cls) # pylint: disable=no-value-for-parameter
648
+
649
+ def __init_subclass__(cls, discriminator: typing.Optional[str] = None) -> None:
650
+ for base in cls.__bases__:
651
+ if hasattr(base, "__mapping__"):
652
+ base.__mapping__[discriminator or cls.__name__] = cls # type: ignore
653
+
654
+ @classmethod
655
+ def _get_discriminator(cls, exist_discriminators) -> typing.Optional["_RestField"]:
656
+ for v in cls.__dict__.values():
657
+ if isinstance(v, _RestField) and v._is_discriminator and v._rest_name not in exist_discriminators:
658
+ return v
659
+ return None
660
+
661
+ @classmethod
662
+ def _deserialize(cls, data, exist_discriminators):
663
+ if not hasattr(cls, "__mapping__"):
664
+ return cls(data)
665
+ discriminator = cls._get_discriminator(exist_discriminators)
666
+ if discriminator is None:
667
+ return cls(data)
668
+ exist_discriminators.append(discriminator._rest_name)
669
+ if isinstance(data, ET.Element):
670
+ model_meta = getattr(cls, "_xml", {})
671
+ prop_meta = getattr(discriminator, "_xml", {})
672
+ xml_name = prop_meta.get("name", discriminator._rest_name)
673
+ xml_ns = prop_meta.get("ns", model_meta.get("ns", None))
674
+ if xml_ns:
675
+ xml_name = "{" + xml_ns + "}" + xml_name
676
+
677
+ if data.get(xml_name) is not None:
678
+ discriminator_value = data.get(xml_name)
679
+ else:
680
+ discriminator_value = data.find(xml_name).text # pyright: ignore
681
+ else:
682
+ discriminator_value = data.get(discriminator._rest_name)
683
+ mapped_cls = cls.__mapping__.get(discriminator_value, cls) # pyright: ignore
684
+ return mapped_cls._deserialize(data, exist_discriminators)
685
+
686
+ def as_dict(self, *, exclude_readonly: bool = False) -> typing.Dict[str, typing.Any]:
687
+ """Return a dict that can be turned into json using json.dump.
688
+
689
+ :keyword bool exclude_readonly: Whether to remove the readonly properties.
690
+ :returns: A dict JSON compatible object
691
+ :rtype: dict
692
+ """
693
+
694
+ result = {}
695
+ readonly_props = []
696
+ if exclude_readonly:
697
+ readonly_props = [p._rest_name for p in self._attr_to_rest_field.values() if _is_readonly(p)]
698
+ for k, v in self.items():
699
+ if exclude_readonly and k in readonly_props: # pyright: ignore
700
+ continue
701
+ is_multipart_file_input = False
702
+ try:
703
+ is_multipart_file_input = next(
704
+ rf for rf in self._attr_to_rest_field.values() if rf._rest_name == k
705
+ )._is_multipart_file_input
706
+ except StopIteration:
707
+ pass
708
+ result[k] = v if is_multipart_file_input else Model._as_dict_value(v, exclude_readonly=exclude_readonly)
709
+ return result
710
+
711
+ @staticmethod
712
+ def _as_dict_value(v: typing.Any, exclude_readonly: bool = False) -> typing.Any:
713
+ if v is None or isinstance(v, _Null):
714
+ return None
715
+ if isinstance(v, (list, tuple, set)):
716
+ return type(v)(Model._as_dict_value(x, exclude_readonly=exclude_readonly) for x in v)
717
+ if isinstance(v, dict):
718
+ return {dk: Model._as_dict_value(dv, exclude_readonly=exclude_readonly) for dk, dv in v.items()}
719
+ return v.as_dict(exclude_readonly=exclude_readonly) if hasattr(v, "as_dict") else v
720
+
721
+
722
+ def _deserialize_model(model_deserializer: typing.Optional[typing.Callable], obj):
723
+ if _is_model(obj):
724
+ return obj
725
+ return _deserialize(model_deserializer, obj)
726
+
727
+
728
+ def _deserialize_with_optional(if_obj_deserializer: typing.Optional[typing.Callable], obj):
729
+ if obj is None:
730
+ return obj
731
+ return _deserialize_with_callable(if_obj_deserializer, obj)
732
+
733
+
734
+ def _deserialize_with_union(deserializers, obj):
735
+ for deserializer in deserializers:
736
+ try:
737
+ return _deserialize(deserializer, obj)
738
+ except DeserializationError:
739
+ pass
740
+ raise DeserializationError()
741
+
742
+
743
+ def _deserialize_dict(
744
+ value_deserializer: typing.Optional[typing.Callable],
745
+ module: typing.Optional[str],
746
+ obj: typing.Dict[typing.Any, typing.Any],
747
+ ):
748
+ if obj is None:
749
+ return obj
750
+ if isinstance(obj, ET.Element):
751
+ obj = {child.tag: child for child in obj}
752
+ return {k: _deserialize(value_deserializer, v, module) for k, v in obj.items()}
753
+
754
+
755
+ def _deserialize_multiple_sequence(
756
+ entry_deserializers: typing.List[typing.Optional[typing.Callable]],
757
+ module: typing.Optional[str],
758
+ obj,
759
+ ):
760
+ if obj is None:
761
+ return obj
762
+ return type(obj)(_deserialize(deserializer, entry, module) for entry, deserializer in zip(obj, entry_deserializers))
763
+
764
+
765
+ def _deserialize_sequence(
766
+ deserializer: typing.Optional[typing.Callable],
767
+ module: typing.Optional[str],
768
+ obj,
769
+ ):
770
+ if obj is None:
771
+ return obj
772
+ if isinstance(obj, ET.Element):
773
+ obj = list(obj)
774
+ return type(obj)(_deserialize(deserializer, entry, module) for entry in obj)
775
+
776
+
777
+ def _sorted_annotations(types: typing.List[typing.Any]) -> typing.List[typing.Any]:
778
+ return sorted(
779
+ types,
780
+ key=lambda x: hasattr(x, "__name__") and x.__name__.lower() in ("str", "float", "int", "bool"),
781
+ )
782
+
783
+
784
+ def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-return-statements, too-many-branches
785
+ annotation: typing.Any,
786
+ module: typing.Optional[str],
787
+ rf: typing.Optional["_RestField"] = None,
788
+ ) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
789
+ if not annotation:
790
+ return None
791
+
792
+ # is it a type alias?
793
+ if isinstance(annotation, str):
794
+ if module is not None:
795
+ annotation = _get_type_alias_type(module, annotation)
796
+
797
+ # is it a forward ref / in quotes?
798
+ if isinstance(annotation, (str, typing.ForwardRef)):
799
+ try:
800
+ model_name = annotation.__forward_arg__ # type: ignore
801
+ except AttributeError:
802
+ model_name = annotation
803
+ if module is not None:
804
+ annotation = _get_model(module, model_name) # type: ignore
805
+
806
+ try:
807
+ if module and _is_model(annotation):
808
+ if rf:
809
+ rf._is_model = True
810
+
811
+ return functools.partial(_deserialize_model, annotation) # pyright: ignore
812
+ except Exception:
813
+ pass
814
+
815
+ # is it a literal?
816
+ try:
817
+ if annotation.__origin__ is typing.Literal: # pyright: ignore
818
+ return None
819
+ except AttributeError:
820
+ pass
821
+
822
+ # is it optional?
823
+ try:
824
+ if any(a for a in annotation.__args__ if a == type(None)): # pyright: ignore
825
+ if len(annotation.__args__) <= 2: # pyright: ignore
826
+ if_obj_deserializer = _get_deserialize_callable_from_annotation(
827
+ next(a for a in annotation.__args__ if a != type(None)), module, rf # pyright: ignore
828
+ )
829
+
830
+ return functools.partial(_deserialize_with_optional, if_obj_deserializer)
831
+ # the type is Optional[Union[...]], we need to remove the None type from the Union
832
+ annotation_copy = copy.copy(annotation)
833
+ annotation_copy.__args__ = [a for a in annotation_copy.__args__ if a != type(None)] # pyright: ignore
834
+ return _get_deserialize_callable_from_annotation(annotation_copy, module, rf)
835
+ except AttributeError:
836
+ pass
837
+
838
+ # is it union?
839
+ if getattr(annotation, "__origin__", None) is typing.Union:
840
+ # initial ordering is we make `string` the last deserialization option, because it is often them most generic
841
+ deserializers = [
842
+ _get_deserialize_callable_from_annotation(arg, module, rf)
843
+ for arg in _sorted_annotations(annotation.__args__) # pyright: ignore
844
+ ]
845
+
846
+ return functools.partial(_deserialize_with_union, deserializers)
847
+
848
+ try:
849
+ if annotation._name == "Dict": # pyright: ignore
850
+ value_deserializer = _get_deserialize_callable_from_annotation(
851
+ annotation.__args__[1], module, rf # pyright: ignore
852
+ )
853
+
854
+ return functools.partial(
855
+ _deserialize_dict,
856
+ value_deserializer,
857
+ module,
858
+ )
859
+ except (AttributeError, IndexError):
860
+ pass
861
+ try:
862
+ if annotation._name in ["List", "Set", "Tuple", "Sequence"]: # pyright: ignore
863
+ if len(annotation.__args__) > 1: # pyright: ignore
864
+ entry_deserializers = [
865
+ _get_deserialize_callable_from_annotation(dt, module, rf)
866
+ for dt in annotation.__args__ # pyright: ignore
867
+ ]
868
+ return functools.partial(_deserialize_multiple_sequence, entry_deserializers, module)
869
+ deserializer = _get_deserialize_callable_from_annotation(
870
+ annotation.__args__[0], module, rf # pyright: ignore
871
+ )
872
+
873
+ return functools.partial(_deserialize_sequence, deserializer, module)
874
+ except (TypeError, IndexError, AttributeError, SyntaxError):
875
+ pass
876
+
877
+ def _deserialize_default(
878
+ deserializer,
879
+ obj,
880
+ ):
881
+ if obj is None:
882
+ return obj
883
+ try:
884
+ return _deserialize_with_callable(deserializer, obj)
885
+ except Exception:
886
+ pass
887
+ return obj
888
+
889
+ if get_deserializer(annotation, rf):
890
+ return functools.partial(_deserialize_default, get_deserializer(annotation, rf))
891
+
892
+ return functools.partial(_deserialize_default, annotation)
893
+
894
+
895
+ def _deserialize_with_callable(
896
+ deserializer: typing.Optional[typing.Callable[[typing.Any], typing.Any]],
897
+ value: typing.Any,
898
+ ): # pylint: disable=too-many-return-statements
899
+ try:
900
+ if value is None or isinstance(value, _Null):
901
+ return None
902
+ if isinstance(value, ET.Element):
903
+ if deserializer is str:
904
+ return value.text or ""
905
+ if deserializer is int:
906
+ return int(value.text) if value.text else None
907
+ if deserializer is float:
908
+ return float(value.text) if value.text else None
909
+ if deserializer is bool:
910
+ return value.text == "true" if value.text else None
911
+ if deserializer is None:
912
+ return value
913
+ if deserializer in [int, float, bool]:
914
+ return deserializer(value)
915
+ if isinstance(deserializer, CaseInsensitiveEnumMeta):
916
+ try:
917
+ return deserializer(value)
918
+ except ValueError:
919
+ # for unknown value, return raw value
920
+ return value
921
+ if isinstance(deserializer, type) and issubclass(deserializer, Model):
922
+ return deserializer._deserialize(value, [])
923
+ return typing.cast(typing.Callable[[typing.Any], typing.Any], deserializer)(value)
924
+ except Exception as e:
925
+ raise DeserializationError() from e
926
+
927
+
928
+ def _deserialize(
929
+ deserializer: typing.Any,
930
+ value: typing.Any,
931
+ module: typing.Optional[str] = None,
932
+ rf: typing.Optional["_RestField"] = None,
933
+ format: typing.Optional[str] = None,
934
+ ) -> typing.Any:
935
+ if isinstance(value, PipelineResponse):
936
+ value = value.http_response.json()
937
+ if rf is None and format:
938
+ rf = _RestField(format=format)
939
+ if not isinstance(deserializer, functools.partial):
940
+ deserializer = _get_deserialize_callable_from_annotation(deserializer, module, rf)
941
+ return _deserialize_with_callable(deserializer, value)
942
+
943
+
944
+ def _failsafe_deserialize(
945
+ deserializer: typing.Any,
946
+ value: typing.Any,
947
+ module: typing.Optional[str] = None,
948
+ rf: typing.Optional["_RestField"] = None,
949
+ format: typing.Optional[str] = None,
950
+ ) -> typing.Any:
951
+ try:
952
+ return _deserialize(deserializer, value, module, rf, format)
953
+ except DeserializationError:
954
+ _LOGGER.warning(
955
+ "Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True
956
+ )
957
+ return None
958
+
959
+
960
+ def _failsafe_deserialize_xml(
961
+ deserializer: typing.Any,
962
+ value: typing.Any,
963
+ ) -> typing.Any:
964
+ try:
965
+ return _deserialize_xml(deserializer, value)
966
+ except DeserializationError:
967
+ _LOGGER.warning(
968
+ "Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True
969
+ )
970
+ return None
971
+
972
+
973
+ class _RestField:
974
+ def __init__(
975
+ self,
976
+ *,
977
+ name: typing.Optional[str] = None,
978
+ type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin
979
+ is_discriminator: bool = False,
980
+ visibility: typing.Optional[typing.List[str]] = None,
981
+ default: typing.Any = _UNSET,
982
+ format: typing.Optional[str] = None,
983
+ is_multipart_file_input: bool = False,
984
+ xml: typing.Optional[typing.Dict[str, typing.Any]] = None,
985
+ ):
986
+ self._type = type
987
+ self._rest_name_input = name
988
+ self._module: typing.Optional[str] = None
989
+ self._is_discriminator = is_discriminator
990
+ self._visibility = visibility
991
+ self._is_model = False
992
+ self._default = default
993
+ self._format = format
994
+ self._is_multipart_file_input = is_multipart_file_input
995
+ self._xml = xml if xml is not None else {}
996
+
997
+ @property
998
+ def _class_type(self) -> typing.Any:
999
+ return getattr(self._type, "args", [None])[0]
1000
+
1001
+ @property
1002
+ def _rest_name(self) -> str:
1003
+ if self._rest_name_input is None:
1004
+ raise ValueError("Rest name was never set")
1005
+ return self._rest_name_input
1006
+
1007
+ def __get__(self, obj: Model, type=None): # pylint: disable=redefined-builtin
1008
+ # by this point, type and rest_name will have a value bc we default
1009
+ # them in __new__ of the Model class
1010
+ item = obj.get(self._rest_name)
1011
+ if item is None:
1012
+ return item
1013
+ if self._is_model:
1014
+ return item
1015
+ return _deserialize(self._type, _serialize(item, self._format), rf=self)
1016
+
1017
+ def __set__(self, obj: Model, value) -> None:
1018
+ if value is None:
1019
+ # we want to wipe out entries if users set attr to None
1020
+ try:
1021
+ obj.__delitem__(self._rest_name)
1022
+ except KeyError:
1023
+ pass
1024
+ return
1025
+ if self._is_model:
1026
+ if not _is_model(value):
1027
+ value = _deserialize(self._type, value)
1028
+ obj.__setitem__(self._rest_name, value)
1029
+ return
1030
+ obj.__setitem__(self._rest_name, _serialize(value, self._format))
1031
+
1032
+ def _get_deserialize_callable_from_annotation(
1033
+ self, annotation: typing.Any
1034
+ ) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
1035
+ return _get_deserialize_callable_from_annotation(annotation, self._module, self)
1036
+
1037
+
1038
+ def rest_field(
1039
+ *,
1040
+ name: typing.Optional[str] = None,
1041
+ type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin
1042
+ visibility: typing.Optional[typing.List[str]] = None,
1043
+ default: typing.Any = _UNSET,
1044
+ format: typing.Optional[str] = None,
1045
+ is_multipart_file_input: bool = False,
1046
+ xml: typing.Optional[typing.Dict[str, typing.Any]] = None,
1047
+ ) -> typing.Any:
1048
+ return _RestField(
1049
+ name=name,
1050
+ type=type,
1051
+ visibility=visibility,
1052
+ default=default,
1053
+ format=format,
1054
+ is_multipart_file_input=is_multipart_file_input,
1055
+ xml=xml,
1056
+ )
1057
+
1058
+
1059
+ def rest_discriminator(
1060
+ *,
1061
+ name: typing.Optional[str] = None,
1062
+ type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin
1063
+ visibility: typing.Optional[typing.List[str]] = None,
1064
+ xml: typing.Optional[typing.Dict[str, typing.Any]] = None,
1065
+ ) -> typing.Any:
1066
+ return _RestField(name=name, type=type, is_discriminator=True, visibility=visibility, xml=xml)
1067
+
1068
+
1069
+ def serialize_xml(model: Model, exclude_readonly: bool = False) -> str:
1070
+ """Serialize a model to XML.
1071
+
1072
+ :param Model model: The model to serialize.
1073
+ :param bool exclude_readonly: Whether to exclude readonly properties.
1074
+ :returns: The XML representation of the model.
1075
+ :rtype: str
1076
+ """
1077
+ return ET.tostring(_get_element(model, exclude_readonly), encoding="unicode") # type: ignore
1078
+
1079
+
1080
+ def _get_element(
1081
+ o: typing.Any,
1082
+ exclude_readonly: bool = False,
1083
+ parent_meta: typing.Optional[typing.Dict[str, typing.Any]] = None,
1084
+ wrapped_element: typing.Optional[ET.Element] = None,
1085
+ ) -> typing.Union[ET.Element, typing.List[ET.Element]]:
1086
+ if _is_model(o):
1087
+ model_meta = getattr(o, "_xml", {})
1088
+
1089
+ # if prop is a model, then use the prop element directly, else generate a wrapper of model
1090
+ if wrapped_element is None:
1091
+ wrapped_element = _create_xml_element(
1092
+ model_meta.get("name", o.__class__.__name__),
1093
+ model_meta.get("prefix"),
1094
+ model_meta.get("ns"),
1095
+ )
1096
+
1097
+ readonly_props = []
1098
+ if exclude_readonly:
1099
+ readonly_props = [p._rest_name for p in o._attr_to_rest_field.values() if _is_readonly(p)]
1100
+
1101
+ for k, v in o.items():
1102
+ # do not serialize readonly properties
1103
+ if exclude_readonly and k in readonly_props:
1104
+ continue
1105
+
1106
+ prop_rest_field = _get_rest_field(o._attr_to_rest_field, k)
1107
+ if prop_rest_field:
1108
+ prop_meta = getattr(prop_rest_field, "_xml").copy()
1109
+ # use the wire name as xml name if no specific name is set
1110
+ if prop_meta.get("name") is None:
1111
+ prop_meta["name"] = k
1112
+ else:
1113
+ # additional properties will not have rest field, use the wire name as xml name
1114
+ prop_meta = {"name": k}
1115
+
1116
+ # if no ns for prop, use model's
1117
+ if prop_meta.get("ns") is None and model_meta.get("ns"):
1118
+ prop_meta["ns"] = model_meta.get("ns")
1119
+ prop_meta["prefix"] = model_meta.get("prefix")
1120
+
1121
+ if prop_meta.get("unwrapped", False):
1122
+ # unwrapped could only set on array
1123
+ wrapped_element.extend(_get_element(v, exclude_readonly, prop_meta))
1124
+ elif prop_meta.get("text", False):
1125
+ # text could only set on primitive type
1126
+ wrapped_element.text = _get_primitive_type_value(v)
1127
+ elif prop_meta.get("attribute", False):
1128
+ xml_name = prop_meta.get("name", k)
1129
+ if prop_meta.get("ns"):
1130
+ ET.register_namespace(prop_meta.get("prefix"), prop_meta.get("ns")) # pyright: ignore
1131
+ xml_name = "{" + prop_meta.get("ns") + "}" + xml_name # pyright: ignore
1132
+ # attribute should be primitive type
1133
+ wrapped_element.set(xml_name, _get_primitive_type_value(v))
1134
+ else:
1135
+ # other wrapped prop element
1136
+ wrapped_element.append(_get_wrapped_element(v, exclude_readonly, prop_meta))
1137
+ return wrapped_element
1138
+ if isinstance(o, list):
1139
+ return [_get_element(x, exclude_readonly, parent_meta) for x in o] # type: ignore
1140
+ if isinstance(o, dict):
1141
+ result = []
1142
+ for k, v in o.items():
1143
+ result.append(
1144
+ _get_wrapped_element(
1145
+ v,
1146
+ exclude_readonly,
1147
+ {
1148
+ "name": k,
1149
+ "ns": parent_meta.get("ns") if parent_meta else None,
1150
+ "prefix": parent_meta.get("prefix") if parent_meta else None,
1151
+ },
1152
+ )
1153
+ )
1154
+ return result
1155
+
1156
+ # primitive case need to create element based on parent_meta
1157
+ if parent_meta:
1158
+ return _get_wrapped_element(
1159
+ o,
1160
+ exclude_readonly,
1161
+ {
1162
+ "name": parent_meta.get("itemsName", parent_meta.get("name")),
1163
+ "prefix": parent_meta.get("itemsPrefix", parent_meta.get("prefix")),
1164
+ "ns": parent_meta.get("itemsNs", parent_meta.get("ns")),
1165
+ },
1166
+ )
1167
+
1168
+ raise ValueError("Could not serialize value into xml: " + o)
1169
+
1170
+
1171
+ def _get_wrapped_element(
1172
+ v: typing.Any,
1173
+ exclude_readonly: bool,
1174
+ meta: typing.Optional[typing.Dict[str, typing.Any]],
1175
+ ) -> ET.Element:
1176
+ wrapped_element = _create_xml_element(
1177
+ meta.get("name") if meta else None, meta.get("prefix") if meta else None, meta.get("ns") if meta else None
1178
+ )
1179
+ if isinstance(v, (dict, list)):
1180
+ wrapped_element.extend(_get_element(v, exclude_readonly, meta))
1181
+ elif _is_model(v):
1182
+ _get_element(v, exclude_readonly, meta, wrapped_element)
1183
+ else:
1184
+ wrapped_element.text = _get_primitive_type_value(v)
1185
+ return wrapped_element
1186
+
1187
+
1188
+ def _get_primitive_type_value(v) -> str:
1189
+ if v is True:
1190
+ return "true"
1191
+ if v is False:
1192
+ return "false"
1193
+ if isinstance(v, _Null):
1194
+ return ""
1195
+ return str(v)
1196
+
1197
+
1198
+ def _create_xml_element(tag, prefix=None, ns=None):
1199
+ if prefix and ns:
1200
+ ET.register_namespace(prefix, ns)
1201
+ if ns:
1202
+ return ET.Element("{" + ns + "}" + tag)
1203
+ return ET.Element(tag)
1204
+
1205
+
1206
+ def _deserialize_xml(
1207
+ deserializer: typing.Any,
1208
+ value: str,
1209
+ ) -> typing.Any:
1210
+ element = ET.fromstring(value) # nosec
1211
+ return _deserialize(deserializer, element)
1212
+
1213
+
1214
+ def _convert_element(e: ET.Element):
1215
+ # dict case
1216
+ if len(e.attrib) > 0 or len({child.tag for child in e}) > 1:
1217
+ dict_result: typing.Dict[str, typing.Any] = {}
1218
+ for child in e:
1219
+ if dict_result.get(child.tag) is not None:
1220
+ if isinstance(dict_result[child.tag], list):
1221
+ dict_result[child.tag].append(_convert_element(child))
1222
+ else:
1223
+ dict_result[child.tag] = [dict_result[child.tag], _convert_element(child)]
1224
+ else:
1225
+ dict_result[child.tag] = _convert_element(child)
1226
+ dict_result.update(e.attrib)
1227
+ return dict_result
1228
+ # array case
1229
+ if len(e) > 0:
1230
+ array_result: typing.List[typing.Any] = []
1231
+ for child in e:
1232
+ array_result.append(_convert_element(child))
1233
+ return array_result
1234
+ # primitive case
1235
+ return e.text