@typespec/http-client-python 0.1.0

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.
Files changed (334) hide show
  1. package/README.md +118 -0
  2. package/dist/emitter/code-model.d.ts +4 -0
  3. package/dist/emitter/code-model.d.ts.map +1 -0
  4. package/dist/emitter/code-model.js +195 -0
  5. package/dist/emitter/code-model.js.map +1 -0
  6. package/dist/emitter/emitter.d.ts +6 -0
  7. package/dist/emitter/emitter.d.ts.map +1 -0
  8. package/dist/emitter/emitter.js +104 -0
  9. package/dist/emitter/emitter.js.map +1 -0
  10. package/dist/emitter/external-process.d.ts +20 -0
  11. package/dist/emitter/external-process.d.ts.map +1 -0
  12. package/dist/emitter/external-process.js +44 -0
  13. package/dist/emitter/external-process.js.map +1 -0
  14. package/dist/emitter/http.d.ts +7 -0
  15. package/dist/emitter/http.d.ts.map +1 -0
  16. package/dist/emitter/http.js +268 -0
  17. package/dist/emitter/http.js.map +1 -0
  18. package/dist/emitter/index.d.ts +3 -0
  19. package/dist/emitter/index.d.ts.map +1 -0
  20. package/dist/emitter/index.js +3 -0
  21. package/dist/emitter/index.js.map +1 -0
  22. package/dist/emitter/lib.d.ts +30 -0
  23. package/dist/emitter/lib.d.ts.map +1 -0
  24. package/dist/emitter/lib.js +33 -0
  25. package/dist/emitter/lib.js.map +1 -0
  26. package/dist/emitter/types.d.ts +36 -0
  27. package/dist/emitter/types.d.ts.map +1 -0
  28. package/dist/emitter/types.js +491 -0
  29. package/dist/emitter/types.js.map +1 -0
  30. package/dist/emitter/utils.d.ts +26 -0
  31. package/dist/emitter/utils.d.ts.map +1 -0
  32. package/dist/emitter/utils.js +155 -0
  33. package/dist/emitter/utils.js.map +1 -0
  34. package/emitter/src/code-model.ts +272 -0
  35. package/emitter/src/emitter.ts +127 -0
  36. package/emitter/src/external-process.ts +52 -0
  37. package/emitter/src/http.ts +382 -0
  38. package/emitter/src/index.ts +2 -0
  39. package/emitter/src/lib.ts +59 -0
  40. package/emitter/src/types.ts +573 -0
  41. package/emitter/src/utils.ts +215 -0
  42. package/emitter/temp/tsconfig.tsbuildinfo +1 -0
  43. package/emitter/test/utils.test.ts +22 -0
  44. package/emitter/tsconfig.build.json +11 -0
  45. package/emitter/tsconfig.json +7 -0
  46. package/emitter/vitest.config.ts +4 -0
  47. package/eng/scripts/Build-Packages.ps1 +86 -0
  48. package/eng/scripts/Check-GitChanges.ps1 +22 -0
  49. package/eng/scripts/Functions.ps1 +26 -0
  50. package/eng/scripts/Generate.ps1 +11 -0
  51. package/eng/scripts/Generation.psm1 +22 -0
  52. package/eng/scripts/Initialize-Repository.ps1 +40 -0
  53. package/eng/scripts/Test-Packages.ps1 +65 -0
  54. package/eng/scripts/ci/format.ts +3 -0
  55. package/eng/scripts/ci/lint.ts +39 -0
  56. package/eng/scripts/ci/mypy.ini +38 -0
  57. package/eng/scripts/ci/pylintrc +59 -0
  58. package/eng/scripts/ci/pyproject.toml +18 -0
  59. package/eng/scripts/ci/pyrightconfig.json +6 -0
  60. package/eng/scripts/ci/regenerate.ts +299 -0
  61. package/eng/scripts/ci/run-ci.ts +88 -0
  62. package/eng/scripts/ci/run_apiview.py +40 -0
  63. package/eng/scripts/ci/run_mypy.py +49 -0
  64. package/eng/scripts/ci/run_pylint.py +50 -0
  65. package/eng/scripts/ci/run_pyright.py +58 -0
  66. package/eng/scripts/ci/util.py +72 -0
  67. package/eng/scripts/ci/utils.ts +48 -0
  68. package/eng/scripts/setup/__pycache__/venvtools.cpython-38.pyc +0 -0
  69. package/eng/scripts/setup/install.py +53 -0
  70. package/eng/scripts/setup/prepare.py +42 -0
  71. package/eng/scripts/setup/run-python3.ts +25 -0
  72. package/eng/scripts/setup/run_tsp.py +42 -0
  73. package/eng/scripts/setup/system-requirements.ts +261 -0
  74. package/eng/scripts/setup/venvtools.py +87 -0
  75. package/generator/LICENSE +21 -0
  76. package/generator/README.md +1 -0
  77. package/generator/dev_requirements.txt +13 -0
  78. package/generator/pygen/__init__.py +107 -0
  79. package/generator/pygen/_version.py +7 -0
  80. package/generator/pygen/black.py +71 -0
  81. package/generator/pygen/codegen/__init__.py +338 -0
  82. package/generator/pygen/codegen/_utils.py +17 -0
  83. package/generator/pygen/codegen/models/__init__.py +204 -0
  84. package/generator/pygen/codegen/models/base.py +186 -0
  85. package/generator/pygen/codegen/models/base_builder.py +118 -0
  86. package/generator/pygen/codegen/models/client.py +433 -0
  87. package/generator/pygen/codegen/models/code_model.py +237 -0
  88. package/generator/pygen/codegen/models/combined_type.py +149 -0
  89. package/generator/pygen/codegen/models/constant_type.py +129 -0
  90. package/generator/pygen/codegen/models/credential_types.py +214 -0
  91. package/generator/pygen/codegen/models/dictionary_type.py +127 -0
  92. package/generator/pygen/codegen/models/enum_type.py +238 -0
  93. package/generator/pygen/codegen/models/imports.py +291 -0
  94. package/generator/pygen/codegen/models/list_type.py +143 -0
  95. package/generator/pygen/codegen/models/lro_operation.py +142 -0
  96. package/generator/pygen/codegen/models/lro_paging_operation.py +32 -0
  97. package/generator/pygen/codegen/models/model_type.py +359 -0
  98. package/generator/pygen/codegen/models/operation.py +530 -0
  99. package/generator/pygen/codegen/models/operation_group.py +184 -0
  100. package/generator/pygen/codegen/models/paging_operation.py +155 -0
  101. package/generator/pygen/codegen/models/parameter.py +412 -0
  102. package/generator/pygen/codegen/models/parameter_list.py +387 -0
  103. package/generator/pygen/codegen/models/primitive_types.py +659 -0
  104. package/generator/pygen/codegen/models/property.py +170 -0
  105. package/generator/pygen/codegen/models/request_builder.py +189 -0
  106. package/generator/pygen/codegen/models/request_builder_parameter.py +115 -0
  107. package/generator/pygen/codegen/models/response.py +348 -0
  108. package/generator/pygen/codegen/models/utils.py +21 -0
  109. package/generator/pygen/codegen/serializers/__init__.py +574 -0
  110. package/generator/pygen/codegen/serializers/base_serializer.py +21 -0
  111. package/generator/pygen/codegen/serializers/builder_serializer.py +1507 -0
  112. package/generator/pygen/codegen/serializers/client_serializer.py +294 -0
  113. package/generator/pygen/codegen/serializers/enum_serializer.py +15 -0
  114. package/generator/pygen/codegen/serializers/general_serializer.py +213 -0
  115. package/generator/pygen/codegen/serializers/import_serializer.py +126 -0
  116. package/generator/pygen/codegen/serializers/metadata_serializer.py +198 -0
  117. package/generator/pygen/codegen/serializers/model_init_serializer.py +33 -0
  118. package/generator/pygen/codegen/serializers/model_serializer.py +317 -0
  119. package/generator/pygen/codegen/serializers/operation_groups_serializer.py +89 -0
  120. package/generator/pygen/codegen/serializers/operations_init_serializer.py +44 -0
  121. package/generator/pygen/codegen/serializers/parameter_serializer.py +221 -0
  122. package/generator/pygen/codegen/serializers/patch_serializer.py +19 -0
  123. package/generator/pygen/codegen/serializers/request_builders_serializer.py +52 -0
  124. package/generator/pygen/codegen/serializers/sample_serializer.py +168 -0
  125. package/generator/pygen/codegen/serializers/test_serializer.py +292 -0
  126. package/generator/pygen/codegen/serializers/types_serializer.py +31 -0
  127. package/generator/pygen/codegen/serializers/utils.py +68 -0
  128. package/generator/pygen/codegen/templates/client.py.jinja2 +37 -0
  129. package/generator/pygen/codegen/templates/client_container.py.jinja2 +12 -0
  130. package/generator/pygen/codegen/templates/config.py.jinja2 +73 -0
  131. package/generator/pygen/codegen/templates/config_container.py.jinja2 +16 -0
  132. package/generator/pygen/codegen/templates/conftest.py.jinja2 +28 -0
  133. package/generator/pygen/codegen/templates/enum.py.jinja2 +13 -0
  134. package/generator/pygen/codegen/templates/enum_container.py.jinja2 +10 -0
  135. package/generator/pygen/codegen/templates/init.py.jinja2 +24 -0
  136. package/generator/pygen/codegen/templates/keywords.jinja2 +19 -0
  137. package/generator/pygen/codegen/templates/lro_operation.py.jinja2 +16 -0
  138. package/generator/pygen/codegen/templates/lro_paging_operation.py.jinja2 +18 -0
  139. package/generator/pygen/codegen/templates/macros.jinja2 +12 -0
  140. package/generator/pygen/codegen/templates/metadata.json.jinja2 +167 -0
  141. package/generator/pygen/codegen/templates/model_base.py.jinja2 +1157 -0
  142. package/generator/pygen/codegen/templates/model_container.py.jinja2 +12 -0
  143. package/generator/pygen/codegen/templates/model_dpg.py.jinja2 +97 -0
  144. package/generator/pygen/codegen/templates/model_init.py.jinja2 +28 -0
  145. package/generator/pygen/codegen/templates/model_msrest.py.jinja2 +92 -0
  146. package/generator/pygen/codegen/templates/operation.py.jinja2 +21 -0
  147. package/generator/pygen/codegen/templates/operation_group.py.jinja2 +75 -0
  148. package/generator/pygen/codegen/templates/operation_groups_container.py.jinja2 +19 -0
  149. package/generator/pygen/codegen/templates/operation_tools.jinja2 +81 -0
  150. package/generator/pygen/codegen/templates/operations_folder_init.py.jinja2 +17 -0
  151. package/generator/pygen/codegen/templates/packaging_templates/CHANGELOG.md.jinja2 +6 -0
  152. package/generator/pygen/codegen/templates/packaging_templates/LICENSE.jinja2 +21 -0
  153. package/generator/pygen/codegen/templates/packaging_templates/MANIFEST.in.jinja2 +8 -0
  154. package/generator/pygen/codegen/templates/packaging_templates/README.md.jinja2 +107 -0
  155. package/generator/pygen/codegen/templates/packaging_templates/dev_requirements.txt.jinja2 +9 -0
  156. package/generator/pygen/codegen/templates/packaging_templates/setup.py.jinja2 +108 -0
  157. package/generator/pygen/codegen/templates/paging_operation.py.jinja2 +21 -0
  158. package/generator/pygen/codegen/templates/patch.py.jinja2 +19 -0
  159. package/generator/pygen/codegen/templates/pkgutil_init.py.jinja2 +1 -0
  160. package/generator/pygen/codegen/templates/request_builder.py.jinja2 +28 -0
  161. package/generator/pygen/codegen/templates/request_builders.py.jinja2 +10 -0
  162. package/generator/pygen/codegen/templates/rest_init.py.jinja2 +12 -0
  163. package/generator/pygen/codegen/templates/sample.py.jinja2 +44 -0
  164. package/generator/pygen/codegen/templates/serialization.py.jinja2 +2114 -0
  165. package/generator/pygen/codegen/templates/test.py.jinja2 +50 -0
  166. package/generator/pygen/codegen/templates/testpreparer.py.jinja2 +26 -0
  167. package/generator/pygen/codegen/templates/types.py.jinja2 +7 -0
  168. package/generator/pygen/codegen/templates/validation.py.jinja2 +38 -0
  169. package/generator/pygen/codegen/templates/vendor.py.jinja2 +95 -0
  170. package/generator/pygen/codegen/templates/version.py.jinja2 +4 -0
  171. package/generator/pygen/m2r.py +65 -0
  172. package/generator/pygen/postprocess/__init__.py +183 -0
  173. package/generator/pygen/postprocess/get_all.py +19 -0
  174. package/generator/pygen/postprocess/venvtools.py +75 -0
  175. package/generator/pygen/preprocess/__init__.py +515 -0
  176. package/generator/pygen/preprocess/helpers.py +27 -0
  177. package/generator/pygen/preprocess/python_mappings.py +224 -0
  178. package/generator/pygen/utils.py +163 -0
  179. package/generator/pygen.egg-info/PKG-INFO +25 -0
  180. package/generator/pygen.egg-info/SOURCES.txt +66 -0
  181. package/generator/pygen.egg-info/dependency_links.txt +1 -0
  182. package/generator/pygen.egg-info/requires.txt +4 -0
  183. package/generator/pygen.egg-info/top_level.txt +1 -0
  184. package/generator/requirements.txt +12 -0
  185. package/generator/setup.py +55 -0
  186. package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_models_common_types_managed_identity_async.py +63 -0
  187. package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_models_resource_async.py +284 -0
  188. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_access_async.py +101 -0
  189. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_flatten_async.py +93 -0
  190. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_usage_async.py +31 -0
  191. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_basic_async.py +76 -0
  192. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_lro_rpc_async.py +22 -0
  193. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_lro_standard_async.py +39 -0
  194. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_model_async.py +33 -0
  195. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_page_async.py +58 -0
  196. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_scalar_async.py +41 -0
  197. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_traits_async.py +87 -0
  198. package/generator/test/azure/mock_api_tests/asynctests/test_azure_example_basic_async.py +30 -0
  199. package/generator/test/azure/mock_api_tests/asynctests/test_azure_special_headers_client_request_id_async.py +30 -0
  200. package/generator/test/azure/mock_api_tests/conftest.py +150 -0
  201. package/generator/test/azure/mock_api_tests/test_azure_arm_models_common_types_managed_identity.py +60 -0
  202. package/generator/test/azure/mock_api_tests/test_azure_arm_models_resource.py +254 -0
  203. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_access.py +92 -0
  204. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_flatten.py +84 -0
  205. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_usage.py +28 -0
  206. package/generator/test/azure/mock_api_tests/test_azure_core_basic.py +70 -0
  207. package/generator/test/azure/mock_api_tests/test_azure_core_lro_rpc.py +20 -0
  208. package/generator/test/azure/mock_api_tests/test_azure_core_lro_standard.py +32 -0
  209. package/generator/test/azure/mock_api_tests/test_azure_core_model.py +30 -0
  210. package/generator/test/azure/mock_api_tests/test_azure_core_page.py +51 -0
  211. package/generator/test/azure/mock_api_tests/test_azure_core_scalar.py +35 -0
  212. package/generator/test/azure/mock_api_tests/test_azure_core_traits.py +85 -0
  213. package/generator/test/azure/mock_api_tests/test_azure_example_basic.py +29 -0
  214. package/generator/test/azure/mock_api_tests/test_azure_special_headers_client_request_id.py +29 -0
  215. package/generator/test/azure/requirements.txt +89 -0
  216. package/generator/test/azure/tox.ini +56 -0
  217. package/generator/test/generic_mock_api_tests/asynctests/test_authentication_async.py +121 -0
  218. package/generator/test/generic_mock_api_tests/asynctests/test_client_naming_async.py +69 -0
  219. package/generator/test/generic_mock_api_tests/asynctests/test_client_structure_async.py +62 -0
  220. package/generator/test/generic_mock_api_tests/asynctests/test_encode_bytes_async.py +133 -0
  221. package/generator/test/generic_mock_api_tests/asynctests/test_encode_datetime_async.py +127 -0
  222. package/generator/test/generic_mock_api_tests/asynctests/test_encode_duration_async.py +63 -0
  223. package/generator/test/generic_mock_api_tests/asynctests/test_encode_numeric_async.py +35 -0
  224. package/generator/test/generic_mock_api_tests/asynctests/test_headasboolean_async.py +35 -0
  225. package/generator/test/generic_mock_api_tests/asynctests/test_parameters_basic_async.py +24 -0
  226. package/generator/test/generic_mock_api_tests/asynctests/test_parameters_body_optionality_async.py +30 -0
  227. package/generator/test/generic_mock_api_tests/asynctests/test_parameters_collection_format_async.py +44 -0
  228. package/generator/test/generic_mock_api_tests/asynctests/test_parameters_spread_async.py +76 -0
  229. package/generator/test/generic_mock_api_tests/asynctests/test_payload_content_negotiation_async.py +37 -0
  230. package/generator/test/generic_mock_api_tests/asynctests/test_payload_json_merge_patch_async.py +98 -0
  231. package/generator/test/generic_mock_api_tests/asynctests/test_payload_media_type_async.py +27 -0
  232. package/generator/test/generic_mock_api_tests/asynctests/test_payload_multipart_async.py +153 -0
  233. package/generator/test/generic_mock_api_tests/asynctests/test_payload_pageable_async.py +19 -0
  234. package/generator/test/generic_mock_api_tests/asynctests/test_payload_xml_async.py +103 -0
  235. package/generator/test/generic_mock_api_tests/asynctests/test_resiliency_srv_driven_async.py +128 -0
  236. package/generator/test/generic_mock_api_tests/asynctests/test_routes_async.py +331 -0
  237. package/generator/test/generic_mock_api_tests/asynctests/test_serialization_encoded_name_json_async.py +24 -0
  238. package/generator/test/generic_mock_api_tests/asynctests/test_server_endpoint_not_defined_async.py +18 -0
  239. package/generator/test/generic_mock_api_tests/asynctests/test_server_path_multiple_async.py +25 -0
  240. package/generator/test/generic_mock_api_tests/asynctests/test_server_path_single_async.py +18 -0
  241. package/generator/test/generic_mock_api_tests/asynctests/test_server_versions_not_versioned_async.py +28 -0
  242. package/generator/test/generic_mock_api_tests/asynctests/test_server_versions_versioned_async.py +34 -0
  243. package/generator/test/generic_mock_api_tests/asynctests/test_special_headers_conditional_request_async.py +38 -0
  244. package/generator/test/generic_mock_api_tests/asynctests/test_special_headers_repeatability_async.py +19 -0
  245. package/generator/test/generic_mock_api_tests/asynctests/test_special_words_async.py +42 -0
  246. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_array_async.py +118 -0
  247. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_dictionary_async.py +98 -0
  248. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_enum_extensible_async.py +25 -0
  249. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_enum_fixed_async.py +27 -0
  250. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_empty_async.py +32 -0
  251. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_enum_discriminator_async.py +70 -0
  252. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_nested_discriminator_async.py +85 -0
  253. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_not_discriminated_async.py +34 -0
  254. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_recursive_async.py +34 -0
  255. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_single_discriminator_async.py +67 -0
  256. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_usage_async.py +32 -0
  257. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_visibility_async.py +47 -0
  258. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_additionalproperties_async.py +352 -0
  259. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_nullable_async.py +110 -0
  260. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_optional_async.py +197 -0
  261. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_valuetypes_async.py +315 -0
  262. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_scalar_async.py +60 -0
  263. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_union_async.py +90 -0
  264. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_added_async.py +36 -0
  265. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_made_optional_async.py +21 -0
  266. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_removed_async.py +21 -0
  267. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_renamed_from_async.py +29 -0
  268. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_return_type_changed_from_async.py +18 -0
  269. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_type_changed_from_async.py +22 -0
  270. package/generator/test/generic_mock_api_tests/conftest.py +113 -0
  271. package/generator/test/generic_mock_api_tests/data/image.jpg +0 -0
  272. package/generator/test/generic_mock_api_tests/data/image.png +0 -0
  273. package/generator/test/generic_mock_api_tests/test_authentication.py +113 -0
  274. package/generator/test/generic_mock_api_tests/test_client_naming.py +57 -0
  275. package/generator/test/generic_mock_api_tests/test_client_structure.py +57 -0
  276. package/generator/test/generic_mock_api_tests/test_encode_bytes.py +128 -0
  277. package/generator/test/generic_mock_api_tests/test_encode_datetime.py +123 -0
  278. package/generator/test/generic_mock_api_tests/test_encode_duration.py +60 -0
  279. package/generator/test/generic_mock_api_tests/test_encode_numeric.py +31 -0
  280. package/generator/test/generic_mock_api_tests/test_headasboolean.py +33 -0
  281. package/generator/test/generic_mock_api_tests/test_parameters_basic.py +22 -0
  282. package/generator/test/generic_mock_api_tests/test_parameters_body_optionality.py +27 -0
  283. package/generator/test/generic_mock_api_tests/test_parameters_collection_format.py +37 -0
  284. package/generator/test/generic_mock_api_tests/test_parameters_spread.py +66 -0
  285. package/generator/test/generic_mock_api_tests/test_payload_content_negotiation.py +33 -0
  286. package/generator/test/generic_mock_api_tests/test_payload_json_merge_patch.py +93 -0
  287. package/generator/test/generic_mock_api_tests/test_payload_media_type.py +25 -0
  288. package/generator/test/generic_mock_api_tests/test_payload_multipart.py +140 -0
  289. package/generator/test/generic_mock_api_tests/test_payload_pageable.py +18 -0
  290. package/generator/test/generic_mock_api_tests/test_payload_xml.py +93 -0
  291. package/generator/test/generic_mock_api_tests/test_resiliency_srv_driven.py +122 -0
  292. package/generator/test/generic_mock_api_tests/test_routes.py +285 -0
  293. package/generator/test/generic_mock_api_tests/test_serialization_encoded_name_json.py +21 -0
  294. package/generator/test/generic_mock_api_tests/test_server_endpoint_not_defined.py +17 -0
  295. package/generator/test/generic_mock_api_tests/test_server_path_multiple.py +21 -0
  296. package/generator/test/generic_mock_api_tests/test_server_path_single.py +17 -0
  297. package/generator/test/generic_mock_api_tests/test_server_versions_not_versioned.py +25 -0
  298. package/generator/test/generic_mock_api_tests/test_server_versions_versioned.py +30 -0
  299. package/generator/test/generic_mock_api_tests/test_special_headers_conditional_request.py +34 -0
  300. package/generator/test/generic_mock_api_tests/test_special_headers_repeatability.py +18 -0
  301. package/generator/test/generic_mock_api_tests/test_special_words.py +37 -0
  302. package/generator/test/generic_mock_api_tests/test_typetest_array.py +103 -0
  303. package/generator/test/generic_mock_api_tests/test_typetest_dictionary.py +86 -0
  304. package/generator/test/generic_mock_api_tests/test_typetest_enum_extensible.py +23 -0
  305. package/generator/test/generic_mock_api_tests/test_typetest_enum_fixed.py +25 -0
  306. package/generator/test/generic_mock_api_tests/test_typetest_model_empty.py +29 -0
  307. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_enum_discriminator.py +58 -0
  308. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_nested_discriminator.py +79 -0
  309. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_not_discriminated.py +31 -0
  310. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_recursive.py +32 -0
  311. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_single_discriminator.py +60 -0
  312. package/generator/test/generic_mock_api_tests/test_typetest_model_usage.py +28 -0
  313. package/generator/test/generic_mock_api_tests/test_typetest_model_visibility.py +40 -0
  314. package/generator/test/generic_mock_api_tests/test_typetest_property_additionalproperties.py +313 -0
  315. package/generator/test/generic_mock_api_tests/test_typetest_property_nullable.py +102 -0
  316. package/generator/test/generic_mock_api_tests/test_typetest_property_optional.py +174 -0
  317. package/generator/test/generic_mock_api_tests/test_typetest_property_valuetypes.py +286 -0
  318. package/generator/test/generic_mock_api_tests/test_typetest_scalar.py +53 -0
  319. package/generator/test/generic_mock_api_tests/test_typetest_union.py +80 -0
  320. package/generator/test/generic_mock_api_tests/test_versioning_added.py +33 -0
  321. package/generator/test/generic_mock_api_tests/test_versioning_made_optional.py +20 -0
  322. package/generator/test/generic_mock_api_tests/test_versioning_removed.py +20 -0
  323. package/generator/test/generic_mock_api_tests/test_versioning_renamed_from.py +27 -0
  324. package/generator/test/generic_mock_api_tests/test_versioning_return_type_changed_from.py +17 -0
  325. package/generator/test/generic_mock_api_tests/test_versioning_type_changed_from.py +21 -0
  326. package/generator/test/generic_mock_api_tests/unittests/test_model_base_serialization.py +4067 -0
  327. package/generator/test/generic_mock_api_tests/unittests/test_model_base_xml_serialization.py +1001 -0
  328. package/generator/test/unbranded/mock_api_tests/asynctests/test_unbranded_async.py +24 -0
  329. package/generator/test/unbranded/mock_api_tests/cadl-ranch-config.yaml +27 -0
  330. package/generator/test/unbranded/mock_api_tests/conftest.py +35 -0
  331. package/generator/test/unbranded/mock_api_tests/test_unbranded.py +57 -0
  332. package/generator/test/unbranded/requirements.txt +72 -0
  333. package/generator/test/unbranded/tox.ini +56 -0
  334. package/package.json +93 -0
@@ -0,0 +1,4067 @@
1
+ # ------------------------------------
2
+ # Copyright (c) Microsoft Corporation.
3
+ # Licensed under the MIT License.
4
+ # ------------------------------------
5
+ import copy
6
+ import decimal
7
+ import json
8
+ import datetime
9
+ from pathlib import Path
10
+ from typing import (
11
+ Any,
12
+ Iterable,
13
+ List,
14
+ Literal,
15
+ Dict,
16
+ Mapping,
17
+ Sequence,
18
+ Set,
19
+ Tuple,
20
+ Optional,
21
+ overload,
22
+ Union,
23
+ )
24
+ import pytest
25
+ import isodate
26
+ import sys
27
+ from enum import Enum
28
+
29
+ from specialwords._model_base import (
30
+ SdkJSONEncoder,
31
+ Model,
32
+ rest_field,
33
+ _is_model,
34
+ rest_discriminator,
35
+ _deserialize,
36
+ )
37
+
38
+ if sys.version_info >= (3, 9):
39
+ from collections.abc import MutableMapping
40
+ else:
41
+ from typing import MutableMapping # type: ignore # pylint: disable=ungrouped-imports
42
+ JSON = MutableMapping[str, Any] # pylint: disable=unsubscriptable-object
43
+
44
+
45
+ class BasicResource(Model):
46
+ platform_update_domain_count: int = rest_field(
47
+ name="platformUpdateDomainCount"
48
+ ) # How many times the platform update domain has been counted
49
+ platform_fault_domain_count: int = rest_field(
50
+ name="platformFaultDomainCount"
51
+ ) # How many times the platform fault domain has been counted
52
+ virtual_machines: List[Any] = rest_field(name="virtualMachines") # List of virtual machines
53
+
54
+ @overload
55
+ def __init__(
56
+ self,
57
+ *,
58
+ platform_update_domain_count: int,
59
+ platform_fault_domain_count: int,
60
+ virtual_machines: List[Any],
61
+ ): ...
62
+
63
+ @overload
64
+ def __init__(self, mapping: Mapping[str, Any], /): ...
65
+
66
+ def __init__(self, *args, **kwargs):
67
+ super().__init__(*args, **kwargs)
68
+
69
+
70
+ class Pet(Model):
71
+ name: str = rest_field() # my name
72
+ species: str = rest_field() # my species
73
+
74
+ @overload
75
+ def __init__(self, *, name: str, species: str): ...
76
+
77
+ @overload
78
+ def __init__(self, mapping: Mapping[str, Any], /): ...
79
+
80
+ def __init__(self, *args, **kwargs):
81
+ super().__init__(*args, **kwargs)
82
+
83
+
84
+ def test_model_and_dict_equal():
85
+ dict_response = {
86
+ "platformUpdateDomainCount": 5,
87
+ "platformFaultDomainCount": 3,
88
+ "virtualMachines": [],
89
+ }
90
+ model = BasicResource(
91
+ platform_update_domain_count=5,
92
+ platform_fault_domain_count=3,
93
+ virtual_machines=[],
94
+ )
95
+
96
+ assert model == dict_response
97
+ assert (
98
+ model.platform_update_domain_count
99
+ == model["platformUpdateDomainCount"]
100
+ == dict_response["platformUpdateDomainCount"]
101
+ == 5
102
+ )
103
+ assert (
104
+ model.platform_fault_domain_count
105
+ == model["platformFaultDomainCount"]
106
+ == dict_response["platformFaultDomainCount"]
107
+ == 3
108
+ )
109
+ assert model.virtual_machines == model["virtualMachines"] == dict_response["virtualMachines"]
110
+
111
+
112
+ def test_json_roundtrip():
113
+ dict_response = {
114
+ "platformUpdateDomainCount": 5,
115
+ "platformFaultDomainCount": 3,
116
+ "virtualMachines": [],
117
+ }
118
+ model = BasicResource(
119
+ platform_update_domain_count=5,
120
+ platform_fault_domain_count=3,
121
+ virtual_machines=[],
122
+ )
123
+ with pytest.raises(TypeError):
124
+ json.dumps(model)
125
+ assert (
126
+ json.dumps(dict(model))
127
+ == '{"platformUpdateDomainCount": 5, "platformFaultDomainCount": 3, "virtualMachines": []}'
128
+ )
129
+ assert json.loads(json.dumps(dict(model))) == model == dict_response
130
+
131
+
132
+ def test_has_no_property():
133
+ dict_response = {
134
+ "platformUpdateDomainCount": 5,
135
+ "platformFaultDomainCount": 3,
136
+ "virtualMachines": [],
137
+ "noProp": "bonjour!",
138
+ }
139
+ model = BasicResource(dict_response)
140
+ assert (
141
+ model.platform_update_domain_count
142
+ == model["platformUpdateDomainCount"]
143
+ == dict_response["platformUpdateDomainCount"]
144
+ == 5
145
+ )
146
+ assert not hasattr(model, "no_prop")
147
+ with pytest.raises(AttributeError) as ex:
148
+ model.no_prop
149
+
150
+ assert str(ex.value) == "'BasicResource' object has no attribute 'no_prop'"
151
+ assert model["noProp"] == dict_response["noProp"] == "bonjour!"
152
+
153
+ # let's add it to model now
154
+
155
+ class BasicResourceWithProperty(BasicResource):
156
+ no_prop: str = rest_field(name="noProp")
157
+
158
+ model = BasicResourceWithProperty(
159
+ platform_update_domain_count=5,
160
+ platform_fault_domain_count=3,
161
+ virtual_machines=[],
162
+ no_prop="bonjour!",
163
+ )
164
+ assert model.no_prop == model["noProp"] == dict_response["noProp"] == "bonjour!"
165
+
166
+
167
+ def test_original_and_attr_name_same():
168
+ class MyModel(Model):
169
+ hello: str = rest_field()
170
+
171
+ @overload
172
+ def __init__(self, *, hello: str): ...
173
+
174
+ @overload
175
+ def __init__(self, mapping: Mapping[str, Any], /): ...
176
+
177
+ def __init__(self, *args, **kwargs):
178
+ super().__init__(*args, **kwargs)
179
+
180
+ dict_response = {"hello": "nihao"}
181
+ model = MyModel(hello="nihao")
182
+ assert model.hello == model["hello"] == dict_response["hello"]
183
+
184
+
185
+ class OptionalModel(Model):
186
+ optional_str: Optional[str] = rest_field()
187
+ optional_time: Optional[datetime.time] = rest_field()
188
+ optional_dict: Optional[Dict[str, Optional[Pet]]] = rest_field(name="optionalDict")
189
+ optional_model: Optional[Pet] = rest_field()
190
+ optional_myself: Optional["OptionalModel"] = rest_field()
191
+
192
+ @overload
193
+ def __init__(
194
+ self,
195
+ *,
196
+ optional_str: Optional[str] = None,
197
+ optional_time: Optional[datetime.time] = None,
198
+ optional_dict: Optional[Dict[str, Optional[Pet]]] = None,
199
+ optional_myself: Optional["OptionalModel"] = None,
200
+ ): ...
201
+
202
+ @overload
203
+ def __init__(self, mapping: Mapping[str, Any], /): ...
204
+
205
+ def __init__(self, *args, **kwargs):
206
+ super().__init__(*args, **kwargs)
207
+
208
+
209
+ def test_optional_property():
210
+ dict_response = {
211
+ "optional_str": "hello!",
212
+ "optional_time": None,
213
+ "optionalDict": {
214
+ "Eugene": {
215
+ "name": "Eugene",
216
+ "species": "Dog",
217
+ },
218
+ "Lady": None,
219
+ },
220
+ "optional_model": None,
221
+ "optional_myself": {
222
+ "optional_str": None,
223
+ "optional_time": "11:34:56",
224
+ "optionalDict": None,
225
+ "optional_model": {"name": "Lady", "species": "Newt"},
226
+ "optional_myself": None,
227
+ },
228
+ }
229
+
230
+ model = OptionalModel(dict_response)
231
+ assert model.optional_str == model["optional_str"] == "hello!"
232
+ assert model.optional_time == model["optional_time"] == None
233
+ assert (
234
+ model.optional_dict
235
+ == model["optionalDict"]
236
+ == {
237
+ "Eugene": {
238
+ "name": "Eugene",
239
+ "species": "Dog",
240
+ },
241
+ "Lady": None,
242
+ }
243
+ )
244
+ assert model.optional_dict
245
+ assert model.optional_dict["Eugene"].name == model.optional_dict["Eugene"]["name"] == "Eugene"
246
+ assert model.optional_dict["Lady"] is None
247
+
248
+ assert (
249
+ model.optional_myself
250
+ == model["optional_myself"]
251
+ == {
252
+ "optional_str": None,
253
+ "optional_time": "11:34:56",
254
+ "optionalDict": None,
255
+ "optional_model": {"name": "Lady", "species": "Newt"},
256
+ "optional_myself": None,
257
+ }
258
+ )
259
+ assert model.optional_myself
260
+ assert model.optional_myself.optional_str is None
261
+ assert model.optional_myself.optional_time == datetime.time(11, 34, 56)
262
+ assert model.optional_myself.optional_dict is None
263
+ assert model.optional_myself.optional_model
264
+ assert model.optional_myself.optional_model.name == "Lady"
265
+ assert model.optional_myself.optional_model.species == "Newt"
266
+ assert model.optional_myself.optional_myself is None
267
+
268
+
269
+ def test_model_pass_in_none():
270
+ model = OptionalModel(optional_str=None)
271
+ assert model.optional_str == None
272
+ with pytest.raises(KeyError):
273
+ model["optionalStr"]
274
+
275
+
276
+ def test_modify_dict():
277
+ model = BasicResource(
278
+ platform_update_domain_count=5,
279
+ platform_fault_domain_count=3,
280
+ virtual_machines=[],
281
+ )
282
+
283
+ # now let's modify the model as a dict
284
+ model["platformUpdateDomainCount"] = 100
285
+ assert model.platform_update_domain_count == model["platformUpdateDomainCount"] == 100
286
+
287
+
288
+ def test_modify_property():
289
+ dict_response = {
290
+ "platformUpdateDomainCount": 5,
291
+ "platformFaultDomainCount": 3,
292
+ "virtualMachines": [],
293
+ }
294
+ model = BasicResource(
295
+ platform_update_domain_count=5,
296
+ platform_fault_domain_count=3,
297
+ virtual_machines=[],
298
+ )
299
+
300
+ # now let's modify the model through it's properties
301
+ model.platform_fault_domain_count = 2000
302
+ model["platformFaultDomainCount"]
303
+ assert model.platform_fault_domain_count == model["platformFaultDomainCount"] == 2000
304
+
305
+
306
+ def test_property_is_a_type():
307
+ class Fish(Model):
308
+ name: str = rest_field()
309
+ species: Literal["Salmon", "Halibut"] = rest_field()
310
+
311
+ @overload
312
+ def __init__(self, *, name: str, species: Literal["Salmon", "Halibut"]): ...
313
+
314
+ @overload
315
+ def __init__(self, mapping: Mapping[str, Any], /): ...
316
+
317
+ def __init__(self, *args, **kwargs):
318
+ super().__init__(*args, **kwargs)
319
+
320
+ class Fishery(Model):
321
+ fish: Fish = rest_field()
322
+
323
+ @overload
324
+ def __init__(self, *, fish: Fish): ...
325
+
326
+ @overload
327
+ def __init__(self, mapping: Mapping[str, Any], /): ...
328
+
329
+ def __init__(self, *args, **kwargs):
330
+ super().__init__(*args, **kwargs)
331
+
332
+ fishery = Fishery({"fish": {"name": "Benjamin", "species": "Salmon"}})
333
+ assert isinstance(fishery.fish, Fish)
334
+ assert fishery.fish.name == fishery.fish["name"] == fishery["fish"]["name"] == "Benjamin"
335
+ assert fishery.fish.species == fishery.fish["species"] == fishery["fish"]["species"] == "Salmon"
336
+
337
+
338
+ def test_datetime_deserialization():
339
+ class DatetimeModel(Model):
340
+ datetime_value: datetime.datetime = rest_field(name="datetimeValue")
341
+
342
+ @overload
343
+ def __init__(self, *, datetime_value: datetime.datetime): ...
344
+
345
+ @overload
346
+ def __init__(self, mapping: Mapping[str, Any], /): ...
347
+
348
+ def __init__(self, *args, **kwargs):
349
+ super().__init__(*args, **kwargs)
350
+
351
+ val_str = "9999-12-31T23:59:59.999Z"
352
+ val = isodate.parse_datetime(val_str)
353
+ model = DatetimeModel({"datetimeValue": val_str})
354
+ assert model["datetimeValue"] == val_str
355
+ assert model.datetime_value == val
356
+
357
+ class BaseModel(Model):
358
+ my_prop: DatetimeModel = rest_field(name="myProp")
359
+
360
+ model = BaseModel({"myProp": {"datetimeValue": val_str}})
361
+ assert isinstance(model.my_prop, DatetimeModel)
362
+ model.my_prop["datetimeValue"]
363
+ assert model.my_prop["datetimeValue"] == model["myProp"]["datetimeValue"] == val_str
364
+ assert model.my_prop.datetime_value == val
365
+
366
+
367
+ def test_date_deserialization():
368
+ class DateModel(Model):
369
+ date_value: datetime.date = rest_field(name="dateValue")
370
+
371
+ @overload
372
+ def __init__(self, *, date_value: datetime.date): ...
373
+
374
+ @overload
375
+ def __init__(self, mapping: Mapping[str, Any], /): ...
376
+
377
+ def __init__(self, *args, **kwargs):
378
+ super().__init__(*args, **kwargs)
379
+
380
+ val_str = "2016-02-29"
381
+ val = isodate.parse_date(val_str)
382
+ model = DateModel({"dateValue": val_str})
383
+ assert model["dateValue"] == val_str
384
+ assert model.date_value == val
385
+
386
+ class BaseModel(Model):
387
+ my_prop: DateModel = rest_field(name="myProp")
388
+
389
+ model = BaseModel({"myProp": {"dateValue": val_str}})
390
+ assert isinstance(model.my_prop, DateModel)
391
+ assert model.my_prop["dateValue"] == model["myProp"]["dateValue"] == val_str
392
+ assert model.my_prop.date_value == val
393
+
394
+
395
+ def test_time_deserialization():
396
+ class TimeModel(Model):
397
+ time_value: datetime.time = rest_field(name="timeValue")
398
+
399
+ @overload
400
+ def __init__(self, *, time_value: datetime.time): ...
401
+
402
+ @overload
403
+ def __init__(self, mapping: Mapping[str, Any], /): ...
404
+
405
+ def __init__(self, *args, **kwargs):
406
+ super().__init__(*args, **kwargs)
407
+
408
+ val_str = "11:34:56"
409
+ val = datetime.time(11, 34, 56)
410
+ model = TimeModel({"timeValue": val_str})
411
+ assert model["timeValue"] == val_str
412
+ assert model.time_value == val
413
+
414
+ class BaseModel(Model):
415
+ my_prop: TimeModel = rest_field(name="myProp")
416
+
417
+ model = BaseModel({"myProp": {"timeValue": val_str}})
418
+ assert isinstance(model.my_prop, TimeModel)
419
+ assert model.my_prop["timeValue"] == model["myProp"]["timeValue"] == val_str
420
+ assert model.my_prop.time_value == val
421
+
422
+
423
+ class SimpleRecursiveModel(Model):
424
+ name: str = rest_field()
425
+ me: "SimpleRecursiveModel" = rest_field()
426
+
427
+ @overload
428
+ def __init__(self, *, name: str, me: "SimpleRecursiveModel"): ...
429
+
430
+ @overload
431
+ def __init__(self, mapping: Mapping[str, Any], /): ...
432
+
433
+ def __init__(self, *args, **kwargs):
434
+ super().__init__(*args, **kwargs)
435
+
436
+
437
+ def test_model_recursion():
438
+ dict_response = {"name": "Snoopy", "me": {"name": "Egg", "me": {"name": "Chicken"}}}
439
+
440
+ model = SimpleRecursiveModel(dict_response)
441
+ assert model["name"] == model.name == "Snoopy"
442
+ assert model["me"] == {"name": "Egg", "me": {"name": "Chicken"}}
443
+ assert isinstance(model.me, SimpleRecursiveModel)
444
+ assert model.me["name"] == model.me.name == "Egg"
445
+ assert model.me["me"] == {"name": "Chicken"}
446
+ assert model.me.me.name == "Chicken"
447
+
448
+
449
+ def test_dictionary_deserialization():
450
+ class DictionaryModel(Model):
451
+ prop: Dict[str, datetime.datetime] = rest_field()
452
+
453
+ @overload
454
+ def __init__(self, *, prop: datetime.datetime): ...
455
+
456
+ @overload
457
+ def __init__(self, mapping: Mapping[str, Any], /): ...
458
+
459
+ def __init__(self, *args, **kwargs):
460
+ super().__init__(*args, **kwargs)
461
+
462
+ val_str = "9999-12-31T23:59:59.999Z"
463
+ val = isodate.parse_datetime(val_str)
464
+ dict_response = {"prop": {"datetime": val_str}}
465
+ model = DictionaryModel(dict_response)
466
+ assert model["prop"] == {"datetime": val_str}
467
+ assert model.prop == {"datetime": val}
468
+
469
+
470
+ def test_attr_and_rest_case():
471
+ class ModelTest(Model):
472
+ our_attr: str = rest_field(name="ourAttr")
473
+
474
+ @overload
475
+ def __init__(self, *, our_attr: str): ...
476
+
477
+ @overload
478
+ def __init__(self, mapping: Mapping[str, Any], /): ...
479
+
480
+ def __init__(self, *args, **kwargs):
481
+ super().__init__(*args, **kwargs)
482
+
483
+ test_model = ModelTest({"ourAttr": "camel"})
484
+ assert test_model.our_attr == test_model["ourAttr"] == "camel"
485
+
486
+ test_model = ModelTest(ModelTest({"ourAttr": "camel"}))
487
+ assert test_model.our_attr == test_model["ourAttr"] == "camel"
488
+
489
+ test_model = ModelTest(our_attr="snake")
490
+ assert test_model.our_attr == test_model["ourAttr"] == "snake"
491
+
492
+
493
+ def test_dictionary_deserialization_model():
494
+ class DictionaryModel(Model):
495
+ prop: Dict[str, Pet] = rest_field()
496
+
497
+ @overload
498
+ def __init__(self, *, prop: Dict[str, Pet]): ...
499
+
500
+ @overload
501
+ def __init__(self, mapping: Mapping[str, Any], /): ...
502
+
503
+ def __init__(self, *args, **kwargs):
504
+ super().__init__(*args, **kwargs)
505
+
506
+ dict_response = {
507
+ "prop": {
508
+ "Eugene": {
509
+ "name": "Eugene",
510
+ "species": "Dog",
511
+ },
512
+ "Lady": {
513
+ "name": "Lady",
514
+ "species": "Newt",
515
+ },
516
+ }
517
+ }
518
+
519
+ model = DictionaryModel(dict_response)
520
+ assert model["prop"] == {
521
+ "Eugene": {
522
+ "name": "Eugene",
523
+ "species": "Dog",
524
+ },
525
+ "Lady": {
526
+ "name": "Lady",
527
+ "species": "Newt",
528
+ },
529
+ }
530
+ assert model.prop == {
531
+ "Eugene": Pet({"name": "Eugene", "species": "Dog"}),
532
+ "Lady": Pet({"name": "Lady", "species": "Newt"}),
533
+ }
534
+ assert model.prop["Eugene"].name == model.prop["Eugene"]["name"] == "Eugene"
535
+ assert model.prop["Eugene"].species == model.prop["Eugene"]["species"] == "Dog"
536
+ assert model.prop["Lady"].name == model.prop["Lady"]["name"] == "Lady"
537
+ assert model.prop["Lady"].species == model.prop["Lady"]["species"] == "Newt"
538
+
539
+
540
+ def test_list_deserialization():
541
+ class ListModel(Model):
542
+ prop: List[datetime.datetime] = rest_field()
543
+
544
+ @overload
545
+ def __init__(self, *, prop: List[datetime.datetime]): ...
546
+
547
+ @overload
548
+ def __init__(self, mapping: Mapping[str, Any], /): ...
549
+
550
+ def __init__(self, *args, **kwargs):
551
+ super().__init__(*args, **kwargs)
552
+
553
+ val_str = "9999-12-31T23:59:59.999Z"
554
+ val = isodate.parse_datetime(val_str)
555
+ dict_response = {"prop": [val_str, val_str]}
556
+ model = ListModel(dict_response)
557
+ assert model["prop"] == [val_str, val_str]
558
+ assert model.prop == [val, val]
559
+
560
+
561
+ def test_list_deserialization_model():
562
+ class ListModel(Model):
563
+ prop: List[Pet] = rest_field()
564
+
565
+ @overload
566
+ def __init__(self, *, prop: List[Pet]): ...
567
+
568
+ @overload
569
+ def __init__(self, mapping: Mapping[str, Any], /): ...
570
+
571
+ def __init__(self, *args, **kwargs):
572
+ super().__init__(*args, **kwargs)
573
+
574
+ dict_response = {
575
+ "prop": [
576
+ {"name": "Eugene", "species": "Dog"},
577
+ {"name": "Lady", "species": "Newt"},
578
+ ]
579
+ }
580
+ model = ListModel(dict_response)
581
+ assert model["prop"] == [
582
+ {"name": "Eugene", "species": "Dog"},
583
+ {"name": "Lady", "species": "Newt"},
584
+ ]
585
+ assert model.prop == [
586
+ Pet({"name": "Eugene", "species": "Dog"}),
587
+ Pet({"name": "Lady", "species": "Newt"}),
588
+ ]
589
+ assert len(model.prop) == 2
590
+ assert model.prop[0].name == model.prop[0]["name"] == "Eugene"
591
+ assert model.prop[0].species == model.prop[0]["species"] == "Dog"
592
+ assert model.prop[1].name == model.prop[1]["name"] == "Lady"
593
+ assert model.prop[1].species == model.prop[1]["species"] == "Newt"
594
+
595
+
596
+ def test_set_deserialization():
597
+ class SetModel(Model):
598
+ prop: Set[datetime.datetime] = rest_field()
599
+
600
+ @overload
601
+ def __init__(self, *, prop: Set[datetime.datetime]): ...
602
+
603
+ @overload
604
+ def __init__(self, mapping: Mapping[str, Any], /): ...
605
+
606
+ def __init__(self, *args, **kwargs):
607
+ super().__init__(*args, **kwargs)
608
+
609
+ val_str = "9999-12-31T23:59:59.999Z"
610
+ val = isodate.parse_datetime(val_str)
611
+ dict_response = {"prop": set([val_str, val_str])}
612
+ model = SetModel(dict_response)
613
+ assert model["prop"] == set([val_str, val_str])
614
+ assert model.prop == set([val, val])
615
+
616
+
617
+ def test_tuple_deserialization():
618
+ class TupleModel(Model):
619
+ prop: Tuple[str, datetime.datetime] = rest_field()
620
+
621
+ @overload
622
+ def __init__(self, *, prop: Tuple[str, datetime.datetime]): ...
623
+
624
+ @overload
625
+ def __init__(self, mapping: Mapping[str, Any], /): ...
626
+
627
+ def __init__(self, *args, **kwargs):
628
+ super().__init__(*args, **kwargs)
629
+
630
+ val_str = "9999-12-31T23:59:59.999Z"
631
+ val = isodate.parse_datetime(val_str)
632
+ dict_response = {"prop": (val_str, val_str)}
633
+ model = TupleModel(dict_response)
634
+ assert model["prop"] == (val_str, val_str)
635
+ assert model.prop == (val_str, val)
636
+
637
+
638
+ def test_list_of_tuple_deserialization_model():
639
+ class Owner(Model):
640
+ name: str = rest_field()
641
+ pet: Pet = rest_field()
642
+
643
+ @overload
644
+ def __init__(self, *, name: str, pet: Pet): ...
645
+
646
+ @overload
647
+ def __init__(self, mapping: Mapping[str, Any], /): ...
648
+
649
+ def __init__(self, *args, **kwargs):
650
+ super().__init__(*args, **kwargs)
651
+
652
+ class ListOfTupleModel(Model):
653
+ prop: List[Tuple[Pet, Owner]] = rest_field()
654
+
655
+ @overload
656
+ def __init__(self, *, prop: List[Tuple[Pet, Owner]]): ...
657
+
658
+ @overload
659
+ def __init__(self, mapping: Mapping[str, Any], /): ...
660
+
661
+ def __init__(self, *args, **kwargs):
662
+ super().__init__(*args, **kwargs)
663
+
664
+ eugene = {"name": "Eugene", "species": "Dog"}
665
+ lady = {"name": "Lady", "species": "Newt"}
666
+ giacamo = {"name": "Giacamo", "pet": eugene}
667
+ elizabeth = {"name": "Elizabeth", "pet": lady}
668
+
669
+ dict_response: Dict[str, Any] = {"prop": [(eugene, giacamo), (lady, elizabeth)]}
670
+ model = ListOfTupleModel(dict_response)
671
+ assert (
672
+ model["prop"]
673
+ == model.prop
674
+ == [(eugene, giacamo), (lady, elizabeth)]
675
+ == [(Pet(eugene), Owner(giacamo)), (Pet(lady), Owner(elizabeth))]
676
+ )
677
+ assert len(model.prop[0]) == len(model["prop"][0]) == 2
678
+ assert model.prop[0][0].name == model.prop[0][0]["name"] == "Eugene"
679
+ assert model.prop[0][0].species == model.prop[0][0]["species"] == "Dog"
680
+ assert model.prop[0][1].name == "Giacamo"
681
+ assert model.prop[0][1].pet == model.prop[0][0]
682
+ assert model.prop[0][1].pet.name == model.prop[0][1]["pet"]["name"] == "Eugene"
683
+ assert model.prop[1][0] == model.prop[1][1].pet
684
+
685
+
686
+ class RecursiveModel(Model):
687
+ name: str = rest_field()
688
+ list_of_me: Optional[List["RecursiveModel"]] = rest_field(name="listOfMe")
689
+ dict_of_me: Optional[Dict[str, "RecursiveModel"]] = rest_field(name="dictOfMe")
690
+ dict_of_list_of_me: Optional[Dict[str, List["RecursiveModel"]]] = rest_field(name="dictOfListOfMe")
691
+ list_of_dict_of_me: Optional[List[Dict[str, "RecursiveModel"]]] = rest_field(name="listOfDictOfMe")
692
+
693
+ @overload
694
+ def __init__(
695
+ self,
696
+ *,
697
+ name: str,
698
+ list_of_me: Optional[List["RecursiveModel"]] = None,
699
+ dict_of_me: Optional[Dict[str, "RecursiveModel"]] = None,
700
+ dict_of_list_of_me: Optional[Dict[str, List["RecursiveModel"]]] = None,
701
+ list_of_dict_of_me: Optional[List[Dict[str, "RecursiveModel"]]] = None,
702
+ ): ...
703
+
704
+ @overload
705
+ def __init__(self, mapping: Mapping[str, Any], /): ...
706
+
707
+ def __init__(self, *args, **kwargs):
708
+ super().__init__(*args, **kwargs)
709
+
710
+
711
+ def test_model_recursion_complex():
712
+ dict_response = {
713
+ "name": "it's me!",
714
+ "listOfMe": [
715
+ {
716
+ "name": "it's me!",
717
+ "listOfMe": None,
718
+ "dictOfMe": None,
719
+ "dictOfListOfMe": None,
720
+ "listOfDictOfMe": None,
721
+ }
722
+ ],
723
+ "dictOfMe": {
724
+ "me": {
725
+ "name": "it's me!",
726
+ "listOfMe": None,
727
+ "dictOfMe": None,
728
+ "dictOfListOfMe": None,
729
+ "listOfDictOfMe": None,
730
+ }
731
+ },
732
+ "dictOfListOfMe": {
733
+ "many mes": [
734
+ {
735
+ "name": "it's me!",
736
+ "listOfMe": None,
737
+ "dictOfMe": None,
738
+ "dictOfListOfMe": None,
739
+ "listOfDictOfMe": None,
740
+ }
741
+ ]
742
+ },
743
+ "listOfDictOfMe": [
744
+ {
745
+ "me": {
746
+ "name": "it's me!",
747
+ "listOfMe": None,
748
+ "dictOfMe": None,
749
+ "dictOfListOfMe": None,
750
+ "listOfDictOfMe": None,
751
+ }
752
+ }
753
+ ],
754
+ }
755
+
756
+ model = RecursiveModel(dict_response)
757
+ assert model.name == model["name"] == "it's me!"
758
+ assert model["listOfMe"] == [
759
+ {
760
+ "name": "it's me!",
761
+ "listOfMe": None,
762
+ "dictOfMe": None,
763
+ "dictOfListOfMe": None,
764
+ "listOfDictOfMe": None,
765
+ }
766
+ ]
767
+ assert model.list_of_me == [
768
+ RecursiveModel(
769
+ {
770
+ "name": "it's me!",
771
+ "listOfMe": None,
772
+ "dictOfMe": None,
773
+ "dictOfListOfMe": None,
774
+ "listOfDictOfMe": None,
775
+ }
776
+ )
777
+ ]
778
+ assert model.list_of_me
779
+ assert model.list_of_me[0].name == "it's me!"
780
+ assert model.list_of_me[0].list_of_me is None
781
+ assert isinstance(model.list_of_me, List)
782
+ assert isinstance(model.list_of_me[0], RecursiveModel)
783
+
784
+ assert model["dictOfMe"] == {
785
+ "me": {
786
+ "name": "it's me!",
787
+ "listOfMe": None,
788
+ "dictOfMe": None,
789
+ "dictOfListOfMe": None,
790
+ "listOfDictOfMe": None,
791
+ }
792
+ }
793
+ assert model.dict_of_me == {
794
+ "me": RecursiveModel(
795
+ {
796
+ "name": "it's me!",
797
+ "listOfMe": None,
798
+ "dictOfMe": None,
799
+ "dictOfListOfMe": None,
800
+ "listOfDictOfMe": None,
801
+ }
802
+ )
803
+ }
804
+
805
+ assert isinstance(model.dict_of_me, Dict)
806
+ assert isinstance(model.dict_of_me["me"], RecursiveModel)
807
+
808
+ assert model["dictOfListOfMe"] == {
809
+ "many mes": [
810
+ {
811
+ "name": "it's me!",
812
+ "listOfMe": None,
813
+ "dictOfMe": None,
814
+ "dictOfListOfMe": None,
815
+ "listOfDictOfMe": None,
816
+ }
817
+ ]
818
+ }
819
+ assert model.dict_of_list_of_me == {
820
+ "many mes": [
821
+ RecursiveModel(
822
+ {
823
+ "name": "it's me!",
824
+ "listOfMe": None,
825
+ "dictOfMe": None,
826
+ "dictOfListOfMe": None,
827
+ "listOfDictOfMe": None,
828
+ }
829
+ )
830
+ ]
831
+ }
832
+ assert isinstance(model.dict_of_list_of_me, Dict)
833
+ assert isinstance(model.dict_of_list_of_me["many mes"], List)
834
+ assert isinstance(model.dict_of_list_of_me["many mes"][0], RecursiveModel)
835
+
836
+ assert model["listOfDictOfMe"] == [
837
+ {
838
+ "me": {
839
+ "name": "it's me!",
840
+ "listOfMe": None,
841
+ "dictOfMe": None,
842
+ "dictOfListOfMe": None,
843
+ "listOfDictOfMe": None,
844
+ }
845
+ }
846
+ ]
847
+ assert model.list_of_dict_of_me == [
848
+ {
849
+ "me": RecursiveModel(
850
+ {
851
+ "name": "it's me!",
852
+ "listOfMe": None,
853
+ "dictOfMe": None,
854
+ "dictOfListOfMe": None,
855
+ "listOfDictOfMe": None,
856
+ }
857
+ )
858
+ }
859
+ ]
860
+ assert isinstance(model.list_of_dict_of_me, List)
861
+ assert isinstance(model.list_of_dict_of_me[0], Dict)
862
+ assert isinstance(model.list_of_dict_of_me[0]["me"], RecursiveModel)
863
+
864
+ assert model.as_dict() == model == dict_response
865
+
866
+
867
+ def test_literals():
868
+ class LiteralModel(Model):
869
+ species: Literal["Mongoose", "Eagle", "Penguin"] = rest_field()
870
+ age: Literal[1, 2, 3] = rest_field()
871
+
872
+ @overload
873
+ def __init__(
874
+ self,
875
+ *,
876
+ species: Literal["Mongoose", "Eagle", "Penguin"],
877
+ age: Literal[1, 2, 3],
878
+ ): ...
879
+
880
+ @overload
881
+ def __init__(self, mapping: Mapping[str, Any], /): ...
882
+
883
+ def __init__(self, *args, **kwargs):
884
+ super().__init__(*args, **kwargs)
885
+
886
+ dict_response = {"species": "Mongoose", "age": 3}
887
+ model = LiteralModel(dict_response)
888
+ assert model.species == model["species"] == "Mongoose"
889
+ assert model.age == model["age"] == 3
890
+
891
+ dict_response = {"species": "invalid", "age": 5}
892
+ model = LiteralModel(dict_response)
893
+ assert model["species"] == "invalid"
894
+ assert model["age"] == 5
895
+
896
+ assert model.species == "invalid"
897
+
898
+ assert model.age == 5
899
+
900
+
901
+ def test_deserialization_callback_override():
902
+ def _callback(obj):
903
+ return [str(entry) for entry in obj]
904
+
905
+ class MyModel(Model):
906
+ prop: Sequence[float] = rest_field()
907
+
908
+ @overload
909
+ def __init__(self, *, prop: Sequence[float]): ...
910
+
911
+ @overload
912
+ def __init__(self, mapping: Mapping[str, Any], /): ...
913
+
914
+ def __init__(self, *args, **kwargs):
915
+ super().__init__(*args, **kwargs)
916
+
917
+ model_without_callback = MyModel(prop=[1.3, 2.4, 3.5])
918
+ assert model_without_callback.prop == [1.3, 2.4, 3.5]
919
+ assert model_without_callback["prop"] == [1.3, 2.4, 3.5]
920
+
921
+ class MyModel2(Model):
922
+ prop: Sequence[int] = rest_field(type=_callback)
923
+
924
+ @overload
925
+ def __init__(self, *, prop: Any): ...
926
+
927
+ @overload
928
+ def __init__(self, mapping: Mapping[str, Any], /): ...
929
+
930
+ def __init__(self, *args, **kwargs):
931
+ super().__init__(*args, **kwargs)
932
+
933
+ model_with_callback = MyModel2(prop=[1.3, 2.4, 3.5])
934
+ assert model_with_callback.prop == ["1.3", "2.4", "3.5"]
935
+ # since the deserialize function is not roundtrip-able, once we deserialize
936
+ # the serialized version is the same
937
+ assert model_with_callback["prop"] == [1.3, 2.4, 3.5]
938
+
939
+
940
+ def test_deserialization_callback_override_parent():
941
+ class ParentNoCallback(Model):
942
+ prop: Sequence[float] = rest_field()
943
+
944
+ @overload
945
+ def __init__(self, *, prop: Sequence[float]): ...
946
+
947
+ @overload
948
+ def __init__(self, mapping: Mapping[str, Any], /): ...
949
+
950
+ def __init__(self, *args, **kwargs):
951
+ super().__init__(*args, **kwargs)
952
+
953
+ def _callback(obj):
954
+ return set([str(entry) for entry in obj])
955
+
956
+ class ChildWithCallback(ParentNoCallback):
957
+ prop: Sequence[float] = rest_field(type=_callback)
958
+
959
+ @overload
960
+ def __init__(self, *, prop: Sequence[float]): ...
961
+
962
+ @overload
963
+ def __init__(self, mapping: Mapping[str, Any], /): ...
964
+
965
+ def __init__(self, *args, **kwargs):
966
+ super().__init__(*args, **kwargs)
967
+
968
+ parent_model = ParentNoCallback(prop=[1, 1, 2, 3])
969
+ assert parent_model.prop == parent_model["prop"] == [1, 1, 2, 3]
970
+
971
+ child_model = ChildWithCallback(prop=[1, 1, 2, 3])
972
+ assert child_model.prop == set(["1", "1", "2", "3"])
973
+ assert child_model["prop"] == [1, 1, 2, 3]
974
+
975
+
976
+ def test_inheritance_basic():
977
+ def _callback(obj):
978
+ return [str(e) for e in obj]
979
+
980
+ class Parent(Model):
981
+ parent_prop: List[int] = rest_field(name="parentProp", type=_callback)
982
+ prop: str = rest_field()
983
+
984
+ @overload
985
+ def __init__(self, *, parent_prop: List[int], prop: str): ...
986
+
987
+ @overload
988
+ def __init__(self, mapping: Mapping[str, Any], /): ...
989
+
990
+ def __init__(self, *args, **kwargs):
991
+ super().__init__(*args, **kwargs)
992
+
993
+ class Child(Parent):
994
+ pass
995
+
996
+ c = Child(parent_prop=[1, 2, 3], prop="hello")
997
+ assert c == {"parentProp": [1, 2, 3], "prop": "hello"}
998
+ assert c.parent_prop == ["1", "2", "3"]
999
+ assert c.prop == "hello"
1000
+
1001
+
1002
+ class ParentA(Model):
1003
+ prop: float = rest_field()
1004
+
1005
+ @overload
1006
+ def __init__(self, *, prop: Any): ...
1007
+
1008
+ @overload
1009
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1010
+
1011
+ def __init__(self, *args, **kwargs):
1012
+ super().__init__(*args, **kwargs)
1013
+
1014
+
1015
+ class ParentB(ParentA):
1016
+ prop: str = rest_field()
1017
+ bcd_prop: Optional[List["ParentB"]] = rest_field(name="bcdProp")
1018
+
1019
+ @overload
1020
+ def __init__(self, *, prop: Any, bcd_prop: Optional[List["ParentB"]] = None): ...
1021
+
1022
+ @overload
1023
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1024
+
1025
+ def __init__(self, *args, **kwargs):
1026
+ super().__init__(*args, **kwargs)
1027
+
1028
+
1029
+ class ParentC(ParentB):
1030
+ prop: float = rest_field()
1031
+ cd_prop: ParentA = rest_field(name="cdProp")
1032
+
1033
+ @overload
1034
+ def __init__(self, *, prop: Any, bcd_prop: List[ParentB], cd_prop: ParentA, **kwargs): ...
1035
+
1036
+ @overload
1037
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1038
+
1039
+ def __init__(self, *args, **kwargs):
1040
+ super().__init__(*args, **kwargs)
1041
+
1042
+
1043
+ class ChildD(ParentC):
1044
+ d_prop: Tuple[ParentA, ParentB, ParentC, Optional["ChildD"]] = rest_field(name="dProp")
1045
+
1046
+ @overload
1047
+ def __init__(
1048
+ self,
1049
+ *,
1050
+ prop: Any,
1051
+ bcd_prop: List[ParentB],
1052
+ cd_prop: ParentA,
1053
+ d_prop: Tuple[ParentA, ParentB, ParentC, Optional["ChildD"]],
1054
+ ): ...
1055
+
1056
+ @overload
1057
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1058
+
1059
+ def __init__(self, *args, **kwargs):
1060
+ super().__init__(*args, **kwargs)
1061
+
1062
+
1063
+ def test_model_dict_comparisons():
1064
+ class Inner(Model):
1065
+ prop: str = rest_field()
1066
+
1067
+ @overload
1068
+ def __init__(
1069
+ self,
1070
+ *,
1071
+ prop: str,
1072
+ ): ...
1073
+
1074
+ @overload
1075
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1076
+
1077
+ def __init__(self, *args, **kwargs):
1078
+ super().__init__(*args, **kwargs)
1079
+
1080
+ class Outer(Model):
1081
+ inner: Inner = rest_field()
1082
+
1083
+ @overload
1084
+ def __init__(
1085
+ self,
1086
+ *,
1087
+ inner: Inner,
1088
+ ): ...
1089
+
1090
+ @overload
1091
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1092
+
1093
+ def __init__(self, *args, **kwargs):
1094
+ super().__init__(*args, **kwargs)
1095
+
1096
+ def _tests(outer):
1097
+ assert outer.inner.prop == outer["inner"].prop == outer.inner["prop"] == outer["inner"]["prop"] == "hello"
1098
+ assert outer.inner == outer["inner"] == {"prop": "hello"}
1099
+ assert outer == {"inner": {"prop": "hello"}}
1100
+
1101
+ _tests(Outer(inner=Inner(prop="hello")))
1102
+ _tests(Outer({"inner": {"prop": "hello"}}))
1103
+
1104
+
1105
+ def test_model_dict_comparisons_list():
1106
+ class Inner(Model):
1107
+ prop: str = rest_field()
1108
+
1109
+ @overload
1110
+ def __init__(
1111
+ self,
1112
+ *,
1113
+ prop: str,
1114
+ ): ...
1115
+
1116
+ @overload
1117
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1118
+
1119
+ def __init__(self, *args, **kwargs):
1120
+ super().__init__(*args, **kwargs)
1121
+
1122
+ class Outer(Model):
1123
+ inner: List[Inner] = rest_field()
1124
+
1125
+ @overload
1126
+ def __init__(
1127
+ self,
1128
+ *,
1129
+ inner: List[Inner],
1130
+ ): ...
1131
+
1132
+ @overload
1133
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1134
+
1135
+ def __init__(self, *args, **kwargs):
1136
+ super().__init__(*args, **kwargs)
1137
+
1138
+ def _tests(outer):
1139
+ assert (
1140
+ outer.inner[0].prop
1141
+ == outer["inner"][0].prop
1142
+ == outer.inner[0]["prop"]
1143
+ == outer["inner"][0]["prop"]
1144
+ == "hello"
1145
+ )
1146
+ assert outer.inner == outer["inner"] == [{"prop": "hello"}]
1147
+ assert outer == {"inner": [{"prop": "hello"}]}
1148
+
1149
+ _tests(Outer(inner=[Inner(prop="hello")]))
1150
+ _tests(Outer({"inner": [{"prop": "hello"}]}))
1151
+
1152
+
1153
+ def test_model_dict_comparisons_dict():
1154
+ class Inner(Model):
1155
+ prop: str = rest_field()
1156
+
1157
+ @overload
1158
+ def __init__(
1159
+ self,
1160
+ *,
1161
+ prop: str,
1162
+ ): ...
1163
+
1164
+ @overload
1165
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1166
+
1167
+ def __init__(self, *args, **kwargs):
1168
+ super().__init__(*args, **kwargs)
1169
+
1170
+ class Outer(Model):
1171
+ inner: Dict[str, Inner] = rest_field()
1172
+
1173
+ @overload
1174
+ def __init__(
1175
+ self,
1176
+ *,
1177
+ inner: Dict[str, Inner],
1178
+ ): ...
1179
+
1180
+ @overload
1181
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1182
+
1183
+ def __init__(self, *args, **kwargs):
1184
+ super().__init__(*args, **kwargs)
1185
+
1186
+ def _tests(outer):
1187
+ assert (
1188
+ outer.inner["key"].prop
1189
+ == outer["inner"]["key"].prop
1190
+ == outer.inner["key"]["prop"]
1191
+ == outer["inner"]["key"]["prop"]
1192
+ == "hello"
1193
+ )
1194
+ assert outer.inner == outer["inner"] == {"key": {"prop": "hello"}}
1195
+ with pytest.raises(AttributeError):
1196
+ outer.inner.key
1197
+ assert outer.inner["key"] == outer["inner"]["key"] == {"prop": "hello"}
1198
+ assert outer == {"inner": {"key": {"prop": "hello"}}}
1199
+
1200
+ _tests(Outer(inner={"key": Inner(prop="hello")}))
1201
+ _tests(Outer({"inner": {"key": {"prop": "hello"}}}))
1202
+
1203
+
1204
+ def test_inheritance_4_levels():
1205
+ a = ParentA(prop=3.4)
1206
+ assert a.prop == 3.4
1207
+ assert a["prop"] == 3.4
1208
+ assert a == {"prop": 3.4}
1209
+ assert isinstance(a, Model)
1210
+
1211
+ b = ParentB(prop=3.4, bcd_prop=[ParentB(prop=4.3)])
1212
+ assert b.prop == "3.4"
1213
+ assert b["prop"] == 3.4
1214
+ assert b.bcd_prop == [ParentB(prop=4.3)]
1215
+ assert b["bcdProp"] != [{"prop": 4.3, "bcdProp": None}]
1216
+ assert b["bcdProp"] == [{"prop": 4.3}]
1217
+ assert b.bcd_prop
1218
+ assert b.bcd_prop[0].prop == "4.3"
1219
+ assert b.bcd_prop[0].bcd_prop is None
1220
+ assert b == {"prop": 3.4, "bcdProp": [{"prop": 4.3}]}
1221
+ assert isinstance(b, ParentB)
1222
+ assert isinstance(b, ParentA)
1223
+
1224
+ c = ParentC(prop=3.4, bcd_prop=[b], cd_prop=a)
1225
+ assert c.prop == c["prop"] == 3.4
1226
+ assert c.bcd_prop == [b]
1227
+ assert c.bcd_prop
1228
+ assert isinstance(c.bcd_prop[0], ParentB)
1229
+ assert c["bcdProp"] == [b] == [{"prop": 3.4, "bcdProp": [{"prop": 4.3}]}]
1230
+ assert c.cd_prop == a
1231
+ assert c["cdProp"] == a == {"prop": 3.4}
1232
+ assert isinstance(c.cd_prop, ParentA)
1233
+
1234
+ d = ChildD(
1235
+ prop=3.4,
1236
+ bcd_prop=[b],
1237
+ cd_prop=a,
1238
+ d_prop=(
1239
+ a,
1240
+ b,
1241
+ c,
1242
+ ChildD(prop=3.4, bcd_prop=[b], cd_prop=a, d_prop=(a, b, c, None)),
1243
+ ),
1244
+ )
1245
+ assert d == {
1246
+ "prop": 3.4,
1247
+ "bcdProp": [b],
1248
+ "cdProp": a,
1249
+ "dProp": (
1250
+ a,
1251
+ b,
1252
+ c,
1253
+ {"prop": 3.4, "bcdProp": [b], "cdProp": a, "dProp": (a, b, c, None)},
1254
+ ),
1255
+ }
1256
+ assert d.prop == d["prop"] == 3.4
1257
+ assert d.bcd_prop == [b]
1258
+ assert d.bcd_prop
1259
+ assert isinstance(d.bcd_prop[0], ParentB)
1260
+ assert d.cd_prop == a
1261
+ assert isinstance(d.cd_prop, ParentA)
1262
+ assert d.d_prop[0] == a # at a
1263
+ assert isinstance(d.d_prop[0], ParentA)
1264
+ assert d.d_prop[1] == b
1265
+ assert isinstance(d.d_prop[1], ParentB)
1266
+ assert d.d_prop[2] == c
1267
+ assert isinstance(d.d_prop[2], ParentC)
1268
+ assert isinstance(d.d_prop[3], ChildD)
1269
+
1270
+ assert isinstance(d.d_prop[3].d_prop[0], ParentA)
1271
+ assert isinstance(d.d_prop[3].d_prop[1], ParentB)
1272
+ assert isinstance(d.d_prop[3].d_prop[2], ParentC)
1273
+ assert d.d_prop[3].d_prop[3] is None
1274
+
1275
+
1276
+ def test_multiple_inheritance_basic():
1277
+ class ParentOne(Model):
1278
+ parent_one_prop: str = rest_field(name="parentOneProp")
1279
+
1280
+ @overload
1281
+ def __init__(
1282
+ self,
1283
+ *,
1284
+ parent_one_prop: str,
1285
+ ): ...
1286
+
1287
+ @overload
1288
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1289
+
1290
+ def __init__(self, *args, **kwargs):
1291
+ super().__init__(*args, **kwargs)
1292
+
1293
+ class ParentTwo(Model):
1294
+ parent_two_prop: int = rest_field(name="parentTwoProp", type=lambda x: str(x))
1295
+
1296
+ @overload
1297
+ def __init__(
1298
+ self,
1299
+ *,
1300
+ parent_two_prop: int,
1301
+ ): ...
1302
+
1303
+ @overload
1304
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1305
+
1306
+ def __init__(self, *args, **kwargs):
1307
+ super().__init__(*args, **kwargs)
1308
+
1309
+ class Child(ParentOne, ParentTwo):
1310
+ @overload
1311
+ def __init__(
1312
+ self,
1313
+ *,
1314
+ parent_one_prop: str,
1315
+ parent_two_prop: int,
1316
+ ): ...
1317
+
1318
+ @overload
1319
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1320
+
1321
+ def __init__(self, *args, **kwargs):
1322
+ super().__init__(*args, **kwargs)
1323
+
1324
+ c = Child(parent_one_prop="Hello", parent_two_prop=3)
1325
+ assert c == {"parentOneProp": "Hello", "parentTwoProp": 3}
1326
+ assert c.parent_one_prop == "Hello"
1327
+ assert c.parent_two_prop == "3"
1328
+ assert isinstance(c, Child)
1329
+ assert isinstance(c, ParentOne)
1330
+ assert isinstance(c, ParentTwo)
1331
+
1332
+
1333
+ def test_multiple_inheritance_mro():
1334
+ class A(Model):
1335
+ prop: str = rest_field()
1336
+
1337
+ @overload
1338
+ def __init__(self, *, prop: str) -> None: ...
1339
+
1340
+ @overload
1341
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1342
+
1343
+ def __init__(self, *args, **kwargs):
1344
+ super().__init__(*args, **kwargs)
1345
+
1346
+ class B(Model):
1347
+ prop: int = rest_field(type=lambda x: int(x))
1348
+
1349
+ @overload
1350
+ def __init__(self, *, prop: str) -> None: ...
1351
+
1352
+ @overload
1353
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1354
+
1355
+ def __init__(self, *args, **kwargs):
1356
+ super().__init__(*args, **kwargs)
1357
+
1358
+ class C(A, B):
1359
+ pass
1360
+
1361
+ assert A(prop="1").prop == "1"
1362
+ assert B(prop="1").prop == 1
1363
+ assert C(prop="1").prop == "1" # A should take precedence over B
1364
+
1365
+
1366
+ class Feline(Model):
1367
+ meows: bool = rest_field()
1368
+ hisses: bool = rest_field()
1369
+ siblings: Optional[List["Feline"]] = rest_field()
1370
+
1371
+ @overload
1372
+ def __init__(
1373
+ self,
1374
+ *,
1375
+ meows: bool,
1376
+ hisses: bool,
1377
+ siblings: Optional[List["Feline"]] = None,
1378
+ ): ...
1379
+
1380
+ @overload
1381
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1382
+
1383
+ def __init__(self, *args, **kwargs):
1384
+ super().__init__(*args, **kwargs)
1385
+
1386
+
1387
+ class Owner(Model):
1388
+ first_name: str = rest_field(name="firstName", type=lambda x: x.capitalize())
1389
+ last_name: str = rest_field(name="lastName", type=lambda x: x.capitalize())
1390
+
1391
+ @overload
1392
+ def __init__(
1393
+ self,
1394
+ *,
1395
+ first_name: str,
1396
+ last_name: str,
1397
+ ): ...
1398
+
1399
+ @overload
1400
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1401
+
1402
+ def __init__(self, *args, **kwargs):
1403
+ super().__init__(*args, **kwargs)
1404
+
1405
+
1406
+ class PetModel(Model):
1407
+ name: str = rest_field()
1408
+ owner: Owner = rest_field()
1409
+
1410
+ @overload
1411
+ def __init__(self, *, name: str, owner: Owner): ...
1412
+
1413
+ @overload
1414
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1415
+
1416
+ def __init__(self, *args, **kwargs):
1417
+ super().__init__(*args, **kwargs)
1418
+
1419
+
1420
+ class Cat(PetModel, Feline):
1421
+ likes_milk: bool = rest_field(name="likesMilk", type=lambda x: True)
1422
+
1423
+ @overload
1424
+ def __init__(
1425
+ self,
1426
+ *,
1427
+ name: str,
1428
+ owner: Owner,
1429
+ meows: bool,
1430
+ hisses: bool,
1431
+ likes_milk: bool,
1432
+ siblings: Optional[List[Feline]],
1433
+ ): ...
1434
+
1435
+ @overload
1436
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1437
+
1438
+ def __init__(self, *args, **kwargs):
1439
+ super().__init__(*args, **kwargs)
1440
+
1441
+
1442
+ class CuteThing(Model):
1443
+ how_cute_am_i: float = rest_field(name="howCuteAmI")
1444
+
1445
+ @overload
1446
+ def __init__(self, *, how_cute_am_i: float): ...
1447
+
1448
+ @overload
1449
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1450
+
1451
+ def __init__(self, *args, **kwargs):
1452
+ super().__init__(*args, **kwargs)
1453
+
1454
+
1455
+ class Kitten(Cat, CuteThing):
1456
+ eats_mice_yet: bool = rest_field(name="eatsMiceYet")
1457
+
1458
+ @overload
1459
+ def __init__(
1460
+ self,
1461
+ *,
1462
+ name: str,
1463
+ owner: Owner,
1464
+ meows: bool,
1465
+ hisses: bool,
1466
+ likes_milk: bool,
1467
+ siblings: Optional[List[Feline]],
1468
+ how_cute_am_i: float,
1469
+ eats_mice_yet: bool,
1470
+ ): ...
1471
+
1472
+ @overload
1473
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1474
+
1475
+ def __init__(self, *args, **kwargs):
1476
+ super().__init__(*args, **kwargs)
1477
+
1478
+
1479
+ def test_multiple_inheritance_complex():
1480
+ cat = Cat(
1481
+ name="Stephanie",
1482
+ owner=Owner(first_name="cecil", last_name="cai"), # gets capitalized in attr
1483
+ meows=True,
1484
+ hisses=True,
1485
+ likes_milk=False, # likes_milk will change to True on the attribute
1486
+ siblings=[Feline(meows=True, hisses=False)],
1487
+ )
1488
+ assert cat == {
1489
+ "name": "Stephanie",
1490
+ "owner": {
1491
+ "firstName": "cecil",
1492
+ "lastName": "cai",
1493
+ },
1494
+ "meows": True,
1495
+ "hisses": True,
1496
+ "likesMilk": False,
1497
+ "siblings": [
1498
+ {
1499
+ "meows": True,
1500
+ "hisses": False,
1501
+ }
1502
+ ],
1503
+ }
1504
+ assert cat.name == "Stephanie"
1505
+ assert isinstance(cat.owner, Owner)
1506
+ assert cat.owner.first_name == "Cecil"
1507
+ assert cat.owner.last_name == "Cai"
1508
+ assert cat.meows
1509
+ assert cat.hisses
1510
+ assert cat.likes_milk
1511
+ assert cat.siblings
1512
+ assert len(cat.siblings) == 1
1513
+ assert isinstance(cat.siblings[0], Feline)
1514
+
1515
+ kitten = Kitten(
1516
+ name="Stephanie",
1517
+ owner=Owner(first_name="cecil", last_name="cai"), # gets capitalized in attr
1518
+ meows=True,
1519
+ hisses=True,
1520
+ likes_milk=False, # likes_milk will change to True on the attribute
1521
+ siblings=[Feline(meows=True, hisses=False)],
1522
+ how_cute_am_i=1.0,
1523
+ eats_mice_yet=True,
1524
+ )
1525
+ assert kitten != {
1526
+ "name": "Stephanie",
1527
+ "owner": {
1528
+ "firstName": "cecil",
1529
+ "lastName": "cai",
1530
+ },
1531
+ "meows": True,
1532
+ "hisses": True,
1533
+ "likesMilk": False,
1534
+ "siblings": [{"meows": True, "hisses": False, "siblings": None}], # we don't automatically set None here
1535
+ "howCuteAmI": 1.0,
1536
+ "eatsMiceYet": True,
1537
+ }
1538
+ assert kitten == {
1539
+ "name": "Stephanie",
1540
+ "owner": {
1541
+ "firstName": "cecil",
1542
+ "lastName": "cai",
1543
+ },
1544
+ "meows": True,
1545
+ "hisses": True,
1546
+ "likesMilk": False,
1547
+ "siblings": [
1548
+ {
1549
+ "meows": True,
1550
+ "hisses": False,
1551
+ }
1552
+ ],
1553
+ "howCuteAmI": 1.0,
1554
+ "eatsMiceYet": True,
1555
+ }
1556
+ assert kitten.name == "Stephanie"
1557
+ assert isinstance(kitten.owner, Owner)
1558
+ assert kitten.owner.first_name == "Cecil"
1559
+ assert kitten.owner.last_name == "Cai"
1560
+ assert kitten.meows
1561
+ assert kitten.hisses
1562
+ assert kitten.likes_milk
1563
+ assert kitten.siblings
1564
+ assert len(kitten.siblings) == 1
1565
+ assert isinstance(kitten.siblings[0], Feline)
1566
+ assert kitten.eats_mice_yet
1567
+ assert kitten.how_cute_am_i == 1.0
1568
+ assert isinstance(kitten, PetModel)
1569
+ assert isinstance(kitten, Cat)
1570
+ assert isinstance(kitten, Feline)
1571
+ assert isinstance(kitten, CuteThing)
1572
+
1573
+
1574
+ class A(Model):
1575
+ b: "B" = rest_field()
1576
+
1577
+ @overload
1578
+ def __init__(self, b: "B"): ...
1579
+
1580
+ @overload
1581
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1582
+
1583
+ def __init__(self, *args, **kwargs):
1584
+ super().__init__(*args, **kwargs)
1585
+
1586
+
1587
+ class B(Model):
1588
+ c: "C" = rest_field()
1589
+
1590
+ @overload
1591
+ def __init__(self, *, c: "C"): ...
1592
+
1593
+ @overload
1594
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1595
+
1596
+ def __init__(self, *args, **kwargs):
1597
+ super().__init__(*args, **kwargs)
1598
+
1599
+
1600
+ class C(Model):
1601
+ d: str = rest_field()
1602
+
1603
+ @overload
1604
+ def __init__(self, *, d: str): ...
1605
+
1606
+ @overload
1607
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1608
+
1609
+ def __init__(self, *args, **kwargs):
1610
+ super().__init__(*args, **kwargs)
1611
+
1612
+
1613
+ def test_nested_creation():
1614
+ a = A({"b": {"c": {"d": "hello"}}})
1615
+ assert isinstance(a["b"], Model)
1616
+ assert isinstance(a["b"]["c"], Model)
1617
+ assert a["b"]["c"] == a["b"].c == a.b.c == {"d": "hello"}
1618
+
1619
+ assert (
1620
+ a["b"]["c"]["d"]
1621
+ == a["b"].c.d
1622
+ == a.b["c"].d
1623
+ == a["b"]["c"].d
1624
+ == a["b"].c["d"]
1625
+ == a.b["c"]["d"]
1626
+ == a.b.c.d
1627
+ == "hello"
1628
+ )
1629
+
1630
+
1631
+ def test_nested_setting():
1632
+ a = A({"b": {"c": {"d": "hello"}}})
1633
+
1634
+ # set with dict
1635
+ a["b"]["c"]["d"] = "setwithdict"
1636
+ assert (
1637
+ a["b"]["c"]["d"]
1638
+ == a["b"].c.d
1639
+ == a.b["c"].d
1640
+ == a["b"]["c"].d
1641
+ == a["b"].c["d"]
1642
+ == a.b["c"]["d"]
1643
+ == a.b.c.d
1644
+ == "setwithdict"
1645
+ )
1646
+
1647
+ # set with attr
1648
+ a.b.c.d = "setwithattr"
1649
+ assert a["b"]["c"]["d"] == "setwithattr"
1650
+ assert (
1651
+ a["b"]["c"]["d"]
1652
+ == a["b"].c.d
1653
+ == a.b["c"].d
1654
+ == a["b"]["c"].d
1655
+ == a["b"].c["d"]
1656
+ == a.b["c"]["d"]
1657
+ == a.b.c.d
1658
+ == "setwithattr"
1659
+ )
1660
+
1661
+
1662
+ class BaseModel(Model):
1663
+ inner_model: "InnerModel" = rest_field(name="innerModel")
1664
+
1665
+ @overload
1666
+ def __init__(self, *, inner_model: "InnerModel"): ...
1667
+
1668
+ @overload
1669
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1670
+
1671
+ def __init__(self, *args, **kwargs):
1672
+ super().__init__(*args, **kwargs)
1673
+
1674
+
1675
+ class InnerModel(Model):
1676
+ datetime_field: datetime.datetime = rest_field(name="datetimeField")
1677
+
1678
+ @overload
1679
+ def __init__(self, *, datetime_field: datetime.datetime): ...
1680
+
1681
+ @overload
1682
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1683
+
1684
+ def __init__(self, *args, **kwargs):
1685
+ super().__init__(*args, **kwargs)
1686
+
1687
+
1688
+ def test_nested_deserialization():
1689
+ serialized_datetime = "9999-12-31T23:59:59.999Z"
1690
+
1691
+ model = BaseModel({"innerModel": {"datetimeField": serialized_datetime}})
1692
+ assert model.inner_model["datetimeField"] == model["innerModel"]["datetimeField"] == serialized_datetime
1693
+ assert (
1694
+ model.inner_model.datetime_field
1695
+ == model["innerModel"].datetime_field
1696
+ == isodate.parse_datetime(serialized_datetime)
1697
+ )
1698
+
1699
+ new_serialized_datetime = "2022-12-31T23:59:59.999Z"
1700
+ model.inner_model.datetime_field = isodate.parse_datetime(new_serialized_datetime)
1701
+ assert model.inner_model["datetimeField"] == "2022-12-31T23:59:59.999000Z"
1702
+
1703
+
1704
+ class X(Model):
1705
+ y: "Y" = rest_field()
1706
+
1707
+ @overload
1708
+ def __init__(self, *, y: "Y"): ...
1709
+
1710
+ @overload
1711
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1712
+
1713
+ def __init__(self, *args, **kwargs):
1714
+ super().__init__(*args, **kwargs)
1715
+
1716
+
1717
+ class Y(Model):
1718
+ z: "Z" = rest_field()
1719
+
1720
+ @overload
1721
+ def __init__(self, *, z: "Z"): ...
1722
+
1723
+ @overload
1724
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1725
+
1726
+ def __init__(self, *args, **kwargs):
1727
+ super().__init__(*args, **kwargs)
1728
+
1729
+
1730
+ class Z(Model):
1731
+ z_val: datetime.datetime = rest_field(name="zVal")
1732
+
1733
+ @overload
1734
+ def __init__(self, *, z_val: datetime.datetime): ...
1735
+
1736
+ @overload
1737
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1738
+
1739
+ def __init__(self, *args, **kwargs):
1740
+ super().__init__(*args, **kwargs)
1741
+
1742
+
1743
+ def test_nested_update():
1744
+ serialized_datetime = "9999-12-31T23:59:59.999Z"
1745
+ parsed_datetime = isodate.parse_datetime(serialized_datetime)
1746
+ x = X({"y": {"z": {"zVal": serialized_datetime}}})
1747
+ assert x.y.z.z_val == x["y"].z.z_val == x.y["z"].z_val == x["y"]["z"].z_val == parsed_datetime
1748
+ assert x.y.z["zVal"] == x.y["z"]["zVal"] == x["y"].z["zVal"] == x["y"]["z"]["zVal"] == serialized_datetime
1749
+
1750
+
1751
+ def test_deserialization_is():
1752
+ # test without datetime deserialization
1753
+ a = A({"b": {"c": {"d": "hello"}}})
1754
+ assert a.b is a.b
1755
+ assert a.b.c is a.b.c
1756
+ assert a.b.c.d is a.b.c.d
1757
+
1758
+ serialized_datetime = "9999-12-31T23:59:59.999Z"
1759
+ x = X({"y": {"z": {"zVal": serialized_datetime}}})
1760
+ assert x.y is x.y
1761
+ assert x.y.z is x.y.z
1762
+
1763
+ assert x.y.z.z_val == isodate.parse_datetime(serialized_datetime)
1764
+
1765
+
1766
+ class InnerModelWithReadonly(Model):
1767
+ normal_property: str = rest_field(name="normalProperty")
1768
+ readonly_property: str = rest_field(name="readonlyProperty", visibility=["read"])
1769
+
1770
+ @overload
1771
+ def __init__(self, *, normal_property: str): ...
1772
+
1773
+ @overload
1774
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1775
+
1776
+ def __init__(self, *args, **kwargs):
1777
+ super().__init__(*args, **kwargs)
1778
+
1779
+
1780
+ class ModelWithReadonly(Model):
1781
+ normal_property: str = rest_field(name="normalProperty")
1782
+ readonly_property: str = rest_field(name="readonlyProperty", visibility=["read"])
1783
+ inner_model: InnerModelWithReadonly = rest_field(name="innerModel")
1784
+
1785
+ @overload
1786
+ def __init__(self, *, normal_property: str): ...
1787
+
1788
+ @overload
1789
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1790
+
1791
+ def __init__(self, *args, **kwargs):
1792
+ super().__init__(*args, **kwargs)
1793
+
1794
+
1795
+ def test_readonly():
1796
+ # we pass the dict to json, so readonly shouldn't show up in the JSON version
1797
+ value = {
1798
+ "normalProperty": "normal",
1799
+ "readonlyProperty": "readonly",
1800
+ "innerModel": {"normalProperty": "normal", "readonlyProperty": "readonly"},
1801
+ }
1802
+ model = ModelWithReadonly(value)
1803
+ assert model.as_dict(exclude_readonly=True) == {
1804
+ "normalProperty": "normal",
1805
+ "innerModel": {"normalProperty": "normal"},
1806
+ }
1807
+ assert json.loads(json.dumps(model, cls=SdkJSONEncoder)) == value
1808
+ assert model == value
1809
+ assert model["readonlyProperty"] == model.readonly_property == "readonly"
1810
+ assert model["innerModel"]["readonlyProperty"] == model.inner_model.readonly_property == "readonly"
1811
+
1812
+
1813
+ def test_readonly_set():
1814
+ value = {
1815
+ "normalProperty": "normal",
1816
+ "readonlyProperty": "readonly",
1817
+ "innerModel": {"normalProperty": "normal", "readonlyProperty": "readonly"},
1818
+ }
1819
+
1820
+ model = ModelWithReadonly(value)
1821
+ assert model.normal_property == model["normalProperty"] == "normal"
1822
+ assert model.readonly_property == model["readonlyProperty"] == "readonly"
1823
+ assert model.inner_model.normal_property == model.inner_model["normalProperty"] == "normal"
1824
+ assert model.inner_model.readonly_property == model.inner_model["readonlyProperty"] == "readonly"
1825
+
1826
+ assert model.as_dict(exclude_readonly=True) == {
1827
+ "normalProperty": "normal",
1828
+ "innerModel": {"normalProperty": "normal"},
1829
+ }
1830
+ assert json.loads(json.dumps(model, cls=SdkJSONEncoder)) == value
1831
+
1832
+ model["normalProperty"] = "setWithDict"
1833
+ model["readonlyProperty"] = "setWithDict"
1834
+ model.inner_model["normalProperty"] = "setWithDict"
1835
+ model.inner_model["readonlyProperty"] = "setWithDict"
1836
+
1837
+ assert model.normal_property == model["normalProperty"] == "setWithDict"
1838
+ assert model.readonly_property == model["readonlyProperty"] == "setWithDict"
1839
+ assert model.inner_model.normal_property == model.inner_model["normalProperty"] == "setWithDict"
1840
+ assert model.inner_model.readonly_property == model.inner_model["readonlyProperty"] == "setWithDict"
1841
+ assert model.as_dict(exclude_readonly=True) == {
1842
+ "normalProperty": "setWithDict",
1843
+ "innerModel": {"normalProperty": "setWithDict"},
1844
+ }
1845
+ assert json.loads(json.dumps(model, cls=SdkJSONEncoder)) == {
1846
+ "normalProperty": "setWithDict",
1847
+ "readonlyProperty": "setWithDict",
1848
+ "innerModel": {
1849
+ "normalProperty": "setWithDict",
1850
+ "readonlyProperty": "setWithDict",
1851
+ },
1852
+ }
1853
+
1854
+
1855
+ def test_incorrect_initialization():
1856
+ class MyModel(Model):
1857
+ id: int = rest_field()
1858
+ field: str = rest_field()
1859
+
1860
+ @overload
1861
+ def __init__(
1862
+ self,
1863
+ *,
1864
+ id: int,
1865
+ field: str,
1866
+ ): ...
1867
+
1868
+ @overload
1869
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1870
+
1871
+ def __init__(self, *args, **kwargs):
1872
+ super().__init__(*args, **kwargs)
1873
+
1874
+ with pytest.raises(TypeError):
1875
+ MyModel(1, "field")
1876
+
1877
+ with pytest.raises(TypeError):
1878
+ MyModel(id=1, field="field", unknown="me")
1879
+
1880
+
1881
+ def test_serialization_initialization_and_setting():
1882
+ serialized_datetime = "9999-12-31T23:59:59.999000Z"
1883
+ parsed_datetime = isodate.parse_datetime(serialized_datetime)
1884
+
1885
+ # pass in parsed
1886
+ z = Z(z_val=parsed_datetime)
1887
+ assert z.z_val == parsed_datetime
1888
+ assert z["zVal"] == serialized_datetime
1889
+
1890
+ # pass in dict
1891
+ z = Z({"zVal": serialized_datetime})
1892
+ assert z.z_val == parsed_datetime
1893
+ assert z["zVal"] == serialized_datetime
1894
+
1895
+ # assert setting
1896
+ serialized_datetime = "2022-12-31T23:59:59.999000Z"
1897
+ z.z_val = isodate.parse_datetime(serialized_datetime)
1898
+ assert z["zVal"] == serialized_datetime
1899
+
1900
+
1901
+ def test_copy_of_input():
1902
+ class TestModel(Model):
1903
+ data: List[int] = rest_field()
1904
+
1905
+ @overload
1906
+ def __init__(self, *, data: List[int]): ...
1907
+
1908
+ @overload
1909
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1910
+
1911
+ def __init__(self, *args, **kwargs):
1912
+ super().__init__(*args, **kwargs)
1913
+
1914
+ raw = [1, 2, 3]
1915
+ m = TestModel(data=raw)
1916
+ assert not m.data is raw
1917
+ assert m.data == raw
1918
+ raw.append(4)
1919
+ assert m.data == [1, 2, 3]
1920
+
1921
+
1922
+ def test_inner_model_custom_serializer():
1923
+ class InnerModel(Model):
1924
+ prop: str = rest_field(type=lambda x: x[::-1])
1925
+
1926
+ @overload
1927
+ def __init__(self, *, prop: str): ...
1928
+
1929
+ @overload
1930
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1931
+
1932
+ def __init__(self, *args, **kwargs):
1933
+ super().__init__(*args, **kwargs)
1934
+
1935
+ class OuterModel(Model):
1936
+ inner: InnerModel = rest_field()
1937
+
1938
+ @overload
1939
+ def __init__(self, *, inner: InnerModel): ...
1940
+
1941
+ @overload
1942
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1943
+
1944
+ def __init__(self, *args, **kwargs):
1945
+ super().__init__(*args, **kwargs)
1946
+
1947
+ outer = OuterModel({"inner": {"prop": "hello"}})
1948
+ assert outer.inner["prop"] == outer["inner"]["prop"] == "hello"
1949
+ assert outer.inner.prop == outer["inner"].prop == "olleh" # cspell: ignore olleh
1950
+
1951
+
1952
+ def test_default_value():
1953
+ class MyModel(Model):
1954
+ prop_default_str: str = rest_field(name="propDefaultStr", default="hello")
1955
+ prop_optional_str: Optional[str] = rest_field(name="propOptionalStr", default=None)
1956
+ prop_default_int: int = rest_field(name="propDefaultInt", default=1)
1957
+ prop_optional_int: Optional[int] = rest_field(name="propOptionalInt", default=None)
1958
+
1959
+ @overload
1960
+ def __init__(
1961
+ self,
1962
+ *,
1963
+ prop_default_str: str = "hello",
1964
+ prop_optional_str: Optional[str] = "propOptionalStr",
1965
+ prop_default_int: int = 1,
1966
+ prop_optional_int: Optional[int] = None,
1967
+ ): ...
1968
+
1969
+ @overload
1970
+ def __init__(self, mapping: Mapping[str, Any], /): ...
1971
+
1972
+ def __init__(self, *args, **kwargs):
1973
+ super().__init__(*args, **kwargs)
1974
+
1975
+ my_model = MyModel()
1976
+ assert my_model.prop_default_str == my_model["propDefaultStr"] == "hello"
1977
+ assert my_model.prop_optional_str is my_model["propOptionalStr"] is None
1978
+ assert my_model.prop_default_int == my_model["propDefaultInt"] == 1
1979
+ assert my_model.prop_optional_int is my_model["propOptionalInt"] is None
1980
+ assert my_model == {
1981
+ "propDefaultStr": "hello",
1982
+ "propOptionalStr": None,
1983
+ "propDefaultInt": 1,
1984
+ "propOptionalInt": None,
1985
+ }
1986
+
1987
+ my_model = MyModel(prop_default_str="goodbye")
1988
+ assert my_model.prop_default_str == my_model["propDefaultStr"] == "goodbye"
1989
+ assert my_model.prop_optional_str is my_model["propOptionalStr"] is None
1990
+ assert my_model.prop_default_int == my_model["propDefaultInt"] == 1
1991
+ assert my_model.prop_optional_int is my_model["propOptionalInt"] is None
1992
+ assert my_model == {
1993
+ "propDefaultStr": "goodbye",
1994
+ "propOptionalStr": None,
1995
+ "propDefaultInt": 1,
1996
+ "propOptionalInt": None,
1997
+ }
1998
+
1999
+ my_model = MyModel(prop_optional_int=4)
2000
+ assert my_model.prop_default_str == my_model["propDefaultStr"] == "hello"
2001
+ assert my_model.prop_optional_str is my_model["propOptionalStr"] is None
2002
+ assert my_model.prop_default_int == my_model["propDefaultInt"] == 1
2003
+ assert my_model.prop_optional_int == my_model["propOptionalInt"] == 4
2004
+ assert my_model == {
2005
+ "propDefaultStr": "hello",
2006
+ "propOptionalStr": None,
2007
+ "propDefaultInt": 1,
2008
+ "propOptionalInt": 4,
2009
+ }
2010
+
2011
+ my_model = MyModel({"propDefaultInt": 5})
2012
+ assert my_model.prop_default_str == my_model["propDefaultStr"] == "hello"
2013
+ assert my_model.prop_optional_str is my_model["propOptionalStr"] is None
2014
+ assert my_model.prop_default_int == my_model["propDefaultInt"] == 5
2015
+ assert my_model.prop_optional_int is my_model["propOptionalInt"] is None
2016
+ assert my_model == {
2017
+ "propDefaultStr": "hello",
2018
+ "propOptionalStr": None,
2019
+ "propDefaultInt": 5,
2020
+ "propOptionalInt": None,
2021
+ }
2022
+
2023
+
2024
+ def test_pass_models_in_dict():
2025
+ class Inner(Model):
2026
+ str_property: str = rest_field(name="strProperty")
2027
+
2028
+ @overload
2029
+ def __init__(
2030
+ self,
2031
+ *,
2032
+ str_property: str,
2033
+ ): ...
2034
+
2035
+ @overload
2036
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2037
+
2038
+ def __init__(self, *args, **kwargs):
2039
+ super().__init__(*args, **kwargs)
2040
+
2041
+ class Outer(Model):
2042
+ inner_property: Inner = rest_field(name="innerProperty")
2043
+
2044
+ @overload
2045
+ def __init__(
2046
+ self,
2047
+ *,
2048
+ inner_property: Inner,
2049
+ ): ...
2050
+
2051
+ @overload
2052
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2053
+
2054
+ def __init__(self, *args, **kwargs):
2055
+ super().__init__(*args, **kwargs)
2056
+
2057
+ def _tests(model: Outer):
2058
+ assert (
2059
+ {"innerProperty": {"strProperty": "hello"}}
2060
+ == {"innerProperty": Inner(str_property="hello")}
2061
+ == {"innerProperty": Inner({"strProperty": "hello"})}
2062
+ == Outer(inner_property=Inner(str_property="hello"))
2063
+ == Outer(inner_property=Inner({"strProperty": "hello"}))
2064
+ == Outer({"innerProperty": {"strProperty": "hello"}})
2065
+ == Outer({"innerProperty": Inner(str_property="hello")})
2066
+ == Outer({"innerProperty": Inner({"strProperty": "hello"})})
2067
+ == model
2068
+ )
2069
+
2070
+ _tests(Outer(inner_property=Inner(str_property="hello")))
2071
+ _tests(Outer(inner_property=Inner({"strProperty": "hello"})))
2072
+ _tests(Outer({"innerProperty": {"strProperty": "hello"}}))
2073
+ _tests(Outer({"innerProperty": Inner(str_property="hello")}))
2074
+ _tests(Outer({"innerProperty": Inner({"strProperty": "hello"})}))
2075
+
2076
+
2077
+ def test_mutability_list():
2078
+ class Inner(Model):
2079
+ str_property: str = rest_field(name="strProperty")
2080
+
2081
+ @overload
2082
+ def __init__(
2083
+ self,
2084
+ *,
2085
+ str_property: str,
2086
+ ): ...
2087
+
2088
+ @overload
2089
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2090
+
2091
+ def __init__(self, *args, **kwargs):
2092
+ super().__init__(*args, **kwargs)
2093
+
2094
+ class Middle(Model):
2095
+ inner_property: List[Inner] = rest_field(name="innerProperty")
2096
+ prop: str = rest_field()
2097
+
2098
+ @overload
2099
+ def __init__(
2100
+ self,
2101
+ *,
2102
+ inner_property: List[Inner],
2103
+ prop: str,
2104
+ ): ...
2105
+
2106
+ @overload
2107
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2108
+
2109
+ def __init__(self, *args, **kwargs):
2110
+ super().__init__(*args, **kwargs)
2111
+
2112
+ class Outer(Model):
2113
+ middle_property: Middle = rest_field(name="middleProperty")
2114
+
2115
+ @overload
2116
+ def __init__(
2117
+ self,
2118
+ *,
2119
+ middle_property: Model,
2120
+ ): ...
2121
+
2122
+ @overload
2123
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2124
+
2125
+ def __init__(self, *args, **kwargs):
2126
+ super().__init__(*args, **kwargs)
2127
+
2128
+ original_dict = {
2129
+ "middleProperty": {
2130
+ "innerProperty": [{"strProperty": "hello"}],
2131
+ "prop": "original",
2132
+ }
2133
+ }
2134
+ model = Outer(original_dict)
2135
+ assert model is not original_dict
2136
+
2137
+ # set with dict syntax
2138
+ assert model.middle_property is model["middleProperty"]
2139
+ middle_property = model.middle_property
2140
+ middle_property["prop"] = "new"
2141
+ assert model["middleProperty"] is model.middle_property is middle_property
2142
+ assert model["middleProperty"]["prop"] == model.middle_property.prop == "new"
2143
+
2144
+ # set with attr syntax
2145
+ middle_property.prop = "newest"
2146
+ assert model["middleProperty"] is model.middle_property is middle_property
2147
+ assert model["middleProperty"]["prop"] == model.middle_property.prop == "newest"
2148
+
2149
+ # modify innerproperty list
2150
+ assert model["middleProperty"]["innerProperty"][0] is model.middle_property.inner_property[0]
2151
+ assert (
2152
+ model["middleProperty"]["innerProperty"][0]
2153
+ is model.middle_property["innerProperty"][0]
2154
+ is model["middleProperty"].inner_property[0]
2155
+ is model.middle_property.inner_property[0]
2156
+ )
2157
+ inner_property = model["middleProperty"]["innerProperty"][0]
2158
+
2159
+ # set with dict syntax
2160
+ inner_property["strProperty"] = "nihao"
2161
+ assert (
2162
+ model["middleProperty"]["innerProperty"][0]
2163
+ is model.middle_property["innerProperty"][0]
2164
+ is model["middleProperty"].inner_property[0]
2165
+ is model.middle_property.inner_property[0]
2166
+ )
2167
+ assert (
2168
+ model["middleProperty"]["innerProperty"][0]["strProperty"]
2169
+ == model.middle_property["innerProperty"][0]["strProperty"]
2170
+ == model["middleProperty"].inner_property[0]["strProperty"]
2171
+ == model.middle_property.inner_property[0]["strProperty"]
2172
+ == model["middleProperty"]["innerProperty"][0].str_property
2173
+ == model.middle_property["innerProperty"][0].str_property
2174
+ == model["middleProperty"].inner_property[0].str_property
2175
+ == model.middle_property.inner_property[0].str_property
2176
+ == "nihao"
2177
+ )
2178
+
2179
+
2180
+ def test_mutability_dict():
2181
+ class Inner(Model):
2182
+ str_property: str = rest_field(name="strProperty")
2183
+
2184
+ @overload
2185
+ def __init__(
2186
+ self,
2187
+ *,
2188
+ str_property: str,
2189
+ ): ...
2190
+
2191
+ @overload
2192
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2193
+
2194
+ def __init__(self, *args, **kwargs):
2195
+ super().__init__(*args, **kwargs)
2196
+
2197
+ class Middle(Model):
2198
+ inner_property: Dict[str, Inner] = rest_field(name="innerProperty")
2199
+ prop: str = rest_field()
2200
+
2201
+ @overload
2202
+ def __init__(
2203
+ self,
2204
+ *,
2205
+ inner_property: Dict[str, Inner],
2206
+ prop: str,
2207
+ ): ...
2208
+
2209
+ @overload
2210
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2211
+
2212
+ def __init__(self, *args, **kwargs):
2213
+ super().__init__(*args, **kwargs)
2214
+
2215
+ class Outer(Model):
2216
+ middle_property: Middle = rest_field(name="middleProperty")
2217
+
2218
+ @overload
2219
+ def __init__(
2220
+ self,
2221
+ *,
2222
+ middle_property: Model,
2223
+ ): ...
2224
+
2225
+ @overload
2226
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2227
+
2228
+ def __init__(self, *args, **kwargs):
2229
+ super().__init__(*args, **kwargs)
2230
+
2231
+ original_dict = {
2232
+ "middleProperty": {
2233
+ "innerProperty": {"inner": {"strProperty": "hello"}},
2234
+ "prop": "original",
2235
+ }
2236
+ }
2237
+ model = Outer(original_dict)
2238
+ assert model is not original_dict
2239
+
2240
+ # set with dict syntax
2241
+ assert model.middle_property is model["middleProperty"]
2242
+ middle_property = model.middle_property
2243
+ middle_property["prop"] = "new"
2244
+ assert model["middleProperty"] is model.middle_property is middle_property
2245
+ assert (
2246
+ model["middleProperty"]["prop"]
2247
+ == model["middleProperty"].prop
2248
+ == model.middle_property.prop
2249
+ == model.middle_property["prop"]
2250
+ == "new"
2251
+ )
2252
+
2253
+ # set with attr syntax
2254
+ middle_property.prop = "newest"
2255
+ assert model["middleProperty"] is model.middle_property is middle_property
2256
+ assert model["middleProperty"]["prop"] == model.middle_property.prop == "newest"
2257
+
2258
+ # modify innerproperty list
2259
+ assert model["middleProperty"]["innerProperty"]["inner"] is model.middle_property.inner_property["inner"]
2260
+ assert (
2261
+ model["middleProperty"]["innerProperty"]["inner"]
2262
+ is model.middle_property["innerProperty"]["inner"]
2263
+ is model["middleProperty"].inner_property["inner"]
2264
+ is model.middle_property.inner_property["inner"]
2265
+ )
2266
+ inner_property = model["middleProperty"]["innerProperty"]["inner"]
2267
+
2268
+ # set with dict syntax
2269
+ inner_property["strProperty"] = "nihao"
2270
+ assert (
2271
+ model["middleProperty"]["innerProperty"]["inner"]
2272
+ is model.middle_property["innerProperty"]["inner"]
2273
+ is model["middleProperty"].inner_property["inner"]
2274
+ is model.middle_property.inner_property["inner"]
2275
+ )
2276
+ assert (
2277
+ model["middleProperty"]["innerProperty"]["inner"]["strProperty"]
2278
+ == model.middle_property["innerProperty"]["inner"]["strProperty"]
2279
+ == model["middleProperty"].inner_property["inner"]["strProperty"]
2280
+ == model.middle_property.inner_property["inner"]["strProperty"]
2281
+ == model["middleProperty"]["innerProperty"]["inner"].str_property
2282
+ == model.middle_property["innerProperty"]["inner"].str_property
2283
+ == model["middleProperty"].inner_property["inner"].str_property
2284
+ == model.middle_property.inner_property["inner"].str_property
2285
+ == "nihao"
2286
+ )
2287
+
2288
+
2289
+ def test_del_model():
2290
+ class TestModel(Model):
2291
+ x: Optional[int] = rest_field()
2292
+
2293
+ my_dict = {}
2294
+ my_dict["x"] = None
2295
+
2296
+ assert my_dict["x"] is None
2297
+
2298
+ my_model = TestModel({})
2299
+ my_model["x"] = None
2300
+
2301
+ assert my_model["x"] is my_model.x is None
2302
+
2303
+ my_model = TestModel({"x": 7})
2304
+ my_model.x = None
2305
+
2306
+ assert "x" not in my_model
2307
+ assert my_model.x is None
2308
+
2309
+ with pytest.raises(KeyError):
2310
+ del my_model["x"]
2311
+ my_model.x = 8
2312
+
2313
+ del my_model["x"]
2314
+ assert "x" not in my_model
2315
+ assert my_model.x is my_model.get("x") is None
2316
+
2317
+ with pytest.raises(AttributeError):
2318
+ del my_model.x
2319
+ my_model.x = None
2320
+ assert "x" not in my_model
2321
+ assert my_model.x is my_model.get("x") is None
2322
+
2323
+
2324
+ def test_pop_model():
2325
+ class Inner(Model):
2326
+ str_property: str = rest_field(name="strProperty")
2327
+
2328
+ @overload
2329
+ def __init__(
2330
+ self,
2331
+ *,
2332
+ str_property: str,
2333
+ ): ...
2334
+
2335
+ @overload
2336
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2337
+
2338
+ def __init__(self, *args, **kwargs):
2339
+ super().__init__(*args, **kwargs)
2340
+
2341
+ class Middle(Model):
2342
+ inner_property: Dict[str, Inner] = rest_field(name="innerProperty")
2343
+ prop: str = rest_field()
2344
+
2345
+ @overload
2346
+ def __init__(
2347
+ self,
2348
+ *,
2349
+ inner_property: Dict[str, Inner],
2350
+ prop: str,
2351
+ ): ...
2352
+
2353
+ @overload
2354
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2355
+
2356
+ def __init__(self, *args, **kwargs):
2357
+ super().__init__(*args, **kwargs)
2358
+
2359
+ class Outer(Model):
2360
+ middle_property: Middle = rest_field(name="middleProperty")
2361
+
2362
+ @overload
2363
+ def __init__(
2364
+ self,
2365
+ *,
2366
+ middle_property: Model,
2367
+ ): ...
2368
+
2369
+ @overload
2370
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2371
+
2372
+ def __init__(self, *args, **kwargs):
2373
+ super().__init__(*args, **kwargs)
2374
+
2375
+ original_dict = {
2376
+ "middleProperty": {
2377
+ "innerProperty": {"inner": {"strProperty": "hello"}},
2378
+ "prop": "original",
2379
+ }
2380
+ }
2381
+ model_dict = Outer(original_dict) # model we will access with dict syntax
2382
+ model_attr = Outer(original_dict) # model we will access with attr syntax
2383
+
2384
+ assert model_dict is not original_dict is not model_attr
2385
+ assert (
2386
+ original_dict["middleProperty"]["innerProperty"]["inner"].pop("strProperty")
2387
+ == model_dict["middleProperty"]["innerProperty"]["inner"].pop("strProperty")
2388
+ == model_attr.middle_property.inner_property["inner"].pop("strProperty")
2389
+ == "hello"
2390
+ )
2391
+
2392
+ with pytest.raises(KeyError):
2393
+ original_dict["middleProperty"]["innerProperty"]["inner"].pop("strProperty")
2394
+ with pytest.raises(KeyError):
2395
+ model_dict["middleProperty"]["innerProperty"]["inner"].pop("strProperty")
2396
+ with pytest.raises(KeyError):
2397
+ model_attr.middle_property.inner_property["inner"].pop("strProperty")
2398
+
2399
+
2400
+ def test_contains():
2401
+ class ParentA(Model):
2402
+ a_prop: str = rest_field(name="aProp")
2403
+
2404
+ @overload
2405
+ def __init__(
2406
+ self,
2407
+ *,
2408
+ a_prop: str,
2409
+ ): ...
2410
+
2411
+ @overload
2412
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2413
+
2414
+ def __init__(self, *args, **kwargs):
2415
+ super().__init__(*args, **kwargs)
2416
+
2417
+ class ParentB(Model):
2418
+ b_prop: str = rest_field(name="bProp")
2419
+
2420
+ @overload
2421
+ def __init__(
2422
+ self,
2423
+ *,
2424
+ b_prop: str,
2425
+ ): ...
2426
+
2427
+ @overload
2428
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2429
+
2430
+ def __init__(self, *args, **kwargs):
2431
+ super().__init__(*args, **kwargs)
2432
+
2433
+ class ChildC(ParentA, ParentB):
2434
+ c_prop: str = rest_field(name="cProp")
2435
+
2436
+ @overload
2437
+ def __init__(
2438
+ self,
2439
+ *,
2440
+ a_prop: str,
2441
+ b_prop: str,
2442
+ c_prop: str,
2443
+ ): ...
2444
+
2445
+ @overload
2446
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2447
+
2448
+ def __init__(self, *args, **kwargs):
2449
+ super().__init__(*args, **kwargs)
2450
+
2451
+ parent_a_dict = {"aProp": "a"}
2452
+ assert "aProp" in parent_a_dict
2453
+
2454
+ parent_a = ParentA(parent_a_dict)
2455
+ assert "aProp" in parent_a
2456
+ assert not "a_prop" in parent_a
2457
+
2458
+ parent_a.a_prop = None # clear it out
2459
+ assert "aProp" not in parent_a
2460
+
2461
+ parent_b_dict = {"bProp": "b"}
2462
+ assert "bProp" in parent_b_dict
2463
+
2464
+ parent_b = ParentB(parent_b_dict)
2465
+ assert "bProp" in parent_b
2466
+ assert "b_prop" not in parent_b
2467
+
2468
+ parent_b.b_prop = None # clear it out
2469
+ assert "bProp" not in parent_b
2470
+
2471
+ props = ["aProp", "bProp", "cProp"]
2472
+ child_c_dict = {"aProp": "a", "bProp": "b", "cProp": "c"}
2473
+ assert all(p for p in props if p in child_c_dict)
2474
+
2475
+ child_c = ChildC(child_c_dict)
2476
+ assert all(p for p in props if p in child_c)
2477
+ assert not any(p for p in ["a_prop", "b_prop", "c_prop"] if p in child_c)
2478
+
2479
+ child_c.a_prop = None
2480
+ child_c.b_prop = None
2481
+ child_c.c_prop = None
2482
+
2483
+ assert not any(p for p in props if p in child_c)
2484
+
2485
+
2486
+ def test_iter():
2487
+ dict_response = {
2488
+ "platformUpdateDomainCount": 5,
2489
+ "platformFaultDomainCount": 3,
2490
+ "virtualMachines": [],
2491
+ }
2492
+ assert isinstance(iter(dict_response), Iterable)
2493
+ model = BasicResource(dict_response)
2494
+ assert isinstance(iter(model), Iterable)
2495
+
2496
+ assert (
2497
+ list(iter(dict_response))
2498
+ == list(iter(model))
2499
+ == ["platformUpdateDomainCount", "platformFaultDomainCount", "virtualMachines"]
2500
+ )
2501
+
2502
+
2503
+ def test_len():
2504
+ dict_response = {
2505
+ "platformUpdateDomainCount": 5,
2506
+ "platformFaultDomainCount": 3,
2507
+ "virtualMachines": [],
2508
+ }
2509
+ model = BasicResource(dict_response)
2510
+ assert len(dict_response) == len(model) == 3
2511
+
2512
+ dict_response.pop("platformUpdateDomainCount")
2513
+ model.pop("platformUpdateDomainCount")
2514
+ assert len(dict_response) == len(model) == 2
2515
+
2516
+
2517
+ def test_keys():
2518
+ class Inner(Model):
2519
+ str_prop: str = rest_field(name="strProp")
2520
+
2521
+ class Outer(Model):
2522
+ inner_prop: Inner = rest_field(name="innerProp")
2523
+
2524
+ outer_dict = {"innerProp": {"strProp": "hello"}}
2525
+ outer = Outer(outer_dict)
2526
+ assert outer.keys() == outer_dict.keys()
2527
+ outer_dict["newProp"] = "hello"
2528
+ outer["newProp"] = "hello"
2529
+
2530
+ assert outer.keys() == outer_dict.keys()
2531
+
2532
+ outer_dict.pop("newProp")
2533
+ outer.pop("newProp")
2534
+ assert outer_dict.keys() == outer.keys()
2535
+
2536
+
2537
+ def test_values():
2538
+ class Inner(Model):
2539
+ str_prop: str = rest_field(name="strProp")
2540
+
2541
+ class Outer(Model):
2542
+ inner_prop: Inner = rest_field(name="innerProp")
2543
+
2544
+ outer_dict = {"innerProp": {"strProp": "hello"}}
2545
+ outer = Outer(outer_dict)
2546
+
2547
+ assert list(outer.values()) == list(outer_dict.values())
2548
+ assert len(outer.values()) == len(outer_dict.values()) == 1
2549
+ assert list(outer.values())[0]["strProp"] == list(outer_dict.values())[0]["strProp"] == "hello"
2550
+
2551
+ outer_dict["innerProp"]["strProp"] = "goodbye"
2552
+ outer.inner_prop.str_prop = "goodbye"
2553
+
2554
+ assert list(outer.inner_prop.values()) == list(outer_dict["innerProp"].values())
2555
+
2556
+
2557
+ def test_items():
2558
+ class Inner(Model):
2559
+ str_prop: str = rest_field(name="strProp")
2560
+
2561
+ class Outer(Model):
2562
+ inner_prop: Inner = rest_field(name="innerProp")
2563
+
2564
+ outer_dict = {"innerProp": {"strProp": "hello"}}
2565
+ outer = Outer(outer_dict)
2566
+
2567
+ assert list(outer.items()) == list(outer_dict.items())
2568
+
2569
+ outer_dict["innerProp"]["strProp"] = "goodbye"
2570
+ outer.inner_prop.str_prop = "goodbye"
2571
+
2572
+ assert list(outer.inner_prop.items()) == list(outer_dict["innerProp"].items())
2573
+
2574
+ outer_dict["newProp"] = "bonjour"
2575
+ outer["newProp"] = "bonjour"
2576
+
2577
+ assert list(outer.items()) == list(outer_dict.items())
2578
+
2579
+
2580
+ def test_get():
2581
+ class MyModel(Model):
2582
+ prop: str = rest_field()
2583
+ rest_prop: str = rest_field(name="restProp")
2584
+
2585
+ my_dict = {"prop": "hello", "restProp": "bonjour"}
2586
+ my_model = MyModel(my_dict)
2587
+
2588
+ assert my_dict.get("prop") == my_model.get("prop") == "hello"
2589
+ my_dict["prop"] = "nihao"
2590
+ my_model.prop = "nihao"
2591
+
2592
+ assert my_dict.get("prop") == my_model.get("prop") == "nihao"
2593
+
2594
+ my_dict["restProp"] = "buongiorno"
2595
+ my_model.rest_prop = "buongiorno"
2596
+
2597
+ assert my_dict.get("restProp") == my_model.get("restProp") == "buongiorno"
2598
+ assert my_dict.get("rest_prop") is None # attr case should not work here
2599
+
2600
+ my_dict["newProp"] = "i'm new"
2601
+ my_model["newProp"] = "i'm new"
2602
+
2603
+ assert my_dict.get("newProp") == my_model.get("newProp") == "i'm new"
2604
+ assert my_dict.get("nonexistent") is my_model.get("nonexistent") is None
2605
+
2606
+ assert my_dict.get("nonexistent", 0) == my_model.get("nonexistent", 0) == 0
2607
+
2608
+
2609
+ def test_pop():
2610
+ class MyModel(Model):
2611
+ prop: str = rest_field()
2612
+ rest_prop: str = rest_field(name="restProp")
2613
+
2614
+ my_dict = {"prop": "hello", "restProp": "bonjour"}
2615
+ my_model = MyModel(my_dict)
2616
+
2617
+ assert my_dict.pop("prop") == my_model.pop("prop") == "hello"
2618
+ with pytest.raises(KeyError):
2619
+ my_dict.pop("prop")
2620
+ with pytest.raises(KeyError):
2621
+ my_model.pop("prop")
2622
+
2623
+ my_dict["prop"] = "nihao"
2624
+ my_model.prop = "nihao"
2625
+
2626
+ assert my_dict.pop("prop") == my_model.pop("prop") == "nihao"
2627
+
2628
+ with pytest.raises(KeyError):
2629
+ my_dict.pop("prop")
2630
+ with pytest.raises(KeyError):
2631
+ my_model.pop("prop")
2632
+
2633
+ my_dict["restProp"] = "buongiorno"
2634
+ my_model.rest_prop = "buongiorno"
2635
+
2636
+ assert my_dict.pop("restProp") == my_model.pop("restProp") == "buongiorno"
2637
+ with pytest.raises(KeyError):
2638
+ my_dict.pop("rest_prop") # attr case should not work here
2639
+
2640
+ my_dict["newProp"] = "i'm new"
2641
+ my_model["newProp"] = "i'm new"
2642
+
2643
+ assert my_dict.pop("newProp") == my_model.pop("newProp") == "i'm new"
2644
+ assert my_dict.pop("nonexistent", 0) == my_model.pop("nonexistent", 0) == 0
2645
+
2646
+
2647
+ def test_popitem():
2648
+ class ModelA(Model):
2649
+ a_str_prop: str = rest_field(name="aStrProp")
2650
+
2651
+ class ModelB(Model):
2652
+ b_str_prop: str = rest_field(name="bStrProp")
2653
+
2654
+ class ModelC(Model):
2655
+ c_str_prop: str = rest_field(name="cStrProp")
2656
+
2657
+ class MainModel(Model):
2658
+ a_prop: ModelA = rest_field(name="aProp")
2659
+ b_prop: ModelB = rest_field(name="bProp")
2660
+ c_prop: ModelC = rest_field(name="cProp")
2661
+
2662
+ my_dict = {
2663
+ "aProp": {"aStrProp": "a"},
2664
+ "bProp": {"bStrProp": "b"},
2665
+ "cProp": {"cStrProp": "c"},
2666
+ }
2667
+
2668
+ def _tests(my_dict: Dict[str, Any], my_model: MainModel):
2669
+ my_dict = copy.deepcopy(my_dict) # so we don't get rid of the dict each time we run tests
2670
+
2671
+ # pop c prop
2672
+ dict_popitem = my_dict.popitem()
2673
+ model_popitem = my_model.popitem()
2674
+ assert dict_popitem[0] == model_popitem[0] == "cProp"
2675
+ assert dict_popitem[1]["cStrProp"] == model_popitem[1]["cStrProp"] == model_popitem[1].c_str_prop == "c"
2676
+
2677
+ # pop b prop
2678
+ dict_popitem = my_dict.popitem()
2679
+ model_popitem = my_model.popitem()
2680
+ assert dict_popitem[0] == model_popitem[0] == "bProp"
2681
+ assert dict_popitem[1]["bStrProp"] == model_popitem[1]["bStrProp"] == model_popitem[1].b_str_prop == "b"
2682
+
2683
+ # pop a prop
2684
+ dict_popitem = my_dict.popitem()
2685
+ model_popitem = my_model.popitem()
2686
+ assert dict_popitem[0] == model_popitem[0] == "aProp"
2687
+ assert dict_popitem[1]["aStrProp"] == model_popitem[1]["aStrProp"] == model_popitem[1].a_str_prop == "a"
2688
+
2689
+ with pytest.raises(KeyError):
2690
+ my_dict.popitem()
2691
+
2692
+ with pytest.raises(KeyError):
2693
+ my_model.popitem()
2694
+
2695
+ _tests(my_dict, MainModel(my_dict))
2696
+ _tests(
2697
+ my_dict,
2698
+ MainModel(
2699
+ a_prop=ModelA(a_str_prop="a"),
2700
+ b_prop=ModelB(b_str_prop="b"),
2701
+ c_prop=ModelC(c_str_prop="c"),
2702
+ ),
2703
+ )
2704
+
2705
+
2706
+ def test_clear():
2707
+ class ModelA(Model):
2708
+ a_str_prop: str = rest_field(name="aStrProp")
2709
+
2710
+ class ModelB(Model):
2711
+ b_str_prop: str = rest_field(name="bStrProp")
2712
+
2713
+ class ModelC(Model):
2714
+ c_str_prop: str = rest_field(name="cStrProp")
2715
+
2716
+ class MainModel(Model):
2717
+ a_prop: ModelA = rest_field(name="aProp")
2718
+ b_prop: ModelB = rest_field(name="bProp")
2719
+ c_prop: ModelC = rest_field(name="cProp")
2720
+
2721
+ my_dict = {
2722
+ "aProp": {"aStrProp": "a"},
2723
+ "bProp": {"bStrProp": "b"},
2724
+ "cProp": {"cStrProp": "c"},
2725
+ }
2726
+
2727
+ def _tests(my_dict: Dict[str, Any], my_model: MainModel):
2728
+ my_dict = copy.deepcopy(my_dict) # so we don't get rid of the dict each time we run tests
2729
+
2730
+ assert my_dict["aProp"] == my_model.a_prop == my_model["aProp"] == {"aStrProp": "a"}
2731
+ my_dict.clear()
2732
+ my_model.clear()
2733
+ assert my_dict == my_model == {}
2734
+
2735
+ assert my_model.a_prop is None
2736
+ assert my_model.b_prop is None
2737
+ assert my_model.c_prop is None
2738
+
2739
+ my_dict.clear()
2740
+ my_model.clear()
2741
+ assert my_dict == my_model == {}
2742
+
2743
+ _tests(my_dict, MainModel(my_dict))
2744
+ _tests(
2745
+ my_dict,
2746
+ MainModel(
2747
+ a_prop=ModelA(a_str_prop="a"),
2748
+ b_prop=ModelB(b_str_prop="b"),
2749
+ c_prop=ModelC(c_str_prop="c"),
2750
+ ),
2751
+ )
2752
+
2753
+
2754
+ def test_update():
2755
+ class ModelA(Model):
2756
+ a_str_prop: str = rest_field(name="aStrProp")
2757
+
2758
+ class ModelB(Model):
2759
+ b_str_prop: str = rest_field(name="bStrProp")
2760
+
2761
+ class ModelC(Model):
2762
+ c_str_prop: str = rest_field(name="cStrProp")
2763
+
2764
+ class MainModel(Model):
2765
+ a_prop: ModelA = rest_field(name="aProp")
2766
+ b_prop: ModelB = rest_field(name="bProp")
2767
+ c_prop: ModelC = rest_field(name="cProp")
2768
+
2769
+ my_dict = {
2770
+ "aProp": {"aStrProp": "a"},
2771
+ "bProp": {"bStrProp": "b"},
2772
+ "cProp": {"cStrProp": "c"},
2773
+ }
2774
+
2775
+ def _tests(my_dict: Dict[str, Any], my_model: MainModel):
2776
+ my_dict = copy.deepcopy(my_dict) # so we don't get rid of the dict each time we run tests
2777
+
2778
+ assert my_dict["aProp"] == my_model.a_prop == my_model["aProp"] == {"aStrProp": "a"}
2779
+ my_dict.update({"aProp": {"aStrProp": "newA"}})
2780
+ my_model.a_prop.update({"aStrProp": "newA"})
2781
+ assert my_dict["aProp"] == my_model.a_prop == my_model["aProp"] == {"aStrProp": "newA"}
2782
+
2783
+ my_dict["bProp"].update({"newBProp": "hello"})
2784
+ my_model.b_prop.update({"newBProp": "hello"})
2785
+
2786
+ assert my_dict["bProp"] == my_model.b_prop == my_model["bProp"] == {"bStrProp": "b", "newBProp": "hello"}
2787
+
2788
+ my_dict.update({"dProp": "hello"})
2789
+ my_model.update({"dProp": "hello"})
2790
+
2791
+ assert my_dict["dProp"] == my_model["dProp"] == "hello"
2792
+
2793
+ _tests(my_dict, MainModel(my_dict))
2794
+ _tests(
2795
+ my_dict,
2796
+ MainModel(
2797
+ a_prop=ModelA(a_str_prop="a"),
2798
+ b_prop=ModelB(b_str_prop="b"),
2799
+ c_prop=ModelC(c_str_prop="c"),
2800
+ ),
2801
+ )
2802
+
2803
+
2804
+ def test_setdefault():
2805
+ class Inner(Model):
2806
+ str_prop: str = rest_field(name="strProp", default="modelDefault")
2807
+
2808
+ class Outer(Model):
2809
+ inner_prop: Inner = rest_field(name="innerProp")
2810
+
2811
+ og_dict = {"innerProp": {}}
2812
+ og_dict["innerProp"].setdefault("strProp", "actualDefault")
2813
+ og_model = Outer(og_dict)
2814
+ og_model.inner_prop.setdefault("strProp", "actualDefault")
2815
+
2816
+ assert og_dict["innerProp"] == og_model["innerProp"] == og_model.inner_prop == {"strProp": "actualDefault"}
2817
+
2818
+ assert (
2819
+ og_dict["innerProp"].setdefault("strProp")
2820
+ == og_model["innerProp"].setdefault("strProp")
2821
+ == og_model.inner_prop.setdefault("strProp")
2822
+ == "actualDefault"
2823
+ )
2824
+
2825
+ assert og_dict.setdefault("newProp") is og_model.setdefault("newProp") is None
2826
+ assert og_dict["newProp"] is og_model["newProp"] is None
2827
+
2828
+
2829
+ def test_repr():
2830
+ class ModelA(Model):
2831
+ a_str_prop: str = rest_field(name="aStrProp")
2832
+
2833
+ class ModelB(Model):
2834
+ b_str_prop: str = rest_field(name="bStrProp")
2835
+
2836
+ class ModelC(Model):
2837
+ c_str_prop: str = rest_field(name="cStrProp")
2838
+
2839
+ class MainModel(Model):
2840
+ a_prop: ModelA = rest_field(name="aProp")
2841
+ b_prop: ModelB = rest_field(name="bProp")
2842
+ c_prop: ModelC = rest_field(name="cProp")
2843
+
2844
+ my_dict = {
2845
+ "aProp": {"aStrProp": "a"},
2846
+ "bProp": {"bStrProp": "b"},
2847
+ "cProp": {"cStrProp": "c"},
2848
+ }
2849
+
2850
+ assert repr(my_dict) == repr(MainModel(my_dict))
2851
+
2852
+
2853
+ ##### REWRITE BODY COMPLEX INTO THIS FILE #####
2854
+
2855
+
2856
+ def test_complex_basic():
2857
+ class Basic(Model):
2858
+ id: Optional[int] = rest_field(default=None)
2859
+ name: Optional[str] = rest_field(default=None)
2860
+ color: Optional[Literal["cyan", "Magenta", "YELLOW", "blacK"]] = rest_field(default=None)
2861
+
2862
+ @overload
2863
+ def __init__(
2864
+ self,
2865
+ *,
2866
+ id: Optional[int] = None,
2867
+ name: Optional[str] = None,
2868
+ color: Optional[Literal["cyan", "Magenta", "YELLOW", "blacK"]] = None,
2869
+ ): ...
2870
+
2871
+ @overload
2872
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2873
+
2874
+ def __init__(self, *args, **kwargs):
2875
+ super().__init__(*args, **kwargs)
2876
+
2877
+ basic = Basic(id=2, name="abc", color="Magenta")
2878
+ assert basic == {"id": 2, "name": "abc", "color": "Magenta"}
2879
+
2880
+ basic.id = 3
2881
+ basic.name = "new_name"
2882
+ basic.color = "blacK"
2883
+
2884
+ assert basic == {"id": 3, "name": "new_name", "color": "blacK"}
2885
+
2886
+ basic["id"] = 4
2887
+ basic["name"] = "newest_name"
2888
+ basic["color"] = "YELLOW"
2889
+
2890
+ assert basic == {"id": 4, "name": "newest_name", "color": "YELLOW"}
2891
+
2892
+
2893
+ def test_complex_boolean_wrapper():
2894
+ class BooleanWrapper(Model):
2895
+ field_true: Optional[bool] = rest_field(default=None)
2896
+ field_false: Optional[bool] = rest_field(default=None)
2897
+
2898
+ @overload
2899
+ def __init__(
2900
+ self,
2901
+ *,
2902
+ field_true: Optional[bool] = None,
2903
+ field_false: Optional[bool] = None,
2904
+ ): ...
2905
+
2906
+ @overload
2907
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2908
+
2909
+ def __init__(self, *args, **kwargs):
2910
+ super().__init__(*args, **kwargs)
2911
+
2912
+ bool_model = BooleanWrapper(field_true=True, field_false=False)
2913
+ assert bool_model == {"field_true": True, "field_false": False}
2914
+ bool_model.field_true = False
2915
+ bool_model.field_false = True
2916
+ assert bool_model == {"field_true": False, "field_false": True}
2917
+
2918
+ bool_model["field_true"] = True
2919
+ bool_model["field_false"] = False
2920
+ assert bool_model == {"field_true": True, "field_false": False}
2921
+
2922
+
2923
+ def test_complex_byte_wrapper():
2924
+ class ByteWrapper(Model):
2925
+ default: Optional[bytes] = rest_field(default=None)
2926
+ base64: Optional[bytes] = rest_field(default=None, format="base64")
2927
+ base64url: Optional[bytes] = rest_field(default=None, format="base64url")
2928
+ list_base64: Optional[List[bytes]] = rest_field(default=None, format="base64")
2929
+ map_base64url: Optional[Dict[str, bytes]] = rest_field(default=None, format="base64url")
2930
+
2931
+ @overload
2932
+ def __init__(
2933
+ self,
2934
+ *,
2935
+ default: Optional[bytes] = None,
2936
+ base64: Optional[bytes] = None,
2937
+ base64url: Optional[bytes] = None,
2938
+ list_base64: Optional[List[bytes]] = None,
2939
+ map_base64url: Optional[Dict[str, bytes]] = None,
2940
+ ): ...
2941
+
2942
+ @overload
2943
+ def __init__(self, mapping: Mapping[str, Any], /): ...
2944
+
2945
+ def __init__(self, *args, **kwargs):
2946
+ super().__init__(*args, **kwargs)
2947
+
2948
+ byte_string = bytes("test", "utf-8")
2949
+ mod = ByteWrapper(
2950
+ default=byte_string,
2951
+ base64=byte_string,
2952
+ base64url=byte_string,
2953
+ list_base64=[byte_string, byte_string],
2954
+ map_base64url={"key1": byte_string, "key2": byte_string},
2955
+ )
2956
+ decoded = "dGVzdA=="
2957
+ decoded_urlsafe = "dGVzdA"
2958
+
2959
+ def _tests(mod: ByteWrapper):
2960
+ assert mod == {
2961
+ "default": decoded,
2962
+ "base64": decoded,
2963
+ "base64url": decoded_urlsafe,
2964
+ "list_base64": [decoded, decoded],
2965
+ "map_base64url": {"key1": decoded_urlsafe, "key2": decoded_urlsafe},
2966
+ }
2967
+ assert mod.default == byte_string
2968
+ assert mod.base64 == byte_string
2969
+ assert mod.base64url == byte_string
2970
+ assert mod.list_base64 == [byte_string, byte_string]
2971
+ assert mod.map_base64url == {"key1": byte_string, "key2": byte_string}
2972
+ assert mod["default"] == decoded
2973
+ assert mod["base64"] == decoded
2974
+ assert mod["base64url"] == decoded_urlsafe
2975
+ assert mod["list_base64"] == [decoded, decoded]
2976
+ assert mod["map_base64url"] == {
2977
+ "key1": decoded_urlsafe,
2978
+ "key2": decoded_urlsafe,
2979
+ }
2980
+
2981
+ _tests(mod)
2982
+ mod.default = byte_string
2983
+ mod.base64 = byte_string
2984
+ mod.base64url = byte_string
2985
+ mod.list_base64 = [byte_string, byte_string]
2986
+ mod.map_base64url = {"key1": byte_string, "key2": byte_string}
2987
+ _tests(mod)
2988
+ mod["default"] = decoded
2989
+ mod["base64"] = decoded
2990
+ mod["base64url"] = decoded_urlsafe
2991
+ mod["list_base64"] = [decoded, decoded]
2992
+ mod["map_base64url"] = {"key1": decoded_urlsafe, "key2": decoded_urlsafe}
2993
+ _tests(mod)
2994
+
2995
+
2996
+ def test_complex_byte_array_wrapper():
2997
+ class ByteArrayWrapper(Model):
2998
+ default: Optional[bytearray] = rest_field(default=None)
2999
+ base64: Optional[bytearray] = rest_field(default=None, format="base64")
3000
+ base64url: Optional[bytearray] = rest_field(default=None, format="base64url")
3001
+ list_base64: Optional[List[bytearray]] = rest_field(default=None, format="base64")
3002
+ map_base64url: Optional[Dict[str, bytearray]] = rest_field(default=None, format="base64url")
3003
+
3004
+ @overload
3005
+ def __init__(
3006
+ self,
3007
+ *,
3008
+ default: Optional[bytearray] = None,
3009
+ base64: Optional[bytearray] = None,
3010
+ base64url: Optional[bytearray] = None,
3011
+ list_base64: Optional[List[bytearray]] = None,
3012
+ map_base64url: Optional[Dict[str, bytearray]] = None,
3013
+ ): ...
3014
+
3015
+ @overload
3016
+ def __init__(self, mapping: Mapping[str, Any], /): ...
3017
+
3018
+ def __init__(self, *args, **kwargs):
3019
+ super().__init__(*args, **kwargs)
3020
+
3021
+ byte_array = bytearray("test".encode("utf-8"))
3022
+ decoded = "dGVzdA=="
3023
+ decoded_urlsafe = "dGVzdA"
3024
+
3025
+ def _tests(model: ByteArrayWrapper):
3026
+ assert model == {
3027
+ "default": decoded,
3028
+ "base64": decoded,
3029
+ "base64url": decoded_urlsafe,
3030
+ "list_base64": [decoded, decoded],
3031
+ "map_base64url": {"key1": decoded_urlsafe, "key2": decoded_urlsafe},
3032
+ }
3033
+ assert model.default == byte_array
3034
+ assert model.base64 == byte_array
3035
+ assert model.base64url == byte_array
3036
+ assert model.list_base64 == [byte_array, byte_array]
3037
+ assert model.map_base64url == {"key1": byte_array, "key2": byte_array}
3038
+ assert model["default"] == decoded
3039
+ assert model["base64"] == decoded
3040
+ assert model["base64url"] == decoded_urlsafe
3041
+ assert model["list_base64"] == [decoded, decoded]
3042
+ assert model["map_base64url"] == {
3043
+ "key1": decoded_urlsafe,
3044
+ "key2": decoded_urlsafe,
3045
+ }
3046
+
3047
+ _tests(
3048
+ ByteArrayWrapper(
3049
+ default=byte_array,
3050
+ base64=byte_array,
3051
+ base64url=byte_array,
3052
+ list_base64=[byte_array, byte_array],
3053
+ map_base64url={"key1": byte_array, "key2": byte_array},
3054
+ )
3055
+ )
3056
+ _tests(
3057
+ ByteArrayWrapper(
3058
+ {
3059
+ "default": decoded,
3060
+ "base64": decoded,
3061
+ "base64url": decoded_urlsafe,
3062
+ "list_base64": [decoded, decoded],
3063
+ "map_base64url": {"key1": decoded_urlsafe, "key2": decoded_urlsafe},
3064
+ }
3065
+ )
3066
+ )
3067
+
3068
+
3069
+ def test_complex_datetime_wrapper():
3070
+ class DatetimeWrapper(Model):
3071
+ default: datetime.datetime = rest_field(default=None)
3072
+ rfc3339: datetime.datetime = rest_field(default=None, format="rfc3339")
3073
+ rfc7231: datetime.datetime = rest_field(default=None, format="rfc7231")
3074
+ unix: datetime.datetime = rest_field(default=None, format="unix-timestamp")
3075
+ list_rfc3339: List[datetime.datetime] = rest_field(default=None, format="rfc3339")
3076
+ dict_rfc7231: Dict[str, datetime.datetime] = rest_field(default=None, format="rfc7231")
3077
+
3078
+ @overload
3079
+ def __init__(
3080
+ self,
3081
+ *,
3082
+ default: Optional[datetime.datetime] = None,
3083
+ rfc3339: Optional[datetime.datetime] = None,
3084
+ rfc7231: Optional[datetime.datetime] = None,
3085
+ unix: Optional[datetime.datetime] = None,
3086
+ list_rfc3339: Optional[List[datetime.datetime]] = None,
3087
+ dict_rfc7231: Optional[Dict[str, datetime.datetime]] = None,
3088
+ ): ...
3089
+
3090
+ @overload
3091
+ def __init__(self, mapping: Mapping[str, Any], /): ...
3092
+
3093
+ def __init__(self, *args, **kwargs):
3094
+ super().__init__(*args, **kwargs)
3095
+
3096
+ rfc3339 = "2023-06-27T06:11:09Z"
3097
+ rfc7231 = "Tue, 27 Jun 2023 06:11:09 GMT"
3098
+ unix = 1687846269
3099
+ dt = datetime.datetime(2023, 6, 27, 6, 11, 9, tzinfo=datetime.timezone.utc)
3100
+
3101
+ def _tests(model: DatetimeWrapper):
3102
+ assert model["default"] == rfc3339
3103
+ assert model["rfc3339"] == rfc3339
3104
+ assert model["rfc7231"] == rfc7231
3105
+ assert model["unix"] == unix
3106
+ assert model["list_rfc3339"] == [rfc3339, rfc3339]
3107
+ assert model["dict_rfc7231"] == {"key1": rfc7231, "key2": rfc7231}
3108
+ assert model.default == model.rfc3339 == model.rfc7231 == model.unix == dt
3109
+ assert model.list_rfc3339 == [dt, dt]
3110
+ assert model.dict_rfc7231 == {"key1": dt, "key2": dt}
3111
+
3112
+ _tests(
3113
+ DatetimeWrapper(
3114
+ default=dt,
3115
+ rfc3339=dt,
3116
+ rfc7231=dt,
3117
+ unix=dt,
3118
+ list_rfc3339=[dt, dt],
3119
+ dict_rfc7231={"key1": dt, "key2": dt},
3120
+ )
3121
+ )
3122
+ _tests(
3123
+ DatetimeWrapper(
3124
+ {
3125
+ "default": rfc3339,
3126
+ "rfc3339": rfc3339,
3127
+ "rfc7231": rfc7231,
3128
+ "unix": unix,
3129
+ "list_rfc3339": [rfc3339, rfc3339],
3130
+ "dict_rfc7231": {"key1": rfc7231, "key2": rfc7231},
3131
+ }
3132
+ )
3133
+ )
3134
+
3135
+
3136
+ def test_complex_date_wrapper():
3137
+ class DateWrapper(Model):
3138
+ field: datetime.date = rest_field(default=None)
3139
+ leap: datetime.date = rest_field(default=None)
3140
+
3141
+ @overload
3142
+ def __init__(
3143
+ self,
3144
+ *,
3145
+ field: Optional[datetime.date] = None,
3146
+ leap: Optional[datetime.date] = None,
3147
+ ): ...
3148
+
3149
+ @overload
3150
+ def __init__(self, mapping: Mapping[str, Any], /): ...
3151
+
3152
+ def __init__(self, *args, **kwargs):
3153
+ super().__init__(*args, **kwargs)
3154
+
3155
+ field = "0001-01-01"
3156
+ leap = "2016-02-29"
3157
+
3158
+ def _tests(model: DateWrapper):
3159
+ assert model.field == isodate.parse_date(field)
3160
+ assert model["field"] == field
3161
+
3162
+ assert model.leap == isodate.parse_date(leap)
3163
+ assert model["leap"] == leap
3164
+
3165
+ model.field = isodate.parse_date(leap)
3166
+ assert model.field == isodate.parse_date(leap)
3167
+ assert model["field"] == leap
3168
+
3169
+ model["field"] = field
3170
+ assert model.field == isodate.parse_date(field)
3171
+ assert model["field"] == field
3172
+
3173
+ _tests(DateWrapper({"field": field, "leap": leap}))
3174
+ _tests(DateWrapper(field=isodate.parse_date(field), leap=isodate.parse_date(leap)))
3175
+
3176
+
3177
+ class DictionaryWrapper(Model):
3178
+ default_program: Dict[str, str] = rest_field(name="defaultProgram", default=None)
3179
+
3180
+ @overload
3181
+ def __init__(
3182
+ self,
3183
+ *,
3184
+ default_program: Optional[Dict[str, str]] = None,
3185
+ ): ...
3186
+
3187
+ @overload
3188
+ def __init__(self, mapping: Mapping[str, Any], /): ...
3189
+
3190
+ def __init__(self, *args, **kwargs):
3191
+ super().__init__(*args, **kwargs)
3192
+
3193
+
3194
+ default_program = {
3195
+ "txt": "notepad",
3196
+ "bmp": "mspaint",
3197
+ "xls": "excel",
3198
+ "exe": "",
3199
+ "": None,
3200
+ }
3201
+
3202
+
3203
+ @pytest.mark.parametrize(
3204
+ "model",
3205
+ [
3206
+ DictionaryWrapper({"defaultProgram": default_program}),
3207
+ DictionaryWrapper(default_program=default_program),
3208
+ ],
3209
+ )
3210
+ def test_complex_dictionary_wrapper(model: DictionaryWrapper):
3211
+ assert model == {"defaultProgram": default_program}
3212
+ assert model.default_program == model["defaultProgram"] == default_program
3213
+
3214
+
3215
+ @pytest.mark.parametrize(
3216
+ "model",
3217
+ [DictionaryWrapper({"defaultProgram": {}}), DictionaryWrapper(default_program={})],
3218
+ )
3219
+ def test_complex_dictionary_wrapper_empty(model: DictionaryWrapper):
3220
+ assert model == {"defaultProgram": {}}
3221
+ assert model.default_program == model["defaultProgram"] == {}
3222
+
3223
+
3224
+ @pytest.mark.parametrize(
3225
+ "model",
3226
+ [
3227
+ DictionaryWrapper({"defaultProgram": None}),
3228
+ DictionaryWrapper(default_program=None),
3229
+ ],
3230
+ )
3231
+ def test_complex_dictionary_wrapper_none(model: DictionaryWrapper):
3232
+ assert model == {"defaultProgram": None}
3233
+ assert model.default_program is None
3234
+
3235
+
3236
+ class ArrayWrapper(Model):
3237
+ array: Optional[List[str]] = rest_field(default=None)
3238
+
3239
+ @overload
3240
+ def __init__(
3241
+ self,
3242
+ *,
3243
+ array: Optional[List[str]] = None,
3244
+ ): ...
3245
+
3246
+ @overload
3247
+ def __init__(self, mapping: Mapping[str, Any], /): ...
3248
+
3249
+ def __init__(self, *args, **kwargs):
3250
+ super().__init__(*args, **kwargs)
3251
+
3252
+
3253
+ array_value = [
3254
+ "1, 2, 3, 4",
3255
+ "",
3256
+ None,
3257
+ "&S#$(*Y",
3258
+ "The quick brown fox jumps over the lazy dog",
3259
+ ]
3260
+
3261
+
3262
+ @pytest.mark.parametrize("model", [ArrayWrapper(array=array_value), ArrayWrapper({"array": array_value})])
3263
+ def test_complex_array_wrapper(model: ArrayWrapper):
3264
+ assert model == {"array": array_value}
3265
+ assert model.array == model["array"] == array_value
3266
+
3267
+ model.array = None
3268
+ with pytest.raises(KeyError):
3269
+ model["array"]
3270
+ assert model.array is None
3271
+
3272
+ model["array"] = [1, 2, 3, 4, 5]
3273
+ assert model.array == ["1", "2", "3", "4", "5"]
3274
+ assert model["array"] == [1, 2, 3, 4, 5]
3275
+
3276
+
3277
+ @pytest.mark.parametrize("model", [ArrayWrapper(array=[]), ArrayWrapper({"array": []})])
3278
+ def test_complex_array_wrapper_empty(model: ArrayWrapper):
3279
+ assert model == {"array": []}
3280
+ assert model.array == model["array"] == []
3281
+
3282
+ model.array = ["bonjour"]
3283
+ assert model.array == model["array"] == ["bonjour"]
3284
+
3285
+
3286
+ @pytest.mark.parametrize("model", [ArrayWrapper(array=None), ArrayWrapper({"array": None})])
3287
+ def test_complex_array_wrapper_none(model: ArrayWrapper):
3288
+ assert model == {"array": None}
3289
+ assert model.array is model["array"] is None
3290
+
3291
+ model.array = ["bonjour"]
3292
+ assert model.array == model["array"] == ["bonjour"]
3293
+
3294
+
3295
+ class PetComplex(Model):
3296
+ id: Optional[int] = rest_field(default=None)
3297
+ name: Optional[str] = rest_field(default=None)
3298
+
3299
+ @overload
3300
+ def __init__(
3301
+ self,
3302
+ *,
3303
+ id: Optional[int] = None,
3304
+ name: Optional[str] = None,
3305
+ ): ...
3306
+
3307
+ @overload
3308
+ def __init__(self, mapping: Mapping[str, Any], /): ...
3309
+
3310
+ def __init__(self, *args, **kwargs):
3311
+ super().__init__(*args, **kwargs)
3312
+
3313
+
3314
+ class DogComplex(PetComplex):
3315
+ food: Optional[str] = rest_field(default=None)
3316
+
3317
+ @overload
3318
+ def __init__(
3319
+ self,
3320
+ *,
3321
+ id: Optional[int] = None,
3322
+ name: Optional[str] = None,
3323
+ food: Optional[str] = None,
3324
+ ): ...
3325
+
3326
+ @overload
3327
+ def __init__(self, mapping: Mapping[str, Any], /): ...
3328
+
3329
+ def __init__(self, *args, **kwargs):
3330
+ super().__init__(*args, **kwargs)
3331
+
3332
+
3333
+ class CatComplex(PetComplex):
3334
+ color: Optional[str] = rest_field(default=None)
3335
+ hates: Optional[List[DogComplex]] = rest_field(default=None)
3336
+
3337
+ @overload
3338
+ def __init__(
3339
+ self,
3340
+ *,
3341
+ id: Optional[int] = None,
3342
+ name: Optional[str] = None,
3343
+ food: Optional[str] = None,
3344
+ color: Optional[str] = None,
3345
+ hates: Optional[List[DogComplex]] = None,
3346
+ ): ...
3347
+
3348
+ @overload
3349
+ def __init__(self, mapping: Mapping[str, Any], /): ...
3350
+
3351
+ def __init__(self, *args, **kwargs):
3352
+ super().__init__(*args, **kwargs)
3353
+
3354
+
3355
+ @pytest.mark.parametrize(
3356
+ "model",
3357
+ [
3358
+ CatComplex(
3359
+ id=2,
3360
+ name="Siamese",
3361
+ hates=[
3362
+ DogComplex(id=1, name="Potato", food="tomato"),
3363
+ DogComplex(id=-1, name="Tomato", food="french fries"),
3364
+ ],
3365
+ ),
3366
+ CatComplex(
3367
+ id=2,
3368
+ name="Siamese",
3369
+ hates=[
3370
+ DogComplex(id=1, name="Potato", food="tomato"),
3371
+ {"id": -1, "name": "Tomato", "food": "french fries"},
3372
+ ],
3373
+ ),
3374
+ CatComplex(
3375
+ id=2,
3376
+ name="Siamese",
3377
+ hates=[
3378
+ {"id": 1, "name": "Potato", "food": "tomato"},
3379
+ {"id": -1, "name": "Tomato", "food": "french fries"},
3380
+ ],
3381
+ ),
3382
+ ],
3383
+ )
3384
+ def test_complex_inheritance(model):
3385
+ assert model.id == model["id"] == 2
3386
+ assert model.name == model["name"] == "Siamese"
3387
+ assert model.hates
3388
+ assert model.hates[1] == model["hates"][1] == {"id": -1, "name": "Tomato", "food": "french fries"}
3389
+ model["breed"] = "persian"
3390
+ model["color"] = "green"
3391
+ with pytest.raises(AttributeError):
3392
+ model.breed
3393
+ assert model == {
3394
+ "id": 2,
3395
+ "name": "Siamese",
3396
+ "color": "green",
3397
+ "breed": "persian",
3398
+ "hates": [
3399
+ DogComplex(id=1, name="Potato", food="tomato"),
3400
+ DogComplex(id=-1, name="Tomato", food="french fries"),
3401
+ ],
3402
+ }
3403
+
3404
+
3405
+ def test_required_prop_not_passed():
3406
+ class ModelWithRequiredProperty(Model):
3407
+ required_property: int = rest_field(name="requiredProperty")
3408
+
3409
+ @overload
3410
+ def __init__(
3411
+ self,
3412
+ *,
3413
+ required_property: int,
3414
+ ): ...
3415
+
3416
+ @overload
3417
+ def __init__(self, mapping: Mapping[str, Any], /): ...
3418
+
3419
+ def __init__(self, *args, **kwargs):
3420
+ super().__init__(*args, **kwargs)
3421
+
3422
+ model = ModelWithRequiredProperty()
3423
+ assert model.required_property is None
3424
+ with pytest.raises(KeyError):
3425
+ model["requiredProperty"]
3426
+
3427
+ model = ModelWithRequiredProperty({})
3428
+ assert model.required_property is None
3429
+ with pytest.raises(KeyError):
3430
+ model["requiredProperty"]
3431
+
3432
+
3433
+ def test_null_serialization(core_library):
3434
+ dict_response = {
3435
+ "name": "it's me!",
3436
+ "listOfMe": [
3437
+ {
3438
+ "name": "it's me!",
3439
+ }
3440
+ ],
3441
+ "dictOfMe": {
3442
+ "me": {
3443
+ "name": "it's me!",
3444
+ }
3445
+ },
3446
+ "dictOfListOfMe": {
3447
+ "many mes": [
3448
+ {
3449
+ "name": "it's me!",
3450
+ }
3451
+ ]
3452
+ },
3453
+ "listOfDictOfMe": None,
3454
+ }
3455
+ model = RecursiveModel(dict_response)
3456
+ assert json.loads(json.dumps(model, cls=SdkJSONEncoder)) == dict_response
3457
+
3458
+ assert model.as_dict() == dict_response
3459
+
3460
+ model.list_of_me = core_library.serialization.NULL
3461
+ model.dict_of_me = None
3462
+ model.list_of_dict_of_me = [
3463
+ {
3464
+ "me": {
3465
+ "name": "it's me!",
3466
+ }
3467
+ }
3468
+ ]
3469
+ model.dict_of_list_of_me["many mes"][0].list_of_me = core_library.serialization.NULL
3470
+ model.dict_of_list_of_me["many mes"][0].dict_of_me = None
3471
+ model.list_of_dict_of_me[0]["me"].list_of_me = core_library.serialization.NULL
3472
+ model.list_of_dict_of_me[0]["me"].dict_of_me = None
3473
+
3474
+ assert json.loads(json.dumps(model, cls=SdkJSONEncoder)) == {
3475
+ "name": "it's me!",
3476
+ "listOfMe": None,
3477
+ "dictOfListOfMe": {
3478
+ "many mes": [
3479
+ {
3480
+ "name": "it's me!",
3481
+ "listOfMe": None,
3482
+ }
3483
+ ]
3484
+ },
3485
+ "listOfDictOfMe": [
3486
+ {
3487
+ "me": {
3488
+ "name": "it's me!",
3489
+ "listOfMe": None,
3490
+ }
3491
+ }
3492
+ ],
3493
+ }
3494
+
3495
+ assert model.as_dict() == {
3496
+ "name": "it's me!",
3497
+ "listOfMe": None,
3498
+ "dictOfListOfMe": {
3499
+ "many mes": [
3500
+ {
3501
+ "name": "it's me!",
3502
+ "listOfMe": None,
3503
+ }
3504
+ ]
3505
+ },
3506
+ "listOfDictOfMe": [
3507
+ {
3508
+ "me": {
3509
+ "name": "it's me!",
3510
+ "listOfMe": None,
3511
+ }
3512
+ }
3513
+ ],
3514
+ }
3515
+
3516
+
3517
+ class UnionBaseModel(Model):
3518
+ name: str = rest_field()
3519
+
3520
+ @overload
3521
+ def __init__(self, *, name: str): ...
3522
+
3523
+ @overload
3524
+ def __init__(self, mapping: Mapping[str, Any]): ...
3525
+
3526
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
3527
+ super().__init__(*args, **kwargs)
3528
+
3529
+
3530
+ class UnionModel1(UnionBaseModel):
3531
+ prop1: int = rest_field()
3532
+
3533
+ @overload
3534
+ def __init__(self, *, name: str, prop1: int): ...
3535
+
3536
+ @overload
3537
+ def __init__(self, mapping: Mapping[str, Any]): ...
3538
+
3539
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
3540
+ super().__init__(*args, **kwargs)
3541
+
3542
+
3543
+ class UnionModel2(UnionBaseModel):
3544
+ prop2: int = rest_field()
3545
+
3546
+ @overload
3547
+ def __init__(self, *, name: str, prop2: int): ...
3548
+
3549
+ @overload
3550
+ def __init__(self, mapping: Mapping[str, Any]): ...
3551
+
3552
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
3553
+ super().__init__(*args, **kwargs)
3554
+
3555
+
3556
+ MyNamedUnion = Union["UnionModel1", "UnionModel2"]
3557
+
3558
+
3559
+ class ModelWithNamedUnionProperty(Model):
3560
+ named_union: "MyNamedUnion" = rest_field(name="namedUnion")
3561
+
3562
+ @overload
3563
+ def __init__(self, *, named_union: "MyNamedUnion"): ...
3564
+
3565
+ @overload
3566
+ def __init__(self, mapping: Mapping[str, Any]): ...
3567
+
3568
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
3569
+ super().__init__(*args, **kwargs)
3570
+
3571
+
3572
+ class ModelWithSimpleUnionProperty(Model):
3573
+ simple_union: Union[int, List[int]] = rest_field(name="simpleUnion")
3574
+
3575
+ @overload
3576
+ def __init__(self, *, simple_union: Union[int, List[int]]): ...
3577
+
3578
+ @overload
3579
+ def __init__(self, mapping: Mapping[str, Any]): ...
3580
+
3581
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
3582
+ super().__init__(*args, **kwargs)
3583
+
3584
+
3585
+ def test_union():
3586
+ simple = ModelWithSimpleUnionProperty(simple_union=1)
3587
+ assert simple.simple_union == simple["simpleUnion"] == 1
3588
+ simple = ModelWithSimpleUnionProperty(simple_union=[1, 2])
3589
+ assert simple.simple_union == simple["simpleUnion"] == [1, 2]
3590
+ named = ModelWithNamedUnionProperty()
3591
+ assert not _is_model(named.named_union)
3592
+ named.named_union = UnionModel1(name="model1", prop1=1)
3593
+ assert _is_model(named.named_union)
3594
+ assert named.named_union == named["namedUnion"] == {"name": "model1", "prop1": 1}
3595
+ named = ModelWithNamedUnionProperty(named_union=UnionModel2(name="model2", prop2=2))
3596
+ assert named.named_union == named["namedUnion"] == {"name": "model2", "prop2": 2}
3597
+ named = ModelWithNamedUnionProperty({"namedUnion": {"name": "model2", "prop2": 2}})
3598
+ assert named.named_union == named["namedUnion"] == {"name": "model2", "prop2": 2}
3599
+
3600
+
3601
+ def test_as_dict():
3602
+ class CatComplex(PetComplex):
3603
+ color: Optional[str] = rest_field(default=None)
3604
+ hates: Optional[List[DogComplex]] = rest_field(default=None, visibility=["read"])
3605
+
3606
+ @overload
3607
+ def __init__(
3608
+ self,
3609
+ *,
3610
+ id: Optional[int] = None,
3611
+ name: Optional[str] = None,
3612
+ food: Optional[str] = None,
3613
+ color: Optional[str] = None,
3614
+ hates: Optional[List[DogComplex]] = None,
3615
+ ): ...
3616
+
3617
+ @overload
3618
+ def __init__(self, mapping: Mapping[str, Any], /): ...
3619
+
3620
+ def __init__(self, *args, **kwargs):
3621
+ super().__init__(*args, **kwargs)
3622
+
3623
+ model = CatComplex(
3624
+ id=2,
3625
+ name="Siamese",
3626
+ hates=[
3627
+ DogComplex(id=1, name="Potato", food="tomato"),
3628
+ DogComplex(id=-1, name="Tomato", food="french fries"),
3629
+ ],
3630
+ )
3631
+ assert model.as_dict(exclude_readonly=True) == {
3632
+ "id": 2,
3633
+ "name": "Siamese",
3634
+ "color": None,
3635
+ }
3636
+
3637
+
3638
+ class Fish(Model):
3639
+ __mapping__: Dict[str, Model] = {}
3640
+ age: int = rest_field()
3641
+ kind: Literal[None] = rest_discriminator(name="kind")
3642
+
3643
+ @overload
3644
+ def __init__(
3645
+ self,
3646
+ *,
3647
+ age: int,
3648
+ ): ...
3649
+
3650
+ @overload
3651
+ def __init__(self, mapping: Mapping[str, Any]): ...
3652
+
3653
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
3654
+ super().__init__(*args, **kwargs)
3655
+ self.kind: Literal[None] = None
3656
+
3657
+
3658
+ class Shark(Fish, discriminator="shark"):
3659
+ __mapping__: Dict[str, Model] = {}
3660
+ kind: Literal["shark"] = rest_discriminator(name="kind")
3661
+ sharktype: Literal[None] = rest_discriminator(name="sharktype")
3662
+
3663
+ @overload
3664
+ def __init__(
3665
+ self,
3666
+ *,
3667
+ age: int,
3668
+ ): ...
3669
+
3670
+ @overload
3671
+ def __init__(self, mapping: Mapping[str, Any]): ...
3672
+
3673
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
3674
+ super().__init__(*args, **kwargs)
3675
+ self.kind: Literal["shark"] = "shark"
3676
+ self.sharktype: Literal[None] = None
3677
+
3678
+
3679
+ class GoblinShark(Shark, discriminator="goblin"):
3680
+ sharktype: Literal["goblin"] = rest_discriminator(name="sharktype")
3681
+
3682
+ @overload
3683
+ def __init__(
3684
+ self,
3685
+ *,
3686
+ age: int,
3687
+ ): ...
3688
+
3689
+ @overload
3690
+ def __init__(self, mapping: Mapping[str, Any]): ...
3691
+
3692
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
3693
+ super().__init__(*args, **kwargs)
3694
+ self.sharktype: Literal["goblin"] = "goblin"
3695
+
3696
+
3697
+ class Salmon(Fish, discriminator="salmon"):
3698
+ kind: Literal["salmon"] = rest_discriminator(name="kind")
3699
+ friends: Optional[List["Fish"]] = rest_field()
3700
+ hate: Optional[Dict[str, "Fish"]] = rest_field()
3701
+ partner: Optional["Fish"] = rest_field()
3702
+
3703
+ @overload
3704
+ def __init__(
3705
+ self,
3706
+ *,
3707
+ age: int,
3708
+ friends: Optional[List["Fish"]] = None,
3709
+ hate: Optional[Dict[str, "Fish"]] = None,
3710
+ partner: Optional["Fish"] = None,
3711
+ ): ...
3712
+
3713
+ @overload
3714
+ def __init__(self, mapping: Mapping[str, Any]): ...
3715
+
3716
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
3717
+ super().__init__(*args, **kwargs)
3718
+ self.kind: Literal["salmon"] = "salmon"
3719
+
3720
+
3721
+ class SawShark(Shark, discriminator="saw"):
3722
+ sharktype: Literal["saw"] = rest_discriminator(name="sharktype")
3723
+
3724
+ @overload
3725
+ def __init__(
3726
+ self,
3727
+ *,
3728
+ age: int,
3729
+ ): ...
3730
+
3731
+ @overload
3732
+ def __init__(self, mapping: Mapping[str, Any]): ...
3733
+
3734
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
3735
+ super().__init__(*args, **kwargs)
3736
+ self.sharktype: Literal["saw"] = "saw"
3737
+
3738
+
3739
+ def test_discriminator():
3740
+ input = {
3741
+ "age": 1,
3742
+ "kind": "salmon",
3743
+ "partner": {
3744
+ "age": 2,
3745
+ "kind": "shark",
3746
+ "sharktype": "saw",
3747
+ },
3748
+ "friends": [
3749
+ {
3750
+ "age": 2,
3751
+ "kind": "salmon",
3752
+ "partner": {
3753
+ "age": 3,
3754
+ "kind": "salmon",
3755
+ },
3756
+ "hate": {
3757
+ "key1": {
3758
+ "age": 4,
3759
+ "kind": "salmon",
3760
+ },
3761
+ "key2": {
3762
+ "age": 2,
3763
+ "kind": "shark",
3764
+ "sharktype": "goblin",
3765
+ },
3766
+ },
3767
+ },
3768
+ {
3769
+ "age": 3,
3770
+ "kind": "shark",
3771
+ "sharktype": "goblin",
3772
+ },
3773
+ ],
3774
+ "hate": {
3775
+ "key3": {
3776
+ "age": 3,
3777
+ "kind": "shark",
3778
+ "sharktype": "saw",
3779
+ },
3780
+ "key4": {
3781
+ "age": 2,
3782
+ "kind": "salmon",
3783
+ "friends": [
3784
+ {
3785
+ "age": 1,
3786
+ "kind": "salmon",
3787
+ },
3788
+ {
3789
+ "age": 4,
3790
+ "kind": "shark",
3791
+ "sharktype": "goblin",
3792
+ },
3793
+ ],
3794
+ },
3795
+ },
3796
+ }
3797
+
3798
+ model = Salmon(input)
3799
+ assert model == input
3800
+ assert model.partner.age == 2
3801
+ assert model.partner == SawShark(age=2)
3802
+ assert model.friends[0].hate["key2"] == GoblinShark(age=2)
3803
+
3804
+
3805
+ def test_body_bytes_format():
3806
+ assert json.dumps(bytes("test", "utf-8"), cls=SdkJSONEncoder) == '"dGVzdA=="'
3807
+ assert json.dumps(bytearray("test", "utf-8"), cls=SdkJSONEncoder) == '"dGVzdA=="'
3808
+ assert json.dumps(bytes("test", "utf-8"), cls=SdkJSONEncoder, format="base64") == '"dGVzdA=="'
3809
+ assert json.dumps(bytes("test", "utf-8"), cls=SdkJSONEncoder, format="base64url") == '"dGVzdA"'
3810
+ assert json.dumps(bytearray("test", "utf-8"), cls=SdkJSONEncoder, format="base64") == '"dGVzdA=="'
3811
+ assert json.dumps(bytearray("test", "utf-8"), cls=SdkJSONEncoder, format="base64url") == '"dGVzdA"'
3812
+
3813
+ assert (
3814
+ json.dumps([bytes("test", "utf-8"), bytes("test", "utf-8")], cls=SdkJSONEncoder) == '["dGVzdA==", "dGVzdA=="]'
3815
+ )
3816
+ assert (
3817
+ json.dumps([bytearray("test", "utf-8"), bytearray("test", "utf-8")], cls=SdkJSONEncoder)
3818
+ == '["dGVzdA==", "dGVzdA=="]'
3819
+ )
3820
+ assert (
3821
+ json.dumps(
3822
+ [bytes("test", "utf-8"), bytes("test", "utf-8")],
3823
+ cls=SdkJSONEncoder,
3824
+ format="base64",
3825
+ )
3826
+ == '["dGVzdA==", "dGVzdA=="]'
3827
+ )
3828
+ assert (
3829
+ json.dumps(
3830
+ [bytes("test", "utf-8"), bytes("test", "utf-8")],
3831
+ cls=SdkJSONEncoder,
3832
+ format="base64url",
3833
+ )
3834
+ == '["dGVzdA", "dGVzdA"]'
3835
+ )
3836
+ assert (
3837
+ json.dumps(
3838
+ [bytearray("test", "utf-8"), bytearray("test", "utf-8")],
3839
+ cls=SdkJSONEncoder,
3840
+ format="base64",
3841
+ )
3842
+ == '["dGVzdA==", "dGVzdA=="]'
3843
+ )
3844
+ assert (
3845
+ json.dumps(
3846
+ [bytearray("test", "utf-8"), bytearray("test", "utf-8")],
3847
+ cls=SdkJSONEncoder,
3848
+ format="base64url",
3849
+ )
3850
+ == '["dGVzdA", "dGVzdA"]'
3851
+ )
3852
+
3853
+ assert (
3854
+ json.dumps(
3855
+ {"a": bytes("test", "utf-8"), "b": bytes("test", "utf-8")},
3856
+ cls=SdkJSONEncoder,
3857
+ )
3858
+ == '{"a": "dGVzdA==", "b": "dGVzdA=="}'
3859
+ )
3860
+ assert (
3861
+ json.dumps(
3862
+ {"a": bytearray("test", "utf-8"), "b": bytearray("test", "utf-8")},
3863
+ cls=SdkJSONEncoder,
3864
+ )
3865
+ == '{"a": "dGVzdA==", "b": "dGVzdA=="}'
3866
+ )
3867
+ assert (
3868
+ json.dumps(
3869
+ {"a": bytes("test", "utf-8"), "b": bytes("test", "utf-8")},
3870
+ cls=SdkJSONEncoder,
3871
+ format="base64",
3872
+ )
3873
+ == '{"a": "dGVzdA==", "b": "dGVzdA=="}'
3874
+ )
3875
+ assert (
3876
+ json.dumps(
3877
+ {"a": bytes("test", "utf-8"), "b": bytes("test", "utf-8")},
3878
+ cls=SdkJSONEncoder,
3879
+ format="base64url",
3880
+ )
3881
+ == '{"a": "dGVzdA", "b": "dGVzdA"}'
3882
+ )
3883
+ assert (
3884
+ json.dumps(
3885
+ {"a": bytearray("test", "utf-8"), "b": bytearray("test", "utf-8")},
3886
+ cls=SdkJSONEncoder,
3887
+ format="base64",
3888
+ )
3889
+ == '{"a": "dGVzdA==", "b": "dGVzdA=="}'
3890
+ )
3891
+ assert (
3892
+ json.dumps(
3893
+ {"a": bytearray("test", "utf-8"), "b": bytearray("test", "utf-8")},
3894
+ cls=SdkJSONEncoder,
3895
+ format="base64url",
3896
+ )
3897
+ == '{"a": "dGVzdA", "b": "dGVzdA"}'
3898
+ )
3899
+
3900
+
3901
+ def test_decimal_deserialization():
3902
+ class DecimalModel(Model):
3903
+ decimal_value: decimal.Decimal = rest_field(name="decimalValue")
3904
+
3905
+ @overload
3906
+ def __init__(self, *, decimal_value: decimal.Decimal): ...
3907
+
3908
+ @overload
3909
+ def __init__(self, mapping: Mapping[str, Any], /): ...
3910
+
3911
+ def __init__(self, *args, **kwargs):
3912
+ super().__init__(*args, **kwargs)
3913
+
3914
+ model = DecimalModel({"decimalValue": 0.33333})
3915
+ assert model["decimalValue"] == 0.33333
3916
+ assert model.decimal_value == decimal.Decimal("0.33333")
3917
+
3918
+ class BaseModel(Model):
3919
+ my_prop: DecimalModel = rest_field(name="myProp")
3920
+
3921
+ model = BaseModel({"myProp": {"decimalValue": 0.33333}})
3922
+ assert isinstance(model.my_prop, DecimalModel)
3923
+ assert model.my_prop["decimalValue"] == model["myProp"]["decimalValue"] == 0.33333
3924
+ assert model.my_prop.decimal_value == decimal.Decimal("0.33333")
3925
+
3926
+
3927
+ def test_decimal_serialization():
3928
+ assert json.dumps(decimal.Decimal("0.33333"), cls=SdkJSONEncoder) == "0.33333"
3929
+ assert (
3930
+ json.dumps([decimal.Decimal("0.33333"), decimal.Decimal("0.33333")], cls=SdkJSONEncoder) == "[0.33333, 0.33333]"
3931
+ )
3932
+ assert (
3933
+ json.dumps(
3934
+ {"a": decimal.Decimal("0.33333"), "b": decimal.Decimal("0.33333")},
3935
+ cls=SdkJSONEncoder,
3936
+ )
3937
+ == '{"a": 0.33333, "b": 0.33333}'
3938
+ )
3939
+
3940
+
3941
+ def test_int_as_str_deserialization():
3942
+ class IntAsStrModel(Model):
3943
+ int_as_str_value: int = rest_field(name="intAsStrValue", format="str")
3944
+
3945
+ model = IntAsStrModel({"intAsStrValue": "123"})
3946
+ assert model["intAsStrValue"] == "123"
3947
+ assert model.int_as_str_value == 123
3948
+
3949
+ class BaseModel(Model):
3950
+ my_prop: IntAsStrModel = rest_field(name="myProp")
3951
+
3952
+ model = BaseModel({"myProp": {"intAsStrValue": "123"}})
3953
+ assert isinstance(model.my_prop, IntAsStrModel)
3954
+ assert model.my_prop["intAsStrValue"] == model["myProp"]["intAsStrValue"] == "123"
3955
+ assert model.my_prop.int_as_str_value == 123
3956
+
3957
+
3958
+ def test_deserialize():
3959
+ expected = {"name": "name", "role": "role"}
3960
+ result = _deserialize(JSON, expected)
3961
+ assert result == expected
3962
+
3963
+
3964
+ def test_enum_deserialization():
3965
+ class MyEnum(Enum):
3966
+ A = "a"
3967
+ B = "b"
3968
+
3969
+ class ModelWithEnumProperty(Model):
3970
+ enum_property: Union[str, MyEnum] = rest_field(name="enumProperty")
3971
+ enum_property_optional: Optional[Union[str, MyEnum]] = rest_field(name="enumPropertyOptional")
3972
+ enum_property_optional_none: Optional[Union[str, MyEnum]] = rest_field(name="enumPropertyOptionalNone")
3973
+
3974
+ raw_input = {
3975
+ "enumProperty": "a",
3976
+ "enumPropertyOptional": "b",
3977
+ "enumPropertyOptionalNone": None,
3978
+ }
3979
+
3980
+ def check_func(target: ModelWithEnumProperty):
3981
+ assert target.enum_property == MyEnum.A
3982
+ assert target["enumProperty"] == "a"
3983
+ assert isinstance(target.enum_property, Enum)
3984
+ assert isinstance(target["enumProperty"], str)
3985
+
3986
+ assert target.enum_property_optional == MyEnum.B
3987
+ assert target["enumPropertyOptional"] == "b"
3988
+ assert isinstance(target.enum_property_optional, Enum)
3989
+ assert isinstance(target["enumPropertyOptional"], str)
3990
+
3991
+ assert target.enum_property_optional_none is None
3992
+ assert target["enumPropertyOptionalNone"] is None
3993
+
3994
+ model = ModelWithEnumProperty(raw_input)
3995
+ check_func(model)
3996
+
3997
+ result = _deserialize(List[ModelWithEnumProperty], [raw_input])
3998
+ for item in result:
3999
+ check_func(item)
4000
+
4001
+
4002
+ def test_not_mutating_original_dict():
4003
+ class MyInnerModel(Model):
4004
+ property: str = rest_field()
4005
+
4006
+ class MyModel(Model):
4007
+ property: MyInnerModel = rest_field()
4008
+
4009
+ origin = {"property": {"property": "hello"}}
4010
+
4011
+ dpg_model = MyModel(origin)
4012
+ assert dpg_model["property"]["property"] == "hello"
4013
+
4014
+ origin["property"]["property"] = "world"
4015
+ assert dpg_model["property"]["property"] == "hello"
4016
+
4017
+
4018
+ def test_model_init_io():
4019
+ class BytesModel(Model):
4020
+ property: bytes = rest_field()
4021
+
4022
+ JPG = Path(__file__).parent.parent / "data/image.jpg"
4023
+ with open(JPG, "rb") as f:
4024
+ b = BytesModel({"property": f})
4025
+ assert b.property == f
4026
+ assert b["property"] == f
4027
+ with open(JPG, "rb") as f:
4028
+ b = BytesModel(property=f)
4029
+ assert b.property == f
4030
+ assert b["property"] == f
4031
+
4032
+
4033
+ def test_additional_properties_serialization():
4034
+ value = {
4035
+ "name": "test",
4036
+ "modelProp": {"name": "test"},
4037
+ "stringProp": "string",
4038
+ "intProp": 1,
4039
+ "floatProp": 1.0,
4040
+ "boolProp": True,
4041
+ "listProp": [1, 2, 3],
4042
+ "dictProp": {"key": "value"},
4043
+ "noneProp": None,
4044
+ "datetimeProp": "2023-06-27T06:11:09Z",
4045
+ "durationProp": "P1D",
4046
+ }
4047
+
4048
+ class NormalModel(Model):
4049
+ prop: str = rest_field(name="name")
4050
+
4051
+ class AdditionalPropertiesModel(Model):
4052
+ name: str = rest_field(name="name")
4053
+
4054
+ model = AdditionalPropertiesModel(name="test")
4055
+ prop = NormalModel(prop="test")
4056
+ model["modelProp"] = prop
4057
+ model["stringProp"] = "string"
4058
+ model["intProp"] = 1
4059
+ model["floatProp"] = 1.0
4060
+ model["boolProp"] = True
4061
+ model["listProp"] = [1, 2, 3]
4062
+ model["dictProp"] = {"key": "value"}
4063
+ model["noneProp"] = None
4064
+ model["datetimeProp"] = datetime.datetime(2023, 6, 27, 6, 11, 9, tzinfo=datetime.timezone.utc)
4065
+ model["durationProp"] = datetime.timedelta(days=1)
4066
+
4067
+ assert json.loads(json.dumps(model, cls=SdkJSONEncoder)) == value