@typespec/http-client-python 0.28.2 → 0.29.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 (368) hide show
  1. package/dist/emitter/code-model.d.ts.map +1 -1
  2. package/dist/emitter/code-model.js +7 -6
  3. package/dist/emitter/code-model.js.map +1 -1
  4. package/dist/emitter/constants.d.ts +6 -0
  5. package/dist/emitter/constants.d.ts.map +1 -0
  6. package/dist/emitter/constants.js +27 -0
  7. package/dist/emitter/constants.js.map +1 -0
  8. package/dist/emitter/emitter.d.ts.map +1 -1
  9. package/dist/emitter/emitter.js +193 -119
  10. package/dist/emitter/emitter.js.map +1 -1
  11. package/dist/emitter/http.d.ts.map +1 -1
  12. package/dist/emitter/http.js +33 -1
  13. package/dist/emitter/http.js.map +1 -1
  14. package/dist/emitter/lib.d.ts +37 -3
  15. package/dist/emitter/lib.d.ts.map +1 -1
  16. package/dist/emitter/lib.js +17 -0
  17. package/dist/emitter/lib.js.map +1 -1
  18. package/dist/emitter/run-python3.js +1 -1
  19. package/dist/emitter/run-python3.js.map +1 -1
  20. package/dist/emitter/system-requirements.js +6 -4
  21. package/dist/emitter/system-requirements.js.map +1 -1
  22. package/dist/emitter/types.d.ts +2 -5
  23. package/dist/emitter/types.d.ts.map +1 -1
  24. package/dist/emitter/types.js +86 -63
  25. package/dist/emitter/types.js.map +1 -1
  26. package/dist/emitter/utils.d.ts.map +1 -1
  27. package/dist/emitter/utils.js +16 -17
  28. package/dist/emitter/utils.js.map +1 -1
  29. package/emitter/src/code-model.ts +7 -13
  30. package/emitter/src/constants.ts +27 -0
  31. package/emitter/src/emitter.ts +241 -123
  32. package/emitter/src/http.ts +29 -1
  33. package/emitter/src/lib.ts +24 -0
  34. package/emitter/src/run-python3.ts +1 -1
  35. package/emitter/src/system-requirements.ts +4 -4
  36. package/emitter/src/types.ts +102 -62
  37. package/emitter/src/utils.ts +16 -18
  38. package/emitter/temp/tsconfig.tsbuildinfo +1 -1
  39. package/emitter/test/utils.test.ts +3 -0
  40. package/emitter/tsconfig.build.json +2 -1
  41. package/emitter/tsconfig.json +2 -1
  42. package/eng/scripts/Build-Packages.ps1 +62 -37
  43. package/eng/scripts/Check-GitChanges.ps1 +30 -3
  44. package/eng/scripts/Functions.ps1 +54 -15
  45. package/eng/scripts/Generate.ps1 +42 -8
  46. package/eng/scripts/Generation.psm1 +44 -15
  47. package/eng/scripts/Initialize-Repository.ps1 +29 -11
  48. package/eng/scripts/Test-Packages.ps1 +47 -31
  49. package/eng/scripts/ci/config/eslint-ci.config.mjs +64 -0
  50. package/eng/scripts/ci/{mypy.ini → config/mypy.ini} +1 -1
  51. package/eng/scripts/ci/{pylintrc → config/pylintrc} +1 -1
  52. package/eng/scripts/ci/{pyrightconfig.json → config/pyrightconfig.json} +1 -1
  53. package/eng/scripts/ci/dev_requirements.txt +2 -1
  54. package/eng/scripts/ci/format.ts +232 -2
  55. package/eng/scripts/ci/lint.ts +223 -20
  56. package/eng/scripts/ci/regenerate-common.ts +411 -136
  57. package/eng/scripts/ci/regenerate.ts +247 -101
  58. package/eng/scripts/ci/run-tests.ts +507 -0
  59. package/eng/scripts/ci/run_apiview.py +22 -16
  60. package/eng/scripts/ci/run_mypy.py +34 -23
  61. package/eng/scripts/ci/run_pylint.py +27 -19
  62. package/eng/scripts/ci/run_pyright.py +9 -5
  63. package/eng/scripts/ci/run_sphinx_build.py +24 -9
  64. package/eng/scripts/ci/typecheck.ts +225 -0
  65. package/eng/scripts/ci/util.py +61 -25
  66. package/eng/scripts/ci/utils.ts +5 -5
  67. package/eng/scripts/setup/__pycache__/package_manager.cpython-311.pyc +0 -0
  68. package/eng/scripts/setup/__pycache__/venvtools.cpython-311.pyc +0 -0
  69. package/eng/scripts/setup/build.ts +1 -1
  70. package/eng/scripts/setup/build_pygen_wheel.py +4 -3
  71. package/eng/scripts/setup/install.py +4 -3
  72. package/eng/scripts/setup/install.ts +1 -1
  73. package/eng/scripts/setup/prepare.py +3 -2
  74. package/eng/scripts/setup/prepare.ts +1 -1
  75. package/eng/scripts/setup/run-python3.ts +1 -1
  76. package/eng/scripts/setup/run_batch.py +197 -0
  77. package/eng/scripts/setup/run_tsp.py +3 -3
  78. package/eng/scripts/setup/system-requirements.ts +8 -4
  79. package/eng/scripts/setup/venvtools.py +1 -1
  80. package/generator/build/lib/pygen/__init__.py +1 -3
  81. package/generator/build/lib/pygen/codegen/__init__.py +0 -1
  82. package/generator/build/lib/pygen/codegen/models/base.py +0 -1
  83. package/generator/build/lib/pygen/codegen/models/client.py +2 -1
  84. package/generator/build/lib/pygen/codegen/models/code_model.py +1 -0
  85. package/generator/build/lib/pygen/codegen/models/enum_type.py +0 -1
  86. package/generator/build/lib/pygen/codegen/models/model_type.py +1 -7
  87. package/generator/build/lib/pygen/codegen/models/parameter.py +16 -14
  88. package/generator/build/lib/pygen/codegen/serializers/__init__.py +27 -12
  89. package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +12 -3
  90. package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +6 -2
  91. package/generator/build/lib/pygen/codegen/serializers/import_serializer.py +17 -4
  92. package/generator/build/lib/pygen/codegen/serializers/parameter_serializer.py +17 -11
  93. package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +176 -71
  94. package/generator/build/lib/pygen/codegen/templates/packaging_templates/README.md.jinja2 +11 -3
  95. package/generator/build/lib/pygen/codegen/templates/packaging_templates/pyproject.toml.jinja2 +1 -1
  96. package/generator/build/lib/pygen/codegen/templates/serialization.py.jinja2 +5 -1
  97. package/generator/build/lib/pygen/preprocess/__init__.py +37 -30
  98. package/generator/component-detection-pip-report.json +7 -7
  99. package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
  100. package/generator/pygen/__init__.py +1 -3
  101. package/generator/pygen/codegen/__init__.py +0 -1
  102. package/generator/pygen/codegen/models/base.py +0 -1
  103. package/generator/pygen/codegen/models/client.py +2 -1
  104. package/generator/pygen/codegen/models/code_model.py +1 -0
  105. package/generator/pygen/codegen/models/enum_type.py +0 -1
  106. package/generator/pygen/codegen/models/model_type.py +1 -7
  107. package/generator/pygen/codegen/models/parameter.py +16 -14
  108. package/generator/pygen/codegen/serializers/__init__.py +27 -12
  109. package/generator/pygen/codegen/serializers/builder_serializer.py +12 -3
  110. package/generator/pygen/codegen/serializers/general_serializer.py +6 -2
  111. package/generator/pygen/codegen/serializers/import_serializer.py +17 -4
  112. package/generator/pygen/codegen/serializers/parameter_serializer.py +17 -11
  113. package/generator/pygen/codegen/templates/model_base.py.jinja2 +176 -71
  114. package/generator/pygen/codegen/templates/packaging_templates/README.md.jinja2 +11 -3
  115. package/generator/pygen/codegen/templates/packaging_templates/pyproject.toml.jinja2 +1 -1
  116. package/generator/pygen/codegen/templates/serialization.py.jinja2 +5 -1
  117. package/generator/pygen/preprocess/__init__.py +37 -30
  118. package/generator/pygen.egg-info/PKG-INFO +1 -2
  119. package/generator/pygen.egg-info/requires.txt +1 -1
  120. package/generator/setup.py +1 -2
  121. package/package.json +63 -42
  122. package/eng/scripts/ci/run-ci.ts +0 -89
  123. package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_commonproperties_async.py +0 -94
  124. package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_largeheader_async.py +0 -30
  125. package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_nonresource_async.py +0 -36
  126. package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_operationtemplates_async.py +0 -205
  127. package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_resource_async.py +0 -478
  128. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_access_async.py +0 -101
  129. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_api_version_header_async.py +0 -18
  130. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_api_version_path_async.py +0 -18
  131. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_api_version_query_async.py +0 -18
  132. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_client_default_value_async.py +0 -46
  133. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_client_initialization_async.py +0 -58
  134. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_client_location_async.py +0 -82
  135. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_deserialize_empty_string_as_null_async.py +0 -20
  136. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_flatten_async.py +0 -115
  137. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_hierrarchy_building_async.py +0 -53
  138. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_next_link_verb_async.py +0 -28
  139. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_override_async.py +0 -106
  140. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_usage_async.py +0 -38
  141. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_basic_async.py +0 -76
  142. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_lro_rpc_async.py +0 -22
  143. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_lro_standard_async.py +0 -39
  144. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_model_async.py +0 -33
  145. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_page_async.py +0 -80
  146. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_scalar_async.py +0 -41
  147. package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_traits_async.py +0 -87
  148. package/generator/test/azure/mock_api_tests/asynctests/test_azure_encode_duration_async.py +0 -19
  149. package/generator/test/azure/mock_api_tests/asynctests/test_azure_example_basic_async.py +0 -30
  150. package/generator/test/azure/mock_api_tests/asynctests/test_azure_payload_pageable_async.py +0 -19
  151. package/generator/test/azure/mock_api_tests/asynctests/test_azure_resource_manager_method_subscription_id_async.py +0 -248
  152. package/generator/test/azure/mock_api_tests/asynctests/test_azure_resource_manager_multi_service_async.py +0 -110
  153. package/generator/test/azure/mock_api_tests/asynctests/test_azure_resource_manager_multi_service_shared_models_async.py +0 -132
  154. package/generator/test/azure/mock_api_tests/asynctests/test_azure_special_headers_client_request_id_async.py +0 -30
  155. package/generator/test/azure/mock_api_tests/asynctests/test_azure_versioning_previewversion_async.py +0 -53
  156. package/generator/test/azure/mock_api_tests/asynctests/test_client_namespace_async.py +0 -34
  157. package/generator/test/azure/mock_api_tests/asynctests/test_client_naming_async.py +0 -69
  158. package/generator/test/azure/mock_api_tests/asynctests/test_client_naming_enum_conflict_async.py +0 -37
  159. package/generator/test/azure/mock_api_tests/asynctests/test_client_overload_async.py +0 -27
  160. package/generator/test/azure/mock_api_tests/asynctests/test_client_structure_async.py +0 -64
  161. package/generator/test/azure/mock_api_tests/asynctests/test_client_structure_clientoperationgroup_async.py +0 -31
  162. package/generator/test/azure/mock_api_tests/asynctests/test_encode_duration_async.py +0 -63
  163. package/generator/test/azure/mock_api_tests/asynctests/test_encode_numeric_async.py +0 -35
  164. package/generator/test/azure/mock_api_tests/asynctests/test_parameters_basic_async.py +0 -24
  165. package/generator/test/azure/mock_api_tests/asynctests/test_parameters_spread_async.py +0 -76
  166. package/generator/test/azure/mock_api_tests/asynctests/test_payload_content_negotiation_async.py +0 -37
  167. package/generator/test/azure/mock_api_tests/asynctests/test_payload_multipart_async.py +0 -216
  168. package/generator/test/azure/mock_api_tests/asynctests/test_resiliency_srv_driven_async.py +0 -130
  169. package/generator/test/azure/mock_api_tests/asynctests/test_serialization_encoded_name_json_async.py +0 -24
  170. package/generator/test/azure/mock_api_tests/asynctests/test_service_multi_service_async.py +0 -31
  171. package/generator/test/azure/mock_api_tests/asynctests/test_special_words_async.py +0 -71
  172. package/generator/test/azure/mock_api_tests/conftest.py +0 -207
  173. package/generator/test/azure/mock_api_tests/data/image.jpg +0 -0
  174. package/generator/test/azure/mock_api_tests/data/image.png +0 -0
  175. package/generator/test/azure/mock_api_tests/test_azure_arm_commonproperties.py +0 -89
  176. package/generator/test/azure/mock_api_tests/test_azure_arm_largeheader.py +0 -27
  177. package/generator/test/azure/mock_api_tests/test_azure_arm_nonresource.py +0 -34
  178. package/generator/test/azure/mock_api_tests/test_azure_arm_operationtemplates.py +0 -180
  179. package/generator/test/azure/mock_api_tests/test_azure_arm_resource.py +0 -436
  180. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_access.py +0 -92
  181. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_api_version_header.py +0 -17
  182. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_api_version_path.py +0 -17
  183. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_api_version_query.py +0 -17
  184. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_client_default_value.py +0 -42
  185. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_client_initialization.py +0 -51
  186. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_client_location.py +0 -70
  187. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_deserialize_empty_string_as_null.py +0 -19
  188. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_flatten.py +0 -104
  189. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_hierrarchy_building.py +0 -45
  190. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_next_link_verb.py +0 -27
  191. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_override.py +0 -102
  192. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_usage.py +0 -32
  193. package/generator/test/azure/mock_api_tests/test_azure_core_basic.py +0 -70
  194. package/generator/test/azure/mock_api_tests/test_azure_core_lro_rpc.py +0 -20
  195. package/generator/test/azure/mock_api_tests/test_azure_core_lro_standard.py +0 -32
  196. package/generator/test/azure/mock_api_tests/test_azure_core_model.py +0 -30
  197. package/generator/test/azure/mock_api_tests/test_azure_core_page.py +0 -71
  198. package/generator/test/azure/mock_api_tests/test_azure_core_scalar.py +0 -35
  199. package/generator/test/azure/mock_api_tests/test_azure_core_traits.py +0 -85
  200. package/generator/test/azure/mock_api_tests/test_azure_encode_duration.py +0 -18
  201. package/generator/test/azure/mock_api_tests/test_azure_example_basic.py +0 -29
  202. package/generator/test/azure/mock_api_tests/test_azure_payload_pageable.py +0 -18
  203. package/generator/test/azure/mock_api_tests/test_azure_resource_manager_method_subscription_id.py +0 -235
  204. package/generator/test/azure/mock_api_tests/test_azure_resource_manager_multi_service.py +0 -104
  205. package/generator/test/azure/mock_api_tests/test_azure_resource_manager_multi_service_shared_models.py +0 -128
  206. package/generator/test/azure/mock_api_tests/test_azure_special_headers_client_request_id.py +0 -29
  207. package/generator/test/azure/mock_api_tests/test_azure_versioning_previewversion.py +0 -50
  208. package/generator/test/azure/mock_api_tests/test_clear_output_folder.py +0 -23
  209. package/generator/test/azure/mock_api_tests/test_client_namespace.py +0 -32
  210. package/generator/test/azure/mock_api_tests/test_client_naming.py +0 -57
  211. package/generator/test/azure/mock_api_tests/test_client_naming_enum_conflict.py +0 -35
  212. package/generator/test/azure/mock_api_tests/test_client_overload.py +0 -25
  213. package/generator/test/azure/mock_api_tests/test_client_structure.py +0 -60
  214. package/generator/test/azure/mock_api_tests/test_client_structure_clientoperationgroup.py +0 -29
  215. package/generator/test/azure/mock_api_tests/test_encode_duration.py +0 -60
  216. package/generator/test/azure/mock_api_tests/test_encode_numeric.py +0 -31
  217. package/generator/test/azure/mock_api_tests/test_model_base_flatten_compatibility.py +0 -251
  218. package/generator/test/azure/mock_api_tests/test_parameters_basic.py +0 -22
  219. package/generator/test/azure/mock_api_tests/test_parameters_spread.py +0 -66
  220. package/generator/test/azure/mock_api_tests/test_payload_content_negotiation.py +0 -33
  221. package/generator/test/azure/mock_api_tests/test_payload_multipart.py +0 -198
  222. package/generator/test/azure/mock_api_tests/test_resiliency_srv_driven.py +0 -124
  223. package/generator/test/azure/mock_api_tests/test_serialization_encoded_name_json.py +0 -21
  224. package/generator/test/azure/mock_api_tests/test_service_multi_service.py +0 -29
  225. package/generator/test/azure/mock_api_tests/test_special_words.py +0 -63
  226. package/generator/test/azure/requirements.txt +0 -125
  227. package/generator/test/azure/tox.ini +0 -64
  228. package/generator/test/data/image.jpg +0 -0
  229. package/generator/test/data/image.png +0 -0
  230. package/generator/test/dev_requirements.txt +0 -8
  231. package/generator/test/generic_mock_api_tests/asynctests/test_authentication_async.py +0 -140
  232. package/generator/test/generic_mock_api_tests/asynctests/test_encode_array_async.py +0 -139
  233. package/generator/test/generic_mock_api_tests/asynctests/test_encode_bytes_async.py +0 -132
  234. package/generator/test/generic_mock_api_tests/asynctests/test_encode_datetime_async.py +0 -127
  235. package/generator/test/generic_mock_api_tests/asynctests/test_headasboolean_async.py +0 -35
  236. package/generator/test/generic_mock_api_tests/asynctests/test_parameters_body_optionality_async.py +0 -30
  237. package/generator/test/generic_mock_api_tests/asynctests/test_parameters_collection_format_async.py +0 -38
  238. package/generator/test/generic_mock_api_tests/asynctests/test_parameters_path_async.py +0 -24
  239. package/generator/test/generic_mock_api_tests/asynctests/test_parameters_query_async.py +0 -18
  240. package/generator/test/generic_mock_api_tests/asynctests/test_payload_json_merge_patch_async.py +0 -98
  241. package/generator/test/generic_mock_api_tests/asynctests/test_payload_media_type_async.py +0 -27
  242. package/generator/test/generic_mock_api_tests/asynctests/test_payload_pageable_async.py +0 -134
  243. package/generator/test/generic_mock_api_tests/asynctests/test_payload_xml_async.py +0 -150
  244. package/generator/test/generic_mock_api_tests/asynctests/test_response_status_code_range_async.py +0 -38
  245. package/generator/test/generic_mock_api_tests/asynctests/test_routes_async.py +0 -331
  246. package/generator/test/generic_mock_api_tests/asynctests/test_server_endpoint_not_defined_async.py +0 -18
  247. package/generator/test/generic_mock_api_tests/asynctests/test_server_path_multiple_async.py +0 -25
  248. package/generator/test/generic_mock_api_tests/asynctests/test_server_path_single_async.py +0 -18
  249. package/generator/test/generic_mock_api_tests/asynctests/test_server_versions_not_versioned_async.py +0 -28
  250. package/generator/test/generic_mock_api_tests/asynctests/test_server_versions_versioned_async.py +0 -34
  251. package/generator/test/generic_mock_api_tests/asynctests/test_special_headers_conditional_request_async.py +0 -38
  252. package/generator/test/generic_mock_api_tests/asynctests/test_special_headers_repeatability_async.py +0 -19
  253. package/generator/test/generic_mock_api_tests/asynctests/test_specs_documentation_async.py +0 -59
  254. package/generator/test/generic_mock_api_tests/asynctests/test_streaming_jsonl_async.py +0 -27
  255. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_array_async.py +0 -127
  256. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_dictionary_async.py +0 -98
  257. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_enum_extensible_async.py +0 -25
  258. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_enum_fixed_async.py +0 -27
  259. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_file_async.py +0 -59
  260. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_empty_async.py +0 -32
  261. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_enum_discriminator_async.py +0 -70
  262. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_nested_discriminator_async.py +0 -85
  263. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_not_discriminated_async.py +0 -34
  264. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_recursive_async.py +0 -34
  265. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_single_discriminator_async.py +0 -67
  266. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_usage_async.py +0 -32
  267. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_visibility_async.py +0 -47
  268. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_additionalproperties_async.py +0 -353
  269. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_nullable_async.py +0 -110
  270. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_optional_async.py +0 -197
  271. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_valuetypes_async.py +0 -315
  272. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_scalar_async.py +0 -60
  273. package/generator/test/generic_mock_api_tests/asynctests/test_typetest_union_async.py +0 -90
  274. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_added_async.py +0 -36
  275. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_made_optional_async.py +0 -21
  276. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_removed_async.py +0 -39
  277. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_renamed_from_async.py +0 -29
  278. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_return_type_changed_from_async.py +0 -18
  279. package/generator/test/generic_mock_api_tests/asynctests/test_versioning_type_changed_from_async.py +0 -22
  280. package/generator/test/generic_mock_api_tests/conftest.py +0 -75
  281. package/generator/test/generic_mock_api_tests/data/image.jpg +0 -0
  282. package/generator/test/generic_mock_api_tests/data/image.png +0 -0
  283. package/generator/test/generic_mock_api_tests/test_authentication.py +0 -130
  284. package/generator/test/generic_mock_api_tests/test_encode_array.py +0 -126
  285. package/generator/test/generic_mock_api_tests/test_encode_bytes.py +0 -128
  286. package/generator/test/generic_mock_api_tests/test_encode_datetime.py +0 -123
  287. package/generator/test/generic_mock_api_tests/test_headasboolean.py +0 -33
  288. package/generator/test/generic_mock_api_tests/test_parameters_body_optionality.py +0 -27
  289. package/generator/test/generic_mock_api_tests/test_parameters_collection_format.py +0 -33
  290. package/generator/test/generic_mock_api_tests/test_parameters_path.py +0 -22
  291. package/generator/test/generic_mock_api_tests/test_parameters_query.py +0 -17
  292. package/generator/test/generic_mock_api_tests/test_payload_json_merge_patch.py +0 -93
  293. package/generator/test/generic_mock_api_tests/test_payload_media_type.py +0 -25
  294. package/generator/test/generic_mock_api_tests/test_payload_pageable.py +0 -99
  295. package/generator/test/generic_mock_api_tests/test_payload_xml.py +0 -135
  296. package/generator/test/generic_mock_api_tests/test_response_status_code_range.py +0 -36
  297. package/generator/test/generic_mock_api_tests/test_routes.py +0 -285
  298. package/generator/test/generic_mock_api_tests/test_server_endpoint_not_defined.py +0 -17
  299. package/generator/test/generic_mock_api_tests/test_server_path_multiple.py +0 -21
  300. package/generator/test/generic_mock_api_tests/test_server_path_single.py +0 -17
  301. package/generator/test/generic_mock_api_tests/test_server_versions_not_versioned.py +0 -25
  302. package/generator/test/generic_mock_api_tests/test_server_versions_versioned.py +0 -30
  303. package/generator/test/generic_mock_api_tests/test_special_headers_conditional_request.py +0 -34
  304. package/generator/test/generic_mock_api_tests/test_special_headers_repeatability.py +0 -18
  305. package/generator/test/generic_mock_api_tests/test_specs_documentation.py +0 -51
  306. package/generator/test/generic_mock_api_tests/test_streaming_jsonl.py +0 -25
  307. package/generator/test/generic_mock_api_tests/test_typetest_array.py +0 -111
  308. package/generator/test/generic_mock_api_tests/test_typetest_dictionary.py +0 -86
  309. package/generator/test/generic_mock_api_tests/test_typetest_enum_extensible.py +0 -23
  310. package/generator/test/generic_mock_api_tests/test_typetest_enum_fixed.py +0 -25
  311. package/generator/test/generic_mock_api_tests/test_typetest_file.py +0 -52
  312. package/generator/test/generic_mock_api_tests/test_typetest_model_empty.py +0 -29
  313. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_enum_discriminator.py +0 -58
  314. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_nested_discriminator.py +0 -79
  315. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_not_discriminated.py +0 -31
  316. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_recursive.py +0 -32
  317. package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_single_discriminator.py +0 -60
  318. package/generator/test/generic_mock_api_tests/test_typetest_model_usage.py +0 -28
  319. package/generator/test/generic_mock_api_tests/test_typetest_model_visibility.py +0 -40
  320. package/generator/test/generic_mock_api_tests/test_typetest_property_additionalproperties.py +0 -314
  321. package/generator/test/generic_mock_api_tests/test_typetest_property_nullable.py +0 -102
  322. package/generator/test/generic_mock_api_tests/test_typetest_property_optional.py +0 -174
  323. package/generator/test/generic_mock_api_tests/test_typetest_property_valuetypes.py +0 -286
  324. package/generator/test/generic_mock_api_tests/test_typetest_scalar.py +0 -53
  325. package/generator/test/generic_mock_api_tests/test_typetest_union.py +0 -80
  326. package/generator/test/generic_mock_api_tests/test_typetest_union_discriminated.py +0 -290
  327. package/generator/test/generic_mock_api_tests/test_versioning_added.py +0 -33
  328. package/generator/test/generic_mock_api_tests/test_versioning_made_optional.py +0 -20
  329. package/generator/test/generic_mock_api_tests/test_versioning_removed.py +0 -37
  330. package/generator/test/generic_mock_api_tests/test_versioning_renamed_from.py +0 -27
  331. package/generator/test/generic_mock_api_tests/test_versioning_return_type_changed_from.py +0 -17
  332. package/generator/test/generic_mock_api_tests/test_versioning_type_changed_from.py +0 -21
  333. package/generator/test/unbranded/mock_api_tests/asynctests/test_auth_flow_async.py +0 -19
  334. package/generator/test/unbranded/mock_api_tests/asynctests/test_encode_duration_async.py +0 -63
  335. package/generator/test/unbranded/mock_api_tests/asynctests/test_encode_numeric_async.py +0 -35
  336. package/generator/test/unbranded/mock_api_tests/asynctests/test_parameters_basic_async.py +0 -24
  337. package/generator/test/unbranded/mock_api_tests/asynctests/test_parameters_spread_async.py +0 -76
  338. package/generator/test/unbranded/mock_api_tests/asynctests/test_payload_content_negotiation_async.py +0 -37
  339. package/generator/test/unbranded/mock_api_tests/asynctests/test_payload_multipart_async.py +0 -218
  340. package/generator/test/unbranded/mock_api_tests/asynctests/test_serialization_encoded_name_json_async.py +0 -24
  341. package/generator/test/unbranded/mock_api_tests/asynctests/test_special_words_async.py +0 -73
  342. package/generator/test/unbranded/mock_api_tests/asynctests/test_unbranded_async.py +0 -24
  343. package/generator/test/unbranded/mock_api_tests/conftest.py +0 -91
  344. package/generator/test/unbranded/mock_api_tests/data/image.jpg +0 -0
  345. package/generator/test/unbranded/mock_api_tests/data/image.png +0 -0
  346. package/generator/test/unbranded/mock_api_tests/test_auth_flow.py +0 -17
  347. package/generator/test/unbranded/mock_api_tests/test_encode_duration.py +0 -60
  348. package/generator/test/unbranded/mock_api_tests/test_encode_numeric.py +0 -32
  349. package/generator/test/unbranded/mock_api_tests/test_parameters_basic.py +0 -22
  350. package/generator/test/unbranded/mock_api_tests/test_parameters_spread.py +0 -66
  351. package/generator/test/unbranded/mock_api_tests/test_payload_content_negotiation.py +0 -33
  352. package/generator/test/unbranded/mock_api_tests/test_payload_multipart.py +0 -200
  353. package/generator/test/unbranded/mock_api_tests/test_serialization_encoded_name_json.py +0 -22
  354. package/generator/test/unbranded/mock_api_tests/test_special_words.py +0 -66
  355. package/generator/test/unbranded/mock_api_tests/test_unbranded.py +0 -65
  356. package/generator/test/unbranded/requirements.txt +0 -69
  357. package/generator/test/unbranded/tox.ini +0 -64
  358. package/generator/test/unittests/conftest.py +0 -15
  359. package/generator/test/unittests/requirements.txt +0 -3
  360. package/generator/test/unittests/test_enums.py +0 -89
  361. package/generator/test/unittests/test_model_base_serialization.py +0 -4906
  362. package/generator/test/unittests/test_model_base_xml_serialization.py +0 -1239
  363. package/generator/test/unittests/test_name_converter.py +0 -35
  364. package/generator/test/unittests/test_optional_return_type.py +0 -268
  365. package/generator/test/unittests/test_parameter_ordering.py +0 -104
  366. package/generator/test/unittests/test_sort_schema.py +0 -93
  367. package/generator/test/unittests/tox.ini +0 -5
  368. /package/eng/scripts/ci/{pyproject.toml → config/pyproject.toml} +0 -0
@@ -1,4906 +0,0 @@
1
- # ------------------------------------
2
- # Copyright (c) Microsoft Corporation.
3
- # Licensed under the MIT License.
4
- # ------------------------------------
5
- from ast import Mod
6
- import copy
7
- import decimal
8
- import json
9
- import datetime
10
- from pathlib import Path
11
- from typing import (
12
- Any,
13
- Iterable,
14
- Literal,
15
- Mapping,
16
- Sequence,
17
- Optional,
18
- overload,
19
- Union,
20
- )
21
- import pytest
22
- import isodate
23
- import sys
24
- from enum import Enum
25
-
26
- from specialwords._utils.model_base import (
27
- SdkJSONEncoder,
28
- Model,
29
- rest_field,
30
- _is_model,
31
- rest_discriminator,
32
- _deserialize,
33
- )
34
-
35
- if sys.version_info >= (3, 9):
36
- from collections.abc import MutableMapping
37
- else:
38
- from typing import MutableMapping # type: ignore # pylint: disable=ungrouped-imports
39
- JSON = MutableMapping[str, Any] # pylint: disable=unsubscriptable-object
40
-
41
-
42
- class BasicResource(Model):
43
- platform_update_domain_count: int = rest_field(
44
- name="platformUpdateDomainCount"
45
- ) # How many times the platform update domain has been counted
46
- platform_fault_domain_count: int = rest_field(
47
- name="platformFaultDomainCount"
48
- ) # How many times the platform fault domain has been counted
49
- virtual_machines: list[Any] = rest_field(name="virtualMachines") # List of virtual machines
50
-
51
- @overload
52
- def __init__(
53
- self,
54
- *,
55
- platform_update_domain_count: int,
56
- platform_fault_domain_count: int,
57
- virtual_machines: list[Any],
58
- ): ...
59
-
60
- @overload
61
- def __init__(self, mapping: Mapping[str, Any], /): ...
62
-
63
- def __init__(self, *args, **kwargs):
64
- super().__init__(*args, **kwargs)
65
-
66
-
67
- class ModelWithArgsProperty(Model):
68
- """A model that has a property named 'args' to test potential conflicts with *args."""
69
-
70
- name: str = rest_field()
71
- args: list[str] = rest_field() # property named 'args' which could conflict with *args
72
-
73
- @overload
74
- def __init__(self, *, name: str, args: list[str]): ...
75
-
76
- @overload
77
- def __init__(self, mapping: Mapping[str, Any], /): ...
78
-
79
- def __init__(self, *args, **kwargs):
80
- super().__init__(*args, **kwargs)
81
-
82
-
83
- class Pet(Model):
84
- name: str = rest_field() # my name
85
- species: str = rest_field() # my species
86
-
87
- @overload
88
- def __init__(self, *, name: str, species: str): ...
89
-
90
- @overload
91
- def __init__(self, mapping: Mapping[str, Any], /): ...
92
-
93
- def __init__(self, *args, **kwargs):
94
- super().__init__(*args, **kwargs)
95
-
96
-
97
- def test_model_and_dict_equal():
98
- dict_response = {
99
- "platformUpdateDomainCount": 5,
100
- "platformFaultDomainCount": 3,
101
- "virtualMachines": [],
102
- }
103
- model = BasicResource(
104
- platform_update_domain_count=5,
105
- platform_fault_domain_count=3,
106
- virtual_machines=[],
107
- )
108
-
109
- assert model == dict_response
110
- assert (
111
- model.platform_update_domain_count
112
- == model["platformUpdateDomainCount"]
113
- == dict_response["platformUpdateDomainCount"]
114
- == 5
115
- )
116
- assert (
117
- model.platform_fault_domain_count
118
- == model["platformFaultDomainCount"]
119
- == dict_response["platformFaultDomainCount"]
120
- == 3
121
- )
122
- assert model.virtual_machines == model["virtualMachines"] == dict_response["virtualMachines"]
123
-
124
-
125
- def test_model_with_args_property():
126
- """Test that a model with a property named 'args' works correctly."""
127
- # Test initialization with keyword arguments
128
- model = ModelWithArgsProperty(name="test", args=["arg1", "arg2", "arg3"])
129
- assert model.name == "test"
130
- assert model.args == ["arg1", "arg2", "arg3"]
131
-
132
- # Test dict-style access
133
- assert model["name"] == "test"
134
- assert model["args"] == ["arg1", "arg2", "arg3"]
135
-
136
- # Test equality with dict
137
- dict_response = {"name": "test", "args": ["arg1", "arg2", "arg3"]}
138
- assert model == dict_response
139
-
140
- # Test initialization from dict (using positional argument which goes to *args)
141
- model_from_dict = ModelWithArgsProperty(dict_response)
142
- assert model_from_dict.name == "test"
143
- assert model_from_dict.args == ["arg1", "arg2", "arg3"]
144
- assert model_from_dict == model
145
-
146
- # Test modification of the 'args' property
147
- model.args = ["new_arg"]
148
- assert model.args == ["new_arg"]
149
- assert model["args"] == ["new_arg"]
150
-
151
- # Test dict-style modification of 'args'
152
- model["args"] = ["modified_arg1", "modified_arg2"]
153
- assert model.args == ["modified_arg1", "modified_arg2"]
154
-
155
- # Test JSON serialization roundtrip
156
- json_str = json.dumps(dict(model))
157
- parsed = json.loads(json_str)
158
- assert parsed["name"] == "test"
159
- assert parsed["args"] == ["modified_arg1", "modified_arg2"]
160
-
161
-
162
- def test_json_roundtrip():
163
- dict_response = {
164
- "platformUpdateDomainCount": 5,
165
- "platformFaultDomainCount": 3,
166
- "virtualMachines": [],
167
- }
168
- model = BasicResource(
169
- platform_update_domain_count=5,
170
- platform_fault_domain_count=3,
171
- virtual_machines=[],
172
- )
173
- with pytest.raises(TypeError):
174
- json.dumps(model)
175
- assert (
176
- json.dumps(dict(model))
177
- == '{"platformUpdateDomainCount": 5, "platformFaultDomainCount": 3, "virtualMachines": []}'
178
- )
179
- assert json.loads(json.dumps(dict(model))) == model == dict_response
180
-
181
-
182
- def test_has_no_property():
183
- dict_response = {
184
- "platformUpdateDomainCount": 5,
185
- "platformFaultDomainCount": 3,
186
- "virtualMachines": [],
187
- "noProp": "bonjour!",
188
- }
189
- model = BasicResource(dict_response)
190
- assert (
191
- model.platform_update_domain_count
192
- == model["platformUpdateDomainCount"]
193
- == dict_response["platformUpdateDomainCount"]
194
- == 5
195
- )
196
- assert not hasattr(model, "no_prop")
197
- with pytest.raises(AttributeError) as ex:
198
- model.no_prop
199
-
200
- assert str(ex.value) == "'BasicResource' object has no attribute 'no_prop'"
201
- assert model["noProp"] == dict_response["noProp"] == "bonjour!"
202
-
203
- # let's add it to model now
204
-
205
- class BasicResourceWithProperty(BasicResource):
206
- no_prop: str = rest_field(name="noProp")
207
-
208
- model = BasicResourceWithProperty(
209
- platform_update_domain_count=5,
210
- platform_fault_domain_count=3,
211
- virtual_machines=[],
212
- no_prop="bonjour!",
213
- )
214
- assert model.no_prop == model["noProp"] == dict_response["noProp"] == "bonjour!"
215
-
216
-
217
- def test_original_and_attr_name_same():
218
- class MyModel(Model):
219
- hello: str = rest_field()
220
-
221
- @overload
222
- def __init__(self, *, hello: str): ...
223
-
224
- @overload
225
- def __init__(self, mapping: Mapping[str, Any], /): ...
226
-
227
- def __init__(self, *args, **kwargs):
228
- super().__init__(*args, **kwargs)
229
-
230
- dict_response = {"hello": "nihao"}
231
- model = MyModel(hello="nihao")
232
- assert model.hello == model["hello"] == dict_response["hello"]
233
-
234
-
235
- class OptionalModel(Model):
236
- optional_str: Optional[str] = rest_field()
237
- optional_time: Optional[datetime.time] = rest_field()
238
- optional_dict: Optional[dict[str, Optional[Pet]]] = rest_field(name="optionalDict")
239
- optional_model: Optional[Pet] = rest_field()
240
- optional_myself: Optional["OptionalModel"] = rest_field()
241
-
242
- @overload
243
- def __init__(
244
- self,
245
- *,
246
- optional_str: Optional[str] = None,
247
- optional_time: Optional[datetime.time] = None,
248
- optional_dict: Optional[dict[str, Optional[Pet]]] = None,
249
- optional_myself: Optional["OptionalModel"] = None,
250
- ): ...
251
-
252
- @overload
253
- def __init__(self, mapping: Mapping[str, Any], /): ...
254
-
255
- def __init__(self, *args, **kwargs):
256
- super().__init__(*args, **kwargs)
257
-
258
-
259
- def test_optional_property():
260
- dict_response = {
261
- "optional_str": "hello!",
262
- "optional_time": None,
263
- "optionalDict": {
264
- "Eugene": {
265
- "name": "Eugene",
266
- "species": "Dog",
267
- },
268
- "Lady": None,
269
- },
270
- "optional_model": None,
271
- "optional_myself": {
272
- "optional_str": None,
273
- "optional_time": "11:34:56",
274
- "optionalDict": None,
275
- "optional_model": {"name": "Lady", "species": "Newt"},
276
- "optional_myself": None,
277
- },
278
- }
279
-
280
- model = OptionalModel(dict_response)
281
- assert model.optional_str == model["optional_str"] == "hello!"
282
- assert model.optional_time == model["optional_time"] == None
283
- assert (
284
- model.optional_dict
285
- == model["optionalDict"]
286
- == {
287
- "Eugene": {
288
- "name": "Eugene",
289
- "species": "Dog",
290
- },
291
- "Lady": None,
292
- }
293
- )
294
- assert model.optional_dict
295
- assert model.optional_dict["Eugene"].name == model.optional_dict["Eugene"]["name"] == "Eugene"
296
- assert model.optional_dict["Lady"] is None
297
-
298
- assert (
299
- model.optional_myself
300
- == model["optional_myself"]
301
- == {
302
- "optional_str": None,
303
- "optional_time": "11:34:56",
304
- "optionalDict": None,
305
- "optional_model": {"name": "Lady", "species": "Newt"},
306
- "optional_myself": None,
307
- }
308
- )
309
- assert model.optional_myself
310
- assert model.optional_myself.optional_str is None
311
- assert model.optional_myself.optional_time == datetime.time(11, 34, 56)
312
- assert model.optional_myself.optional_dict is None
313
- assert model.optional_myself.optional_model
314
- assert model.optional_myself.optional_model.name == "Lady"
315
- assert model.optional_myself.optional_model.species == "Newt"
316
- assert model.optional_myself.optional_myself is None
317
-
318
-
319
- def test_model_pass_in_none():
320
- model = OptionalModel(optional_str=None)
321
- assert model.optional_str == None
322
- with pytest.raises(KeyError):
323
- model["optionalStr"]
324
-
325
-
326
- def test_modify_dict():
327
- model = BasicResource(
328
- platform_update_domain_count=5,
329
- platform_fault_domain_count=3,
330
- virtual_machines=[],
331
- )
332
-
333
- # now let's modify the model as a dict
334
- model["platformUpdateDomainCount"] = 100
335
- assert model.platform_update_domain_count == model["platformUpdateDomainCount"] == 100
336
-
337
-
338
- def test_modify_property():
339
- dict_response = {
340
- "platformUpdateDomainCount": 5,
341
- "platformFaultDomainCount": 3,
342
- "virtualMachines": [],
343
- }
344
- model = BasicResource(
345
- platform_update_domain_count=5,
346
- platform_fault_domain_count=3,
347
- virtual_machines=[],
348
- )
349
-
350
- # now let's modify the model through it's properties
351
- model.platform_fault_domain_count = 2000
352
- model["platformFaultDomainCount"]
353
- assert model.platform_fault_domain_count == model["platformFaultDomainCount"] == 2000
354
-
355
-
356
- def test_property_is_a_type():
357
- class Fish(Model):
358
- name: str = rest_field()
359
- species: Literal["Salmon", "Halibut"] = rest_field()
360
-
361
- @overload
362
- def __init__(self, *, name: str, species: Literal["Salmon", "Halibut"]): ...
363
-
364
- @overload
365
- def __init__(self, mapping: Mapping[str, Any], /): ...
366
-
367
- def __init__(self, *args, **kwargs):
368
- super().__init__(*args, **kwargs)
369
-
370
- class Fishery(Model):
371
- fish: Fish = rest_field()
372
-
373
- @overload
374
- def __init__(self, *, fish: Fish): ...
375
-
376
- @overload
377
- def __init__(self, mapping: Mapping[str, Any], /): ...
378
-
379
- def __init__(self, *args, **kwargs):
380
- super().__init__(*args, **kwargs)
381
-
382
- fishery = Fishery({"fish": {"name": "Benjamin", "species": "Salmon"}})
383
- assert isinstance(fishery.fish, Fish)
384
- assert fishery.fish.name == fishery.fish["name"] == fishery["fish"]["name"] == "Benjamin"
385
- assert fishery.fish.species == fishery.fish["species"] == fishery["fish"]["species"] == "Salmon"
386
-
387
-
388
- def test_model_initialization():
389
- class DatetimeModel(Model):
390
- datetime_value: datetime.datetime = rest_field(name="datetimeValue")
391
-
392
- @overload
393
- def __init__(self, *, datetime_value: datetime.datetime): ...
394
-
395
- @overload
396
- def __init__(self, mapping: Mapping[str, Any], /): ...
397
-
398
- def __init__(self, *args, **kwargs):
399
- super().__init__(*args, **kwargs)
400
-
401
- val_str = "9999-12-31T23:59:59.999000Z"
402
- val = isodate.parse_datetime(val_str)
403
-
404
- # when initialize model with dict, the dict value is shall be serialized value
405
- model1 = DatetimeModel({"datetimeValue": val_str})
406
- assert model1["datetimeValue"] == val_str
407
- assert model1.datetime_value == val
408
-
409
- # when initialize model with keyword args, the value is deserialized value
410
- model2 = DatetimeModel(datetime_value=val)
411
- assert model2["datetimeValue"] == val_str
412
- assert model2.datetime_value == val
413
-
414
- # what if we initialize with dict but the dict has deserialized value? this case show what happens.
415
- # Since we always serialize the value before initializing the model from dict, we could still get correct result
416
- model3 = DatetimeModel({"datetimeValue": val})
417
- assert model3["datetimeValue"] == val_str
418
- assert model3.datetime_value == val
419
-
420
-
421
- def test_model_dict_prop_initialization():
422
- class DatetimeModel(Model):
423
- dict_prop: dict[str, datetime.datetime] = rest_field(name="dictProp")
424
-
425
- @overload
426
- def __init__(self, *, dict_prop: dict[str, datetime.datetime]): ...
427
-
428
- @overload
429
- def __init__(self, mapping: Mapping[str, Any], /): ...
430
-
431
- def __init__(self, *args, **kwargs):
432
- super().__init__(*args, **kwargs)
433
-
434
- val_str = "9999-12-31T23:59:59.999000Z"
435
- val = isodate.parse_datetime(val_str)
436
-
437
- # when initialize model with dict, the dict value is shall be serialized value
438
- model1 = DatetimeModel({"dictProp": {"key1": val_str}})
439
- assert model1["dictProp"] == {"key1": val_str}
440
- assert model1.dict_prop == {"key1": val}
441
-
442
- # when initialize model with keyword args, the value is deserialized value
443
- model2 = DatetimeModel(dict_prop={"key1": val})
444
- assert model2["dictProp"] == {"key1": val_str}
445
- assert model2.dict_prop == {"key1": val}
446
-
447
- # what if we initialize with dict but the dict has deserialized value? this case show what happens.
448
- # Since we always serialize the value before initializing the model from dict, we could still get correct result
449
- model3 = DatetimeModel({"dictProp": {"key1": val}})
450
- assert model3["dictProp"] == {"key1": val_str}
451
- assert model3.dict_prop == {"key1": val}
452
-
453
-
454
- def test_datetime_deserialization():
455
- class DatetimeModel(Model):
456
- datetime_value: datetime.datetime = rest_field(name="datetimeValue")
457
-
458
- @overload
459
- def __init__(self, *, datetime_value: datetime.datetime): ...
460
-
461
- @overload
462
- def __init__(self, mapping: Mapping[str, Any], /): ...
463
-
464
- def __init__(self, *args, **kwargs):
465
- super().__init__(*args, **kwargs)
466
-
467
- val_str = "9999-12-31T23:59:59.999Z"
468
- val = isodate.parse_datetime(val_str)
469
- model = DatetimeModel({"datetimeValue": val_str})
470
- assert model["datetimeValue"] == val_str
471
- assert model.datetime_value == val
472
-
473
- class BaseModel(Model):
474
- my_prop: DatetimeModel = rest_field(name="myProp")
475
-
476
- model = BaseModel({"myProp": {"datetimeValue": val_str}})
477
- assert isinstance(model.my_prop, DatetimeModel)
478
- model.my_prop["datetimeValue"]
479
- assert model.my_prop["datetimeValue"] == model["myProp"]["datetimeValue"] == val_str
480
- assert model.my_prop.datetime_value == val
481
-
482
-
483
- def test_date_deserialization():
484
- class DateModel(Model):
485
- date_value: datetime.date = rest_field(name="dateValue")
486
-
487
- @overload
488
- def __init__(self, *, date_value: datetime.date): ...
489
-
490
- @overload
491
- def __init__(self, mapping: Mapping[str, Any], /): ...
492
-
493
- def __init__(self, *args, **kwargs):
494
- super().__init__(*args, **kwargs)
495
-
496
- val_str = "2016-02-29"
497
- val = isodate.parse_date(val_str)
498
- model = DateModel({"dateValue": val_str})
499
- assert model["dateValue"] == val_str
500
- assert model.date_value == val
501
-
502
- class BaseModel(Model):
503
- my_prop: DateModel = rest_field(name="myProp")
504
-
505
- model = BaseModel({"myProp": {"dateValue": val_str}})
506
- assert isinstance(model.my_prop, DateModel)
507
- assert model.my_prop["dateValue"] == model["myProp"]["dateValue"] == val_str
508
- assert model.my_prop.date_value == val
509
-
510
-
511
- def test_time_deserialization():
512
- class TimeModel(Model):
513
- time_value: datetime.time = rest_field(name="timeValue")
514
-
515
- @overload
516
- def __init__(self, *, time_value: datetime.time): ...
517
-
518
- @overload
519
- def __init__(self, mapping: Mapping[str, Any], /): ...
520
-
521
- def __init__(self, *args, **kwargs):
522
- super().__init__(*args, **kwargs)
523
-
524
- val_str = "11:34:56"
525
- val = datetime.time(11, 34, 56)
526
- model = TimeModel({"timeValue": val_str})
527
- assert model["timeValue"] == val_str
528
- assert model.time_value == val
529
-
530
- class BaseModel(Model):
531
- my_prop: TimeModel = rest_field(name="myProp")
532
-
533
- model = BaseModel({"myProp": {"timeValue": val_str}})
534
- assert isinstance(model.my_prop, TimeModel)
535
- assert model.my_prop["timeValue"] == model["myProp"]["timeValue"] == val_str
536
- assert model.my_prop.time_value == val
537
-
538
-
539
- class SimpleRecursiveModel(Model):
540
- name: str = rest_field()
541
- me: "SimpleRecursiveModel" = rest_field()
542
-
543
- @overload
544
- def __init__(self, *, name: str, me: "SimpleRecursiveModel"): ...
545
-
546
- @overload
547
- def __init__(self, mapping: Mapping[str, Any], /): ...
548
-
549
- def __init__(self, *args, **kwargs):
550
- super().__init__(*args, **kwargs)
551
-
552
-
553
- def test_model_recursion():
554
- dict_response = {"name": "Snoopy", "me": {"name": "Egg", "me": {"name": "Chicken"}}}
555
-
556
- model = SimpleRecursiveModel(dict_response)
557
- assert model["name"] == model.name == "Snoopy"
558
- assert model["me"] == {"name": "Egg", "me": {"name": "Chicken"}}
559
- assert isinstance(model.me, SimpleRecursiveModel)
560
- assert model.me["name"] == model.me.name == "Egg"
561
- assert model.me["me"] == {"name": "Chicken"}
562
- assert model.me.me.name == "Chicken"
563
-
564
-
565
- def test_dictionary_deserialization():
566
- class DictionaryModel(Model):
567
- prop: dict[str, datetime.datetime] = rest_field()
568
-
569
- @overload
570
- def __init__(self, *, prop: datetime.datetime): ...
571
-
572
- @overload
573
- def __init__(self, mapping: Mapping[str, Any], /): ...
574
-
575
- def __init__(self, *args, **kwargs):
576
- super().__init__(*args, **kwargs)
577
-
578
- val_str = "9999-12-31T23:59:59.999Z"
579
- val = isodate.parse_datetime(val_str)
580
- dict_response = {"prop": {"datetime": val_str}}
581
- model = DictionaryModel(dict_response)
582
- assert model["prop"] == {"datetime": val_str}
583
- assert model.prop == {"datetime": val}
584
-
585
-
586
- def test_attr_and_rest_case():
587
- class ModelTest(Model):
588
- our_attr: str = rest_field(name="ourAttr")
589
-
590
- @overload
591
- def __init__(self, *, our_attr: str): ...
592
-
593
- @overload
594
- def __init__(self, mapping: Mapping[str, Any], /): ...
595
-
596
- def __init__(self, *args, **kwargs):
597
- super().__init__(*args, **kwargs)
598
-
599
- test_model = ModelTest({"ourAttr": "camel"})
600
- assert test_model.our_attr == test_model["ourAttr"] == "camel"
601
-
602
- test_model = ModelTest(ModelTest({"ourAttr": "camel"}))
603
- assert test_model.our_attr == test_model["ourAttr"] == "camel"
604
-
605
- test_model = ModelTest(our_attr="snake")
606
- assert test_model.our_attr == test_model["ourAttr"] == "snake"
607
-
608
-
609
- def test_dictionary_deserialization_model():
610
- class DictionaryModel(Model):
611
- prop: dict[str, Pet] = rest_field()
612
-
613
- @overload
614
- def __init__(self, *, prop: dict[str, Pet]): ...
615
-
616
- @overload
617
- def __init__(self, mapping: Mapping[str, Any], /): ...
618
-
619
- def __init__(self, *args, **kwargs):
620
- super().__init__(*args, **kwargs)
621
-
622
- dict_response = {
623
- "prop": {
624
- "Eugene": {
625
- "name": "Eugene",
626
- "species": "Dog",
627
- },
628
- "Lady": {
629
- "name": "Lady",
630
- "species": "Newt",
631
- },
632
- }
633
- }
634
-
635
- model = DictionaryModel(dict_response)
636
- assert model["prop"] == {
637
- "Eugene": {
638
- "name": "Eugene",
639
- "species": "Dog",
640
- },
641
- "Lady": {
642
- "name": "Lady",
643
- "species": "Newt",
644
- },
645
- }
646
- assert model.prop == {
647
- "Eugene": Pet({"name": "Eugene", "species": "Dog"}),
648
- "Lady": Pet({"name": "Lady", "species": "Newt"}),
649
- }
650
- assert model.prop["Eugene"].name == model.prop["Eugene"]["name"] == "Eugene"
651
- assert model.prop["Eugene"].species == model.prop["Eugene"]["species"] == "Dog"
652
- assert model.prop["Lady"].name == model.prop["Lady"]["name"] == "Lady"
653
- assert model.prop["Lady"].species == model.prop["Lady"]["species"] == "Newt"
654
-
655
-
656
- def test_list_deserialization():
657
- class ListModel(Model):
658
- prop: list[datetime.datetime] = rest_field()
659
-
660
- @overload
661
- def __init__(self, *, prop: list[datetime.datetime]): ...
662
-
663
- @overload
664
- def __init__(self, mapping: Mapping[str, Any], /): ...
665
-
666
- def __init__(self, *args, **kwargs):
667
- super().__init__(*args, **kwargs)
668
-
669
- val_str = "9999-12-31T23:59:59.999Z"
670
- val = isodate.parse_datetime(val_str)
671
- dict_response = {"prop": [val_str, val_str]}
672
- model = ListModel(dict_response)
673
- assert model["prop"] == [val_str, val_str]
674
- assert model.prop == [val, val]
675
-
676
-
677
- def test_list_deserialization_model():
678
- class ListModel(Model):
679
- prop: list[Pet] = rest_field()
680
-
681
- @overload
682
- def __init__(self, *, prop: list[Pet]): ...
683
-
684
- @overload
685
- def __init__(self, mapping: Mapping[str, Any], /): ...
686
-
687
- def __init__(self, *args, **kwargs):
688
- super().__init__(*args, **kwargs)
689
-
690
- dict_response = {
691
- "prop": [
692
- {"name": "Eugene", "species": "Dog"},
693
- {"name": "Lady", "species": "Newt"},
694
- ]
695
- }
696
- model = ListModel(dict_response)
697
- assert model["prop"] == [
698
- {"name": "Eugene", "species": "Dog"},
699
- {"name": "Lady", "species": "Newt"},
700
- ]
701
- assert model.prop == [
702
- Pet({"name": "Eugene", "species": "Dog"}),
703
- Pet({"name": "Lady", "species": "Newt"}),
704
- ]
705
- assert len(model.prop) == 2
706
- assert model.prop[0].name == model.prop[0]["name"] == "Eugene"
707
- assert model.prop[0].species == model.prop[0]["species"] == "Dog"
708
- assert model.prop[1].name == model.prop[1]["name"] == "Lady"
709
- assert model.prop[1].species == model.prop[1]["species"] == "Newt"
710
-
711
-
712
- def test_set_deserialization():
713
- class SetModel(Model):
714
- prop: set[datetime.datetime] = rest_field()
715
-
716
- @overload
717
- def __init__(self, *, prop: set[datetime.datetime]): ...
718
-
719
- @overload
720
- def __init__(self, mapping: Mapping[str, Any], /): ...
721
-
722
- def __init__(self, *args, **kwargs):
723
- super().__init__(*args, **kwargs)
724
-
725
- val_str = "9999-12-31T23:59:59.999Z"
726
- val = isodate.parse_datetime(val_str)
727
- dict_response = {"prop": set([val_str, val_str])}
728
- model = SetModel(dict_response)
729
- assert model["prop"] == set([val_str, val_str])
730
- assert model.prop == set([val, val])
731
-
732
-
733
- def test_tuple_deserialization():
734
- class TupleModel(Model):
735
- prop: tuple[str, datetime.datetime] = rest_field()
736
-
737
- @overload
738
- def __init__(self, *, prop: tuple[str, datetime.datetime]): ...
739
-
740
- @overload
741
- def __init__(self, mapping: Mapping[str, Any], /): ...
742
-
743
- def __init__(self, *args, **kwargs):
744
- super().__init__(*args, **kwargs)
745
-
746
- val_str = "9999-12-31T23:59:59.999Z"
747
- val = isodate.parse_datetime(val_str)
748
- dict_response = {"prop": (val_str, val_str)}
749
- model = TupleModel(dict_response)
750
- assert model["prop"] == (val_str, val_str)
751
- assert model.prop == (val_str, val)
752
-
753
-
754
- def test_list_of_tuple_deserialization_model():
755
- class Owner(Model):
756
- name: str = rest_field()
757
- pet: Pet = rest_field()
758
-
759
- @overload
760
- def __init__(self, *, name: str, pet: Pet): ...
761
-
762
- @overload
763
- def __init__(self, mapping: Mapping[str, Any], /): ...
764
-
765
- def __init__(self, *args, **kwargs):
766
- super().__init__(*args, **kwargs)
767
-
768
- class ListOfTupleModel(Model):
769
- prop: list[tuple[Pet, Owner]] = rest_field()
770
-
771
- @overload
772
- def __init__(self, *, prop: list[tuple[Pet, Owner]]): ...
773
-
774
- @overload
775
- def __init__(self, mapping: Mapping[str, Any], /): ...
776
-
777
- def __init__(self, *args, **kwargs):
778
- super().__init__(*args, **kwargs)
779
-
780
- eugene = {"name": "Eugene", "species": "Dog"}
781
- lady = {"name": "Lady", "species": "Newt"}
782
- giacamo = {"name": "Giacamo", "pet": eugene}
783
- elizabeth = {"name": "Elizabeth", "pet": lady}
784
-
785
- dict_response: dict[str, Any] = {"prop": [(eugene, giacamo), (lady, elizabeth)]}
786
- model = ListOfTupleModel(dict_response)
787
- assert (
788
- model["prop"]
789
- == model.prop
790
- == [(eugene, giacamo), (lady, elizabeth)]
791
- == [(Pet(eugene), Owner(giacamo)), (Pet(lady), Owner(elizabeth))]
792
- )
793
- assert len(model.prop[0]) == len(model["prop"][0]) == 2
794
- assert model.prop[0][0].name == model.prop[0][0]["name"] == "Eugene"
795
- assert model.prop[0][0].species == model.prop[0][0]["species"] == "Dog"
796
- assert model.prop[0][1].name == "Giacamo"
797
- assert model.prop[0][1].pet == model.prop[0][0]
798
- assert model.prop[0][1].pet.name == model.prop[0][1]["pet"]["name"] == "Eugene"
799
- assert model.prop[1][0] == model.prop[1][1].pet
800
-
801
-
802
- class RecursiveModel(Model):
803
- name: str = rest_field()
804
- list_of_me: Optional[list["RecursiveModel"]] = rest_field(name="listOfMe")
805
- dict_of_me: Optional[dict[str, "RecursiveModel"]] = rest_field(name="dictOfMe")
806
- dict_of_list_of_me: Optional[dict[str, list["RecursiveModel"]]] = rest_field(name="dictOfListOfMe")
807
- list_of_dict_of_me: Optional[list[dict[str, "RecursiveModel"]]] = rest_field(name="listOfDictOfMe")
808
-
809
- @overload
810
- def __init__(
811
- self,
812
- *,
813
- name: str,
814
- list_of_me: Optional[list["RecursiveModel"]] = None,
815
- dict_of_me: Optional[dict[str, "RecursiveModel"]] = None,
816
- dict_of_list_of_me: Optional[dict[str, list["RecursiveModel"]]] = None,
817
- list_of_dict_of_me: Optional[list[dict[str, "RecursiveModel"]]] = None,
818
- ): ...
819
-
820
- @overload
821
- def __init__(self, mapping: Mapping[str, Any], /): ...
822
-
823
- def __init__(self, *args, **kwargs):
824
- super().__init__(*args, **kwargs)
825
-
826
-
827
- def test_model_recursion_complex():
828
- dict_response = {
829
- "name": "it's me!",
830
- "listOfMe": [
831
- {
832
- "name": "it's me!",
833
- "listOfMe": None,
834
- "dictOfMe": None,
835
- "dictOfListOfMe": None,
836
- "listOfDictOfMe": None,
837
- }
838
- ],
839
- "dictOfMe": {
840
- "me": {
841
- "name": "it's me!",
842
- "listOfMe": None,
843
- "dictOfMe": None,
844
- "dictOfListOfMe": None,
845
- "listOfDictOfMe": None,
846
- }
847
- },
848
- "dictOfListOfMe": {
849
- "many mes": [
850
- {
851
- "name": "it's me!",
852
- "listOfMe": None,
853
- "dictOfMe": None,
854
- "dictOfListOfMe": None,
855
- "listOfDictOfMe": None,
856
- }
857
- ]
858
- },
859
- "listOfDictOfMe": [
860
- {
861
- "me": {
862
- "name": "it's me!",
863
- "listOfMe": None,
864
- "dictOfMe": None,
865
- "dictOfListOfMe": None,
866
- "listOfDictOfMe": None,
867
- }
868
- }
869
- ],
870
- }
871
-
872
- model = RecursiveModel(dict_response)
873
- assert model.name == model["name"] == "it's me!"
874
- assert model["listOfMe"] == [
875
- {
876
- "name": "it's me!",
877
- "listOfMe": None,
878
- "dictOfMe": None,
879
- "dictOfListOfMe": None,
880
- "listOfDictOfMe": None,
881
- }
882
- ]
883
- assert model.list_of_me == [
884
- RecursiveModel(
885
- {
886
- "name": "it's me!",
887
- "listOfMe": None,
888
- "dictOfMe": None,
889
- "dictOfListOfMe": None,
890
- "listOfDictOfMe": None,
891
- }
892
- )
893
- ]
894
- assert model.list_of_me
895
- assert model.list_of_me[0].name == "it's me!"
896
- assert model.list_of_me[0].list_of_me is None
897
- assert isinstance(model.list_of_me, list)
898
- assert isinstance(model.list_of_me[0], RecursiveModel)
899
-
900
- assert model["dictOfMe"] == {
901
- "me": {
902
- "name": "it's me!",
903
- "listOfMe": None,
904
- "dictOfMe": None,
905
- "dictOfListOfMe": None,
906
- "listOfDictOfMe": None,
907
- }
908
- }
909
- assert model.dict_of_me == {
910
- "me": RecursiveModel(
911
- {
912
- "name": "it's me!",
913
- "listOfMe": None,
914
- "dictOfMe": None,
915
- "dictOfListOfMe": None,
916
- "listOfDictOfMe": None,
917
- }
918
- )
919
- }
920
-
921
- assert isinstance(model.dict_of_me, dict)
922
- assert isinstance(model.dict_of_me["me"], RecursiveModel)
923
-
924
- assert model["dictOfListOfMe"] == {
925
- "many mes": [
926
- {
927
- "name": "it's me!",
928
- "listOfMe": None,
929
- "dictOfMe": None,
930
- "dictOfListOfMe": None,
931
- "listOfDictOfMe": None,
932
- }
933
- ]
934
- }
935
- assert model.dict_of_list_of_me == {
936
- "many mes": [
937
- RecursiveModel(
938
- {
939
- "name": "it's me!",
940
- "listOfMe": None,
941
- "dictOfMe": None,
942
- "dictOfListOfMe": None,
943
- "listOfDictOfMe": None,
944
- }
945
- )
946
- ]
947
- }
948
- assert isinstance(model.dict_of_list_of_me, dict)
949
- assert isinstance(model.dict_of_list_of_me["many mes"], list)
950
- assert isinstance(model.dict_of_list_of_me["many mes"][0], RecursiveModel)
951
-
952
- assert model["listOfDictOfMe"] == [
953
- {
954
- "me": {
955
- "name": "it's me!",
956
- "listOfMe": None,
957
- "dictOfMe": None,
958
- "dictOfListOfMe": None,
959
- "listOfDictOfMe": None,
960
- }
961
- }
962
- ]
963
- assert model.list_of_dict_of_me == [
964
- {
965
- "me": RecursiveModel(
966
- {
967
- "name": "it's me!",
968
- "listOfMe": None,
969
- "dictOfMe": None,
970
- "dictOfListOfMe": None,
971
- "listOfDictOfMe": None,
972
- }
973
- )
974
- }
975
- ]
976
- assert isinstance(model.list_of_dict_of_me, list)
977
- assert isinstance(model.list_of_dict_of_me[0], dict)
978
- assert isinstance(model.list_of_dict_of_me[0]["me"], RecursiveModel)
979
-
980
- assert model.as_dict() == model == dict_response
981
-
982
-
983
- def test_literals():
984
- class LiteralModel(Model):
985
- species: Literal["Mongoose", "Eagle", "Penguin"] = rest_field()
986
- age: Literal[1, 2, 3] = rest_field()
987
-
988
- @overload
989
- def __init__(
990
- self,
991
- *,
992
- species: Literal["Mongoose", "Eagle", "Penguin"],
993
- age: Literal[1, 2, 3],
994
- ): ...
995
-
996
- @overload
997
- def __init__(self, mapping: Mapping[str, Any], /): ...
998
-
999
- def __init__(self, *args, **kwargs):
1000
- super().__init__(*args, **kwargs)
1001
-
1002
- dict_response = {"species": "Mongoose", "age": 3}
1003
- model = LiteralModel(dict_response)
1004
- assert model.species == model["species"] == "Mongoose"
1005
- assert model.age == model["age"] == 3
1006
-
1007
- dict_response = {"species": "invalid", "age": 5}
1008
- model = LiteralModel(dict_response)
1009
- assert model["species"] == "invalid"
1010
- assert model["age"] == 5
1011
-
1012
- assert model.species == "invalid"
1013
-
1014
- assert model.age == 5
1015
-
1016
-
1017
- def test_deserialization_callback_override():
1018
- def _callback(obj):
1019
- return [str(entry) for entry in obj]
1020
-
1021
- class MyModel(Model):
1022
- prop: Sequence[float] = rest_field()
1023
-
1024
- @overload
1025
- def __init__(self, *, prop: Sequence[float]): ...
1026
-
1027
- @overload
1028
- def __init__(self, mapping: Mapping[str, Any], /): ...
1029
-
1030
- def __init__(self, *args, **kwargs):
1031
- super().__init__(*args, **kwargs)
1032
-
1033
- model_without_callback = MyModel(prop=[1.3, 2.4, 3.5])
1034
- assert model_without_callback.prop == [1.3, 2.4, 3.5]
1035
- assert model_without_callback["prop"] == [1.3, 2.4, 3.5]
1036
-
1037
- class MyModel2(Model):
1038
- prop: Sequence[int] = rest_field(type=_callback)
1039
-
1040
- @overload
1041
- def __init__(self, *, prop: Any): ...
1042
-
1043
- @overload
1044
- def __init__(self, mapping: Mapping[str, Any], /): ...
1045
-
1046
- def __init__(self, *args, **kwargs):
1047
- super().__init__(*args, **kwargs)
1048
-
1049
- model_with_callback = MyModel2(prop=[1.3, 2.4, 3.5])
1050
- assert model_with_callback.prop == ["1.3", "2.4", "3.5"]
1051
- assert model_with_callback["prop"] == ["1.3", "2.4", "3.5"]
1052
-
1053
-
1054
- def test_deserialization_callback_override_parent():
1055
- class ParentNoCallback(Model):
1056
- prop: Sequence[float] = rest_field()
1057
-
1058
- @overload
1059
- def __init__(self, *, prop: Sequence[float]): ...
1060
-
1061
- @overload
1062
- def __init__(self, mapping: Mapping[str, Any], /): ...
1063
-
1064
- def __init__(self, *args, **kwargs):
1065
- super().__init__(*args, **kwargs)
1066
-
1067
- def _callback(obj):
1068
- return set([str(entry) for entry in obj])
1069
-
1070
- class ChildWithCallback(ParentNoCallback):
1071
- prop: Sequence[float] = rest_field(type=_callback)
1072
-
1073
- @overload
1074
- def __init__(self, *, prop: Sequence[float]): ...
1075
-
1076
- @overload
1077
- def __init__(self, mapping: Mapping[str, Any], /): ...
1078
-
1079
- def __init__(self, *args, **kwargs):
1080
- super().__init__(*args, **kwargs)
1081
-
1082
- parent_model = ParentNoCallback(prop=[1, 1, 2, 3])
1083
- assert parent_model.prop == parent_model["prop"] == [1, 1, 2, 3]
1084
-
1085
- child_model = ChildWithCallback(prop=[1, 1, 2, 3])
1086
- assert child_model.prop == set(["1", "1", "2", "3"])
1087
- assert child_model["prop"] == set(["1", "1", "2", "3"])
1088
-
1089
-
1090
- def test_inheritance_basic():
1091
- def _callback(obj):
1092
- return [str(e) for e in obj]
1093
-
1094
- class Parent(Model):
1095
- parent_prop: list[int] = rest_field(name="parentProp", type=_callback)
1096
- prop: str = rest_field()
1097
-
1098
- @overload
1099
- def __init__(self, *, parent_prop: list[int], prop: str): ...
1100
-
1101
- @overload
1102
- def __init__(self, mapping: Mapping[str, Any], /): ...
1103
-
1104
- def __init__(self, *args, **kwargs):
1105
- super().__init__(*args, **kwargs)
1106
-
1107
- class Child(Parent):
1108
- pass
1109
-
1110
- c = Child(parent_prop=[1, 2, 3], prop="hello")
1111
- assert c == {"parentProp": [1, 2, 3], "prop": "hello"}
1112
- assert c.parent_prop == ["1", "2", "3"]
1113
- assert c.prop == "hello"
1114
-
1115
-
1116
- class ParentA(Model):
1117
- prop: float = rest_field()
1118
-
1119
- @overload
1120
- def __init__(self, *, prop: Any): ...
1121
-
1122
- @overload
1123
- def __init__(self, mapping: Mapping[str, Any], /): ...
1124
-
1125
- def __init__(self, *args, **kwargs):
1126
- super().__init__(*args, **kwargs)
1127
-
1128
-
1129
- class ParentB(ParentA):
1130
- prop: str = rest_field()
1131
- bcd_prop: Optional[list["ParentB"]] = rest_field(name="bcdProp")
1132
-
1133
- @overload
1134
- def __init__(self, *, prop: Any, bcd_prop: Optional[list["ParentB"]] = None): ...
1135
-
1136
- @overload
1137
- def __init__(self, mapping: Mapping[str, Any], /): ...
1138
-
1139
- def __init__(self, *args, **kwargs):
1140
- super().__init__(*args, **kwargs)
1141
-
1142
-
1143
- class ParentC(ParentB):
1144
- prop: float = rest_field()
1145
- cd_prop: ParentA = rest_field(name="cdProp")
1146
-
1147
- @overload
1148
- def __init__(self, *, prop: Any, bcd_prop: list[ParentB], cd_prop: ParentA, **kwargs): ...
1149
-
1150
- @overload
1151
- def __init__(self, mapping: Mapping[str, Any], /): ...
1152
-
1153
- def __init__(self, *args, **kwargs):
1154
- super().__init__(*args, **kwargs)
1155
-
1156
-
1157
- class ChildD(ParentC):
1158
- d_prop: tuple[ParentA, ParentB, ParentC, Optional["ChildD"]] = rest_field(name="dProp")
1159
-
1160
- @overload
1161
- def __init__(
1162
- self,
1163
- *,
1164
- prop: Any,
1165
- bcd_prop: list[ParentB],
1166
- cd_prop: ParentA,
1167
- d_prop: tuple[ParentA, ParentB, ParentC, Optional["ChildD"]],
1168
- ): ...
1169
-
1170
- @overload
1171
- def __init__(self, mapping: Mapping[str, Any], /): ...
1172
-
1173
- def __init__(self, *args, **kwargs):
1174
- super().__init__(*args, **kwargs)
1175
-
1176
-
1177
- def test_model_dict_comparisons():
1178
- class Inner(Model):
1179
- prop: str = rest_field()
1180
-
1181
- @overload
1182
- def __init__(
1183
- self,
1184
- *,
1185
- prop: str,
1186
- ): ...
1187
-
1188
- @overload
1189
- def __init__(self, mapping: Mapping[str, Any], /): ...
1190
-
1191
- def __init__(self, *args, **kwargs):
1192
- super().__init__(*args, **kwargs)
1193
-
1194
- class Outer(Model):
1195
- inner: Inner = rest_field()
1196
-
1197
- @overload
1198
- def __init__(
1199
- self,
1200
- *,
1201
- inner: Inner,
1202
- ): ...
1203
-
1204
- @overload
1205
- def __init__(self, mapping: Mapping[str, Any], /): ...
1206
-
1207
- def __init__(self, *args, **kwargs):
1208
- super().__init__(*args, **kwargs)
1209
-
1210
- def _tests(outer):
1211
- assert outer.inner.prop == outer["inner"].prop == outer.inner["prop"] == outer["inner"]["prop"] == "hello"
1212
- assert outer.inner == outer["inner"] == {"prop": "hello"}
1213
- assert outer == {"inner": {"prop": "hello"}}
1214
-
1215
- _tests(Outer(inner=Inner(prop="hello")))
1216
- _tests(Outer({"inner": {"prop": "hello"}}))
1217
-
1218
-
1219
- def test_model_dict_comparisons_list():
1220
- class Inner(Model):
1221
- prop: str = rest_field()
1222
-
1223
- @overload
1224
- def __init__(
1225
- self,
1226
- *,
1227
- prop: str,
1228
- ): ...
1229
-
1230
- @overload
1231
- def __init__(self, mapping: Mapping[str, Any], /): ...
1232
-
1233
- def __init__(self, *args, **kwargs):
1234
- super().__init__(*args, **kwargs)
1235
-
1236
- class Outer(Model):
1237
- inner: list[Inner] = rest_field()
1238
-
1239
- @overload
1240
- def __init__(
1241
- self,
1242
- *,
1243
- inner: list[Inner],
1244
- ): ...
1245
-
1246
- @overload
1247
- def __init__(self, mapping: Mapping[str, Any], /): ...
1248
-
1249
- def __init__(self, *args, **kwargs):
1250
- super().__init__(*args, **kwargs)
1251
-
1252
- def _tests(outer):
1253
- assert (
1254
- outer.inner[0].prop
1255
- == outer["inner"][0].prop
1256
- == outer.inner[0]["prop"]
1257
- == outer["inner"][0]["prop"]
1258
- == "hello"
1259
- )
1260
- assert outer.inner == outer["inner"] == [{"prop": "hello"}]
1261
- assert outer == {"inner": [{"prop": "hello"}]}
1262
-
1263
- _tests(Outer(inner=[Inner(prop="hello")]))
1264
- _tests(Outer({"inner": [{"prop": "hello"}]}))
1265
-
1266
-
1267
- def test_model_dict_comparisons_dict():
1268
- class Inner(Model):
1269
- prop: str = rest_field()
1270
-
1271
- @overload
1272
- def __init__(
1273
- self,
1274
- *,
1275
- prop: str,
1276
- ): ...
1277
-
1278
- @overload
1279
- def __init__(self, mapping: Mapping[str, Any], /): ...
1280
-
1281
- def __init__(self, *args, **kwargs):
1282
- super().__init__(*args, **kwargs)
1283
-
1284
- class Outer(Model):
1285
- inner: dict[str, Inner] = rest_field()
1286
-
1287
- @overload
1288
- def __init__(
1289
- self,
1290
- *,
1291
- inner: dict[str, Inner],
1292
- ): ...
1293
-
1294
- @overload
1295
- def __init__(self, mapping: Mapping[str, Any], /): ...
1296
-
1297
- def __init__(self, *args, **kwargs):
1298
- super().__init__(*args, **kwargs)
1299
-
1300
- def _tests(outer):
1301
- assert (
1302
- outer.inner["key"].prop
1303
- == outer["inner"]["key"].prop
1304
- == outer.inner["key"]["prop"]
1305
- == outer["inner"]["key"]["prop"]
1306
- == "hello"
1307
- )
1308
- assert outer.inner == outer["inner"] == {"key": {"prop": "hello"}}
1309
- with pytest.raises(AttributeError):
1310
- outer.inner.key
1311
- assert outer.inner["key"] == outer["inner"]["key"] == {"prop": "hello"}
1312
- assert outer == {"inner": {"key": {"prop": "hello"}}}
1313
-
1314
- _tests(Outer(inner={"key": Inner(prop="hello")}))
1315
- _tests(Outer({"inner": {"key": {"prop": "hello"}}}))
1316
-
1317
-
1318
- def test_inheritance_4_levels():
1319
- a = ParentA(prop=3.4)
1320
- assert a.prop == 3.4
1321
- assert a["prop"] == 3.4
1322
- assert a == {"prop": 3.4}
1323
- assert isinstance(a, Model)
1324
-
1325
- b = ParentB(prop=3.4, bcd_prop=[ParentB(prop=4.3)])
1326
- assert b.prop == "3.4"
1327
- assert b["prop"] == 3.4
1328
- assert b.bcd_prop == [ParentB(prop=4.3)]
1329
- assert b["bcdProp"] != [{"prop": 4.3, "bcdProp": None}]
1330
- assert b["bcdProp"] == [{"prop": 4.3}]
1331
- assert b.bcd_prop
1332
- assert b.bcd_prop[0].prop == "4.3"
1333
- assert b.bcd_prop[0].bcd_prop is None
1334
- assert b == {"prop": 3.4, "bcdProp": [{"prop": 4.3}]}
1335
- assert isinstance(b, ParentB)
1336
- assert isinstance(b, ParentA)
1337
-
1338
- c = ParentC(prop=3.4, bcd_prop=[b], cd_prop=a)
1339
- assert c.prop == c["prop"] == 3.4
1340
- assert c.bcd_prop == [b]
1341
- assert c.bcd_prop
1342
- assert isinstance(c.bcd_prop[0], ParentB)
1343
- assert c["bcdProp"] == [b] == [{"prop": 3.4, "bcdProp": [{"prop": 4.3}]}]
1344
- assert c.cd_prop == a
1345
- assert c["cdProp"] == a == {"prop": 3.4}
1346
- assert isinstance(c.cd_prop, ParentA)
1347
-
1348
- d = ChildD(
1349
- prop=3.4,
1350
- bcd_prop=[b],
1351
- cd_prop=a,
1352
- d_prop=(
1353
- a,
1354
- b,
1355
- c,
1356
- ChildD(prop=3.4, bcd_prop=[b], cd_prop=a, d_prop=(a, b, c, None)),
1357
- ),
1358
- )
1359
- assert d == {
1360
- "prop": 3.4,
1361
- "bcdProp": [b],
1362
- "cdProp": a,
1363
- "dProp": (
1364
- a,
1365
- b,
1366
- c,
1367
- {"prop": 3.4, "bcdProp": [b], "cdProp": a, "dProp": (a, b, c, None)},
1368
- ),
1369
- }
1370
- assert d.prop == d["prop"] == 3.4
1371
- assert d.bcd_prop == [b]
1372
- assert d.bcd_prop
1373
- assert isinstance(d.bcd_prop[0], ParentB)
1374
- assert d.cd_prop == a
1375
- assert isinstance(d.cd_prop, ParentA)
1376
- assert d.d_prop[0] == a # at a
1377
- assert isinstance(d.d_prop[0], ParentA)
1378
- assert d.d_prop[1] == b
1379
- assert isinstance(d.d_prop[1], ParentB)
1380
- assert d.d_prop[2] == c
1381
- assert isinstance(d.d_prop[2], ParentC)
1382
- assert isinstance(d.d_prop[3], ChildD)
1383
-
1384
- assert isinstance(d.d_prop[3].d_prop[0], ParentA)
1385
- assert isinstance(d.d_prop[3].d_prop[1], ParentB)
1386
- assert isinstance(d.d_prop[3].d_prop[2], ParentC)
1387
- assert d.d_prop[3].d_prop[3] is None
1388
-
1389
-
1390
- def test_multiple_inheritance_basic():
1391
- class ParentOne(Model):
1392
- parent_one_prop: str = rest_field(name="parentOneProp")
1393
-
1394
- @overload
1395
- def __init__(
1396
- self,
1397
- *,
1398
- parent_one_prop: str,
1399
- ): ...
1400
-
1401
- @overload
1402
- def __init__(self, mapping: Mapping[str, Any], /): ...
1403
-
1404
- def __init__(self, *args, **kwargs):
1405
- super().__init__(*args, **kwargs)
1406
-
1407
- class ParentTwo(Model):
1408
- parent_two_prop: int = rest_field(name="parentTwoProp", type=lambda x: str(x))
1409
-
1410
- @overload
1411
- def __init__(
1412
- self,
1413
- *,
1414
- parent_two_prop: int,
1415
- ): ...
1416
-
1417
- @overload
1418
- def __init__(self, mapping: Mapping[str, Any], /): ...
1419
-
1420
- def __init__(self, *args, **kwargs):
1421
- super().__init__(*args, **kwargs)
1422
-
1423
- class Child(ParentOne, ParentTwo):
1424
- @overload
1425
- def __init__(
1426
- self,
1427
- *,
1428
- parent_one_prop: str,
1429
- parent_two_prop: int,
1430
- ): ...
1431
-
1432
- @overload
1433
- def __init__(self, mapping: Mapping[str, Any], /): ...
1434
-
1435
- def __init__(self, *args, **kwargs):
1436
- super().__init__(*args, **kwargs)
1437
-
1438
- c = Child(parent_one_prop="Hello", parent_two_prop=3)
1439
- assert c == {"parentOneProp": "Hello", "parentTwoProp": 3}
1440
- assert c.parent_one_prop == "Hello"
1441
- assert c.parent_two_prop == "3"
1442
- assert isinstance(c, Child)
1443
- assert isinstance(c, ParentOne)
1444
- assert isinstance(c, ParentTwo)
1445
-
1446
-
1447
- def test_multiple_inheritance_mro():
1448
- class A(Model):
1449
- prop: str = rest_field()
1450
-
1451
- @overload
1452
- def __init__(self, *, prop: str) -> None: ...
1453
-
1454
- @overload
1455
- def __init__(self, mapping: Mapping[str, Any], /): ...
1456
-
1457
- def __init__(self, *args, **kwargs):
1458
- super().__init__(*args, **kwargs)
1459
-
1460
- class B(Model):
1461
- prop: int = rest_field(type=lambda x: int(x))
1462
-
1463
- @overload
1464
- def __init__(self, *, prop: str) -> None: ...
1465
-
1466
- @overload
1467
- def __init__(self, mapping: Mapping[str, Any], /): ...
1468
-
1469
- def __init__(self, *args, **kwargs):
1470
- super().__init__(*args, **kwargs)
1471
-
1472
- class C(A, B):
1473
- pass
1474
-
1475
- assert A(prop="1").prop == "1"
1476
- assert B(prop="1").prop == 1
1477
- assert C(prop="1").prop == "1" # A should take precedence over B
1478
-
1479
-
1480
- class Feline(Model):
1481
- meows: bool = rest_field()
1482
- hisses: bool = rest_field()
1483
- siblings: Optional[list["Feline"]] = rest_field()
1484
-
1485
- @overload
1486
- def __init__(
1487
- self,
1488
- *,
1489
- meows: bool,
1490
- hisses: bool,
1491
- siblings: Optional[list["Feline"]] = None,
1492
- ): ...
1493
-
1494
- @overload
1495
- def __init__(self, mapping: Mapping[str, Any], /): ...
1496
-
1497
- def __init__(self, *args, **kwargs):
1498
- super().__init__(*args, **kwargs)
1499
-
1500
-
1501
- class Owner(Model):
1502
- first_name: str = rest_field(name="firstName", type=lambda x: x.capitalize())
1503
- last_name: str = rest_field(name="lastName", type=lambda x: x.capitalize())
1504
-
1505
- @overload
1506
- def __init__(
1507
- self,
1508
- *,
1509
- first_name: str,
1510
- last_name: str,
1511
- ): ...
1512
-
1513
- @overload
1514
- def __init__(self, mapping: Mapping[str, Any], /): ...
1515
-
1516
- def __init__(self, *args, **kwargs):
1517
- super().__init__(*args, **kwargs)
1518
-
1519
-
1520
- class PetModel(Model):
1521
- name: str = rest_field()
1522
- owner: Owner = rest_field()
1523
-
1524
- @overload
1525
- def __init__(self, *, name: str, owner: Owner): ...
1526
-
1527
- @overload
1528
- def __init__(self, mapping: Mapping[str, Any], /): ...
1529
-
1530
- def __init__(self, *args, **kwargs):
1531
- super().__init__(*args, **kwargs)
1532
-
1533
-
1534
- class Cat(PetModel, Feline):
1535
- likes_milk: bool = rest_field(name="likesMilk", type=lambda x: True)
1536
-
1537
- @overload
1538
- def __init__(
1539
- self,
1540
- *,
1541
- name: str,
1542
- owner: Owner,
1543
- meows: bool,
1544
- hisses: bool,
1545
- likes_milk: bool,
1546
- siblings: Optional[list[Feline]],
1547
- ): ...
1548
-
1549
- @overload
1550
- def __init__(self, mapping: Mapping[str, Any], /): ...
1551
-
1552
- def __init__(self, *args, **kwargs):
1553
- super().__init__(*args, **kwargs)
1554
-
1555
-
1556
- class CuteThing(Model):
1557
- how_cute_am_i: float = rest_field(name="howCuteAmI")
1558
-
1559
- @overload
1560
- def __init__(self, *, how_cute_am_i: float): ...
1561
-
1562
- @overload
1563
- def __init__(self, mapping: Mapping[str, Any], /): ...
1564
-
1565
- def __init__(self, *args, **kwargs):
1566
- super().__init__(*args, **kwargs)
1567
-
1568
-
1569
- class Kitten(Cat, CuteThing):
1570
- eats_mice_yet: bool = rest_field(name="eatsMiceYet")
1571
-
1572
- @overload
1573
- def __init__(
1574
- self,
1575
- *,
1576
- name: str,
1577
- owner: Owner,
1578
- meows: bool,
1579
- hisses: bool,
1580
- likes_milk: bool,
1581
- siblings: Optional[list[Feline]],
1582
- how_cute_am_i: float,
1583
- eats_mice_yet: bool,
1584
- ): ...
1585
-
1586
- @overload
1587
- def __init__(self, mapping: Mapping[str, Any], /): ...
1588
-
1589
- def __init__(self, *args, **kwargs):
1590
- super().__init__(*args, **kwargs)
1591
-
1592
-
1593
- def test_multiple_inheritance_complex():
1594
- cat = Cat(
1595
- name="Stephanie",
1596
- owner=Owner(first_name="cecil", last_name="cai"), # gets capitalized in attr
1597
- meows=True,
1598
- hisses=True,
1599
- likes_milk=False, # likes_milk will change to True on the attribute
1600
- siblings=[Feline(meows=True, hisses=False)],
1601
- )
1602
- assert cat == {
1603
- "name": "Stephanie",
1604
- "owner": {
1605
- "firstName": "cecil",
1606
- "lastName": "cai",
1607
- },
1608
- "meows": True,
1609
- "hisses": True,
1610
- "likesMilk": False,
1611
- "siblings": [
1612
- {
1613
- "meows": True,
1614
- "hisses": False,
1615
- }
1616
- ],
1617
- }
1618
- assert cat.name == "Stephanie"
1619
- assert isinstance(cat.owner, Owner)
1620
- assert cat.owner.first_name == "Cecil"
1621
- assert cat.owner.last_name == "Cai"
1622
- assert cat.meows
1623
- assert cat.hisses
1624
- assert cat.likes_milk
1625
- assert cat.siblings
1626
- assert len(cat.siblings) == 1
1627
- assert isinstance(cat.siblings[0], Feline)
1628
-
1629
- kitten = Kitten(
1630
- name="Stephanie",
1631
- owner=Owner(first_name="cecil", last_name="cai"), # gets capitalized in attr
1632
- meows=True,
1633
- hisses=True,
1634
- likes_milk=False, # likes_milk will change to True on the attribute
1635
- siblings=[Feline(meows=True, hisses=False)],
1636
- how_cute_am_i=1.0,
1637
- eats_mice_yet=True,
1638
- )
1639
- assert kitten != {
1640
- "name": "Stephanie",
1641
- "owner": {
1642
- "firstName": "cecil",
1643
- "lastName": "cai",
1644
- },
1645
- "meows": True,
1646
- "hisses": True,
1647
- "likesMilk": False,
1648
- "siblings": [{"meows": True, "hisses": False, "siblings": None}], # we don't automatically set None here
1649
- "howCuteAmI": 1.0,
1650
- "eatsMiceYet": True,
1651
- }
1652
- assert kitten == {
1653
- "name": "Stephanie",
1654
- "owner": {
1655
- "firstName": "cecil",
1656
- "lastName": "cai",
1657
- },
1658
- "meows": True,
1659
- "hisses": True,
1660
- "likesMilk": False,
1661
- "siblings": [
1662
- {
1663
- "meows": True,
1664
- "hisses": False,
1665
- }
1666
- ],
1667
- "howCuteAmI": 1.0,
1668
- "eatsMiceYet": True,
1669
- }
1670
- assert kitten.name == "Stephanie"
1671
- assert isinstance(kitten.owner, Owner)
1672
- assert kitten.owner.first_name == "Cecil"
1673
- assert kitten.owner.last_name == "Cai"
1674
- assert kitten.meows
1675
- assert kitten.hisses
1676
- assert kitten.likes_milk
1677
- assert kitten.siblings
1678
- assert len(kitten.siblings) == 1
1679
- assert isinstance(kitten.siblings[0], Feline)
1680
- assert kitten.eats_mice_yet
1681
- assert kitten.how_cute_am_i == 1.0
1682
- assert isinstance(kitten, PetModel)
1683
- assert isinstance(kitten, Cat)
1684
- assert isinstance(kitten, Feline)
1685
- assert isinstance(kitten, CuteThing)
1686
-
1687
-
1688
- class A(Model):
1689
- b: "B" = rest_field()
1690
-
1691
- @overload
1692
- def __init__(self, b: "B"): ...
1693
-
1694
- @overload
1695
- def __init__(self, mapping: Mapping[str, Any], /): ...
1696
-
1697
- def __init__(self, *args, **kwargs):
1698
- super().__init__(*args, **kwargs)
1699
-
1700
-
1701
- class B(Model):
1702
- c: "C" = rest_field()
1703
-
1704
- @overload
1705
- def __init__(self, *, c: "C"): ...
1706
-
1707
- @overload
1708
- def __init__(self, mapping: Mapping[str, Any], /): ...
1709
-
1710
- def __init__(self, *args, **kwargs):
1711
- super().__init__(*args, **kwargs)
1712
-
1713
-
1714
- class C(Model):
1715
- d: str = rest_field()
1716
-
1717
- @overload
1718
- def __init__(self, *, d: str): ...
1719
-
1720
- @overload
1721
- def __init__(self, mapping: Mapping[str, Any], /): ...
1722
-
1723
- def __init__(self, *args, **kwargs):
1724
- super().__init__(*args, **kwargs)
1725
-
1726
-
1727
- def test_nested_creation():
1728
- a = A({"b": {"c": {"d": "hello"}}})
1729
- assert isinstance(a["b"], Model)
1730
- assert isinstance(a["b"]["c"], Model)
1731
- assert a["b"]["c"] == a["b"].c == a.b.c == {"d": "hello"}
1732
-
1733
- assert (
1734
- a["b"]["c"]["d"]
1735
- == a["b"].c.d
1736
- == a.b["c"].d
1737
- == a["b"]["c"].d
1738
- == a["b"].c["d"]
1739
- == a.b["c"]["d"]
1740
- == a.b.c.d
1741
- == "hello"
1742
- )
1743
-
1744
-
1745
- def test_nested_setting():
1746
- a = A({"b": {"c": {"d": "hello"}}})
1747
-
1748
- # set with dict
1749
- a["b"]["c"]["d"] = "setwithdict"
1750
- assert (
1751
- a["b"]["c"]["d"]
1752
- == a["b"].c.d
1753
- == a.b["c"].d
1754
- == a["b"]["c"].d
1755
- == a["b"].c["d"]
1756
- == a.b["c"]["d"]
1757
- == a.b.c.d
1758
- == "setwithdict"
1759
- )
1760
-
1761
- # set with attr
1762
- a.b.c.d = "setwithattr"
1763
- assert a["b"]["c"]["d"] == "setwithattr"
1764
- assert (
1765
- a["b"]["c"]["d"]
1766
- == a["b"].c.d
1767
- == a.b["c"].d
1768
- == a["b"]["c"].d
1769
- == a["b"].c["d"]
1770
- == a.b["c"]["d"]
1771
- == a.b.c.d
1772
- == "setwithattr"
1773
- )
1774
-
1775
-
1776
- class BaseModel(Model):
1777
- inner_model: "InnerModel" = rest_field(name="innerModel")
1778
-
1779
- @overload
1780
- def __init__(self, *, inner_model: "InnerModel"): ...
1781
-
1782
- @overload
1783
- def __init__(self, mapping: Mapping[str, Any], /): ...
1784
-
1785
- def __init__(self, *args, **kwargs):
1786
- super().__init__(*args, **kwargs)
1787
-
1788
-
1789
- class InnerModel(Model):
1790
- datetime_field: datetime.datetime = rest_field(name="datetimeField")
1791
-
1792
- @overload
1793
- def __init__(self, *, datetime_field: datetime.datetime): ...
1794
-
1795
- @overload
1796
- def __init__(self, mapping: Mapping[str, Any], /): ...
1797
-
1798
- def __init__(self, *args, **kwargs):
1799
- super().__init__(*args, **kwargs)
1800
-
1801
-
1802
- def test_nested_deserialization():
1803
- serialized_datetime = "9999-12-31T23:59:59.999Z"
1804
-
1805
- model = BaseModel({"innerModel": {"datetimeField": serialized_datetime}})
1806
- assert model.inner_model["datetimeField"] == model["innerModel"]["datetimeField"] == serialized_datetime
1807
- assert (
1808
- model.inner_model.datetime_field
1809
- == model["innerModel"].datetime_field
1810
- == isodate.parse_datetime(serialized_datetime)
1811
- )
1812
-
1813
- new_serialized_datetime = "2022-12-31T23:59:59.999Z"
1814
- model.inner_model.datetime_field = isodate.parse_datetime(new_serialized_datetime)
1815
- assert model.inner_model["datetimeField"] == "2022-12-31T23:59:59.999000Z"
1816
-
1817
-
1818
- class X(Model):
1819
- y: "Y" = rest_field()
1820
-
1821
- @overload
1822
- def __init__(self, *, y: "Y"): ...
1823
-
1824
- @overload
1825
- def __init__(self, mapping: Mapping[str, Any], /): ...
1826
-
1827
- def __init__(self, *args, **kwargs):
1828
- super().__init__(*args, **kwargs)
1829
-
1830
-
1831
- class Y(Model):
1832
- z: "Z" = rest_field()
1833
-
1834
- @overload
1835
- def __init__(self, *, z: "Z"): ...
1836
-
1837
- @overload
1838
- def __init__(self, mapping: Mapping[str, Any], /): ...
1839
-
1840
- def __init__(self, *args, **kwargs):
1841
- super().__init__(*args, **kwargs)
1842
-
1843
-
1844
- class Z(Model):
1845
- z_val: datetime.datetime = rest_field(name="zVal")
1846
-
1847
- @overload
1848
- def __init__(self, *, z_val: datetime.datetime): ...
1849
-
1850
- @overload
1851
- def __init__(self, mapping: Mapping[str, Any], /): ...
1852
-
1853
- def __init__(self, *args, **kwargs):
1854
- super().__init__(*args, **kwargs)
1855
-
1856
-
1857
- def test_nested_update():
1858
- serialized_datetime = "9999-12-31T23:59:59.999Z"
1859
- parsed_datetime = isodate.parse_datetime(serialized_datetime)
1860
- x = X({"y": {"z": {"zVal": serialized_datetime}}})
1861
- assert x.y.z.z_val == x["y"].z.z_val == x.y["z"].z_val == x["y"]["z"].z_val == parsed_datetime
1862
- assert x.y.z["zVal"] == x.y["z"]["zVal"] == x["y"].z["zVal"] == x["y"]["z"]["zVal"] == serialized_datetime
1863
-
1864
-
1865
- def test_deserialization_is():
1866
- # test without datetime deserialization
1867
- a = A({"b": {"c": {"d": "hello"}}})
1868
- assert a.b is a.b
1869
- assert a.b.c is a.b.c
1870
- assert a.b.c.d is a.b.c.d
1871
-
1872
- serialized_datetime = "9999-12-31T23:59:59.999Z"
1873
- x = X({"y": {"z": {"zVal": serialized_datetime}}})
1874
- assert x.y is x.y
1875
- assert x.y.z is x.y.z
1876
-
1877
- assert x.y.z.z_val == isodate.parse_datetime(serialized_datetime)
1878
-
1879
-
1880
- class InnerModelWithReadonly(Model):
1881
- normal_property: str = rest_field(name="normalProperty")
1882
- readonly_property: str = rest_field(name="readonlyProperty", visibility=["read"])
1883
-
1884
- @overload
1885
- def __init__(self, *, normal_property: str): ...
1886
-
1887
- @overload
1888
- def __init__(self, mapping: Mapping[str, Any], /): ...
1889
-
1890
- def __init__(self, *args, **kwargs):
1891
- super().__init__(*args, **kwargs)
1892
-
1893
-
1894
- class ModelWithReadonly(Model):
1895
- normal_property: str = rest_field(name="normalProperty")
1896
- readonly_property: str = rest_field(name="readonlyProperty", visibility=["read"])
1897
- inner_model: InnerModelWithReadonly = rest_field(name="innerModel")
1898
-
1899
- @overload
1900
- def __init__(self, *, normal_property: str): ...
1901
-
1902
- @overload
1903
- def __init__(self, mapping: Mapping[str, Any], /): ...
1904
-
1905
- def __init__(self, *args, **kwargs):
1906
- super().__init__(*args, **kwargs)
1907
-
1908
-
1909
- def test_readonly():
1910
- # we pass the dict to json, so readonly shouldn't show up in the JSON version
1911
- value = {
1912
- "normalProperty": "normal",
1913
- "readonlyProperty": "readonly",
1914
- "innerModel": {"normalProperty": "normal", "readonlyProperty": "readonly"},
1915
- }
1916
- model = ModelWithReadonly(value)
1917
- assert model.as_dict(exclude_readonly=True) == {
1918
- "normalProperty": "normal",
1919
- "innerModel": {"normalProperty": "normal"},
1920
- }
1921
- assert json.loads(json.dumps(model, cls=SdkJSONEncoder)) == value
1922
- assert model == value
1923
- assert model["readonlyProperty"] == model.readonly_property == "readonly"
1924
- assert model["innerModel"]["readonlyProperty"] == model.inner_model.readonly_property == "readonly"
1925
-
1926
-
1927
- def test_readonly_set():
1928
- value = {
1929
- "normalProperty": "normal",
1930
- "readonlyProperty": "readonly",
1931
- "innerModel": {"normalProperty": "normal", "readonlyProperty": "readonly"},
1932
- }
1933
-
1934
- model = ModelWithReadonly(value)
1935
- assert model.normal_property == model["normalProperty"] == "normal"
1936
- assert model.readonly_property == model["readonlyProperty"] == "readonly"
1937
- assert model.inner_model.normal_property == model.inner_model["normalProperty"] == "normal"
1938
- assert model.inner_model.readonly_property == model.inner_model["readonlyProperty"] == "readonly"
1939
-
1940
- assert model.as_dict(exclude_readonly=True) == {
1941
- "normalProperty": "normal",
1942
- "innerModel": {"normalProperty": "normal"},
1943
- }
1944
- assert json.loads(json.dumps(model, cls=SdkJSONEncoder)) == value
1945
-
1946
- model["normalProperty"] = "setWithDict"
1947
- model["readonlyProperty"] = "setWithDict"
1948
- model.inner_model["normalProperty"] = "setWithDict"
1949
- model.inner_model["readonlyProperty"] = "setWithDict"
1950
-
1951
- assert model.normal_property == model["normalProperty"] == "setWithDict"
1952
- assert model.readonly_property == model["readonlyProperty"] == "setWithDict"
1953
- assert model.inner_model.normal_property == model.inner_model["normalProperty"] == "setWithDict"
1954
- assert model.inner_model.readonly_property == model.inner_model["readonlyProperty"] == "setWithDict"
1955
- assert model.as_dict(exclude_readonly=True) == {
1956
- "normalProperty": "setWithDict",
1957
- "innerModel": {"normalProperty": "setWithDict"},
1958
- }
1959
- assert json.loads(json.dumps(model, cls=SdkJSONEncoder)) == {
1960
- "normalProperty": "setWithDict",
1961
- "readonlyProperty": "setWithDict",
1962
- "innerModel": {
1963
- "normalProperty": "setWithDict",
1964
- "readonlyProperty": "setWithDict",
1965
- },
1966
- }
1967
-
1968
-
1969
- def test_incorrect_initialization():
1970
- class MyModel(Model):
1971
- id: int = rest_field()
1972
- field: str = rest_field()
1973
-
1974
- @overload
1975
- def __init__(
1976
- self,
1977
- *,
1978
- id: int,
1979
- field: str,
1980
- ): ...
1981
-
1982
- @overload
1983
- def __init__(self, mapping: Mapping[str, Any], /): ...
1984
-
1985
- def __init__(self, *args, **kwargs):
1986
- super().__init__(*args, **kwargs)
1987
-
1988
- with pytest.raises(TypeError):
1989
- MyModel(1, "field")
1990
-
1991
- with pytest.raises(TypeError):
1992
- MyModel(id=1, field="field", unknown="me")
1993
-
1994
-
1995
- def test_serialization_initialization_and_setting():
1996
- serialized_datetime = "9999-12-31T23:59:59.999000Z"
1997
- parsed_datetime = isodate.parse_datetime(serialized_datetime)
1998
-
1999
- # pass in parsed
2000
- z = Z(z_val=parsed_datetime)
2001
- assert z.z_val == parsed_datetime
2002
- assert z["zVal"] == serialized_datetime
2003
-
2004
- # pass in dict
2005
- z = Z({"zVal": serialized_datetime})
2006
- assert z.z_val == parsed_datetime
2007
- assert z["zVal"] == serialized_datetime
2008
-
2009
- # assert setting
2010
- serialized_datetime = "2022-12-31T23:59:59.999000Z"
2011
- z.z_val = isodate.parse_datetime(serialized_datetime)
2012
- assert z["zVal"] == serialized_datetime
2013
-
2014
-
2015
- def test_copy_of_input():
2016
- class TestModel(Model):
2017
- data: list[int] = rest_field()
2018
-
2019
- @overload
2020
- def __init__(self, *, data: list[int]): ...
2021
-
2022
- @overload
2023
- def __init__(self, mapping: Mapping[str, Any], /): ...
2024
-
2025
- def __init__(self, *args, **kwargs):
2026
- super().__init__(*args, **kwargs)
2027
-
2028
- raw = [1, 2, 3]
2029
- m = TestModel(data=raw)
2030
- assert not m.data is raw
2031
- assert m.data == raw
2032
- raw.append(4)
2033
- assert m.data == [1, 2, 3]
2034
-
2035
-
2036
- def test_inner_model_custom_serializer():
2037
- class InnerModel(Model):
2038
- prop: str = rest_field(type=lambda x: x[::-1])
2039
-
2040
- @overload
2041
- def __init__(self, *, prop: str): ...
2042
-
2043
- @overload
2044
- def __init__(self, mapping: Mapping[str, Any], /): ...
2045
-
2046
- def __init__(self, *args, **kwargs):
2047
- super().__init__(*args, **kwargs)
2048
-
2049
- class OuterModel(Model):
2050
- inner: InnerModel = rest_field()
2051
-
2052
- @overload
2053
- def __init__(self, *, inner: InnerModel): ...
2054
-
2055
- @overload
2056
- def __init__(self, mapping: Mapping[str, Any], /): ...
2057
-
2058
- def __init__(self, *args, **kwargs):
2059
- super().__init__(*args, **kwargs)
2060
-
2061
- outer = OuterModel({"inner": {"prop": "hello"}})
2062
- assert outer.inner["prop"] == outer["inner"]["prop"] == "hello"
2063
- assert outer.inner.prop == outer["inner"].prop == "olleh" # cspell: ignore olleh
2064
-
2065
-
2066
- def test_default_value():
2067
- class MyModel(Model):
2068
- prop_default_str: str = rest_field(name="propDefaultStr", default="hello")
2069
- prop_optional_str: Optional[str] = rest_field(name="propOptionalStr", default=None)
2070
- prop_default_int: int = rest_field(name="propDefaultInt", default=1)
2071
- prop_optional_int: Optional[int] = rest_field(name="propOptionalInt", default=None)
2072
-
2073
- @overload
2074
- def __init__(
2075
- self,
2076
- *,
2077
- prop_default_str: str = "hello",
2078
- prop_optional_str: Optional[str] = "propOptionalStr",
2079
- prop_default_int: int = 1,
2080
- prop_optional_int: Optional[int] = None,
2081
- ): ...
2082
-
2083
- @overload
2084
- def __init__(self, mapping: Mapping[str, Any], /): ...
2085
-
2086
- def __init__(self, *args, **kwargs):
2087
- super().__init__(*args, **kwargs)
2088
-
2089
- my_model = MyModel()
2090
- assert my_model.prop_default_str == my_model["propDefaultStr"] == "hello"
2091
- assert my_model.prop_optional_str is my_model["propOptionalStr"] is None
2092
- assert my_model.prop_default_int == my_model["propDefaultInt"] == 1
2093
- assert my_model.prop_optional_int is my_model["propOptionalInt"] is None
2094
- assert my_model == {
2095
- "propDefaultStr": "hello",
2096
- "propOptionalStr": None,
2097
- "propDefaultInt": 1,
2098
- "propOptionalInt": None,
2099
- }
2100
-
2101
- my_model = MyModel(prop_default_str="goodbye")
2102
- assert my_model.prop_default_str == my_model["propDefaultStr"] == "goodbye"
2103
- assert my_model.prop_optional_str is my_model["propOptionalStr"] is None
2104
- assert my_model.prop_default_int == my_model["propDefaultInt"] == 1
2105
- assert my_model.prop_optional_int is my_model["propOptionalInt"] is None
2106
- assert my_model == {
2107
- "propDefaultStr": "goodbye",
2108
- "propOptionalStr": None,
2109
- "propDefaultInt": 1,
2110
- "propOptionalInt": None,
2111
- }
2112
-
2113
- my_model = MyModel(prop_optional_int=4)
2114
- assert my_model.prop_default_str == my_model["propDefaultStr"] == "hello"
2115
- assert my_model.prop_optional_str is my_model["propOptionalStr"] is None
2116
- assert my_model.prop_default_int == my_model["propDefaultInt"] == 1
2117
- assert my_model.prop_optional_int == my_model["propOptionalInt"] == 4
2118
- assert my_model == {
2119
- "propDefaultStr": "hello",
2120
- "propOptionalStr": None,
2121
- "propDefaultInt": 1,
2122
- "propOptionalInt": 4,
2123
- }
2124
-
2125
- my_model = MyModel({"propDefaultInt": 5})
2126
- assert my_model.prop_default_str == my_model["propDefaultStr"] == "hello"
2127
- assert my_model.prop_optional_str is my_model["propOptionalStr"] is None
2128
- assert my_model.prop_default_int == my_model["propDefaultInt"] == 5
2129
- assert my_model.prop_optional_int is my_model["propOptionalInt"] is None
2130
- assert my_model == {
2131
- "propDefaultStr": "hello",
2132
- "propOptionalStr": None,
2133
- "propDefaultInt": 5,
2134
- "propOptionalInt": None,
2135
- }
2136
-
2137
-
2138
- def test_pass_models_in_dict():
2139
- class Inner(Model):
2140
- str_property: str = rest_field(name="strProperty")
2141
-
2142
- @overload
2143
- def __init__(
2144
- self,
2145
- *,
2146
- str_property: str,
2147
- ): ...
2148
-
2149
- @overload
2150
- def __init__(self, mapping: Mapping[str, Any], /): ...
2151
-
2152
- def __init__(self, *args, **kwargs):
2153
- super().__init__(*args, **kwargs)
2154
-
2155
- class Outer(Model):
2156
- inner_property: Inner = rest_field(name="innerProperty")
2157
-
2158
- @overload
2159
- def __init__(
2160
- self,
2161
- *,
2162
- inner_property: Inner,
2163
- ): ...
2164
-
2165
- @overload
2166
- def __init__(self, mapping: Mapping[str, Any], /): ...
2167
-
2168
- def __init__(self, *args, **kwargs):
2169
- super().__init__(*args, **kwargs)
2170
-
2171
- def _tests(model: Outer):
2172
- assert (
2173
- {"innerProperty": {"strProperty": "hello"}}
2174
- == {"innerProperty": Inner(str_property="hello")}
2175
- == {"innerProperty": Inner({"strProperty": "hello"})}
2176
- == Outer(inner_property=Inner(str_property="hello"))
2177
- == Outer(inner_property=Inner({"strProperty": "hello"}))
2178
- == Outer({"innerProperty": {"strProperty": "hello"}})
2179
- == Outer({"innerProperty": Inner(str_property="hello")})
2180
- == Outer({"innerProperty": Inner({"strProperty": "hello"})})
2181
- == model
2182
- )
2183
-
2184
- _tests(Outer(inner_property=Inner(str_property="hello")))
2185
- _tests(Outer(inner_property=Inner({"strProperty": "hello"})))
2186
- _tests(Outer({"innerProperty": {"strProperty": "hello"}}))
2187
- _tests(Outer({"innerProperty": Inner(str_property="hello")}))
2188
- _tests(Outer({"innerProperty": Inner({"strProperty": "hello"})}))
2189
-
2190
-
2191
- def test_mutability_list():
2192
- class Inner(Model):
2193
- str_property: str = rest_field(name="strProperty")
2194
-
2195
- @overload
2196
- def __init__(
2197
- self,
2198
- *,
2199
- str_property: str,
2200
- ): ...
2201
-
2202
- @overload
2203
- def __init__(self, mapping: Mapping[str, Any], /): ...
2204
-
2205
- def __init__(self, *args, **kwargs):
2206
- super().__init__(*args, **kwargs)
2207
-
2208
- class Middle(Model):
2209
- inner_property: list[Inner] = rest_field(name="innerProperty")
2210
- prop: str = rest_field()
2211
-
2212
- @overload
2213
- def __init__(
2214
- self,
2215
- *,
2216
- inner_property: list[Inner],
2217
- prop: str,
2218
- ): ...
2219
-
2220
- @overload
2221
- def __init__(self, mapping: Mapping[str, Any], /): ...
2222
-
2223
- def __init__(self, *args, **kwargs):
2224
- super().__init__(*args, **kwargs)
2225
-
2226
- class Outer(Model):
2227
- middle_property: Middle = rest_field(name="middleProperty")
2228
-
2229
- @overload
2230
- def __init__(
2231
- self,
2232
- *,
2233
- middle_property: Model,
2234
- ): ...
2235
-
2236
- @overload
2237
- def __init__(self, mapping: Mapping[str, Any], /): ...
2238
-
2239
- def __init__(self, *args, **kwargs):
2240
- super().__init__(*args, **kwargs)
2241
-
2242
- original_dict = {
2243
- "middleProperty": {
2244
- "innerProperty": [{"strProperty": "hello"}],
2245
- "prop": "original",
2246
- }
2247
- }
2248
- model = Outer(original_dict)
2249
- assert model is not original_dict
2250
-
2251
- # set with dict syntax
2252
- assert model.middle_property is model["middleProperty"]
2253
- middle_property = model.middle_property
2254
- middle_property["prop"] = "new"
2255
- assert model["middleProperty"] is model.middle_property is middle_property
2256
- assert model["middleProperty"]["prop"] == model.middle_property.prop == "new"
2257
-
2258
- # set with attr syntax
2259
- middle_property.prop = "newest"
2260
- assert model["middleProperty"] is model.middle_property is middle_property
2261
- assert model["middleProperty"]["prop"] == model.middle_property.prop == "newest"
2262
-
2263
- # modify innerproperty list
2264
- assert model["middleProperty"]["innerProperty"][0] is model.middle_property.inner_property[0]
2265
- assert (
2266
- model["middleProperty"]["innerProperty"][0]
2267
- is model.middle_property["innerProperty"][0]
2268
- is model["middleProperty"].inner_property[0]
2269
- is model.middle_property.inner_property[0]
2270
- )
2271
- inner_property = model["middleProperty"]["innerProperty"][0]
2272
-
2273
- # set with dict syntax
2274
- inner_property["strProperty"] = "nihao"
2275
- assert (
2276
- model["middleProperty"]["innerProperty"][0]
2277
- is model.middle_property["innerProperty"][0]
2278
- is model["middleProperty"].inner_property[0]
2279
- is model.middle_property.inner_property[0]
2280
- )
2281
- assert (
2282
- model["middleProperty"]["innerProperty"][0]["strProperty"]
2283
- == model.middle_property["innerProperty"][0]["strProperty"]
2284
- == model["middleProperty"].inner_property[0]["strProperty"]
2285
- == model.middle_property.inner_property[0]["strProperty"]
2286
- == model["middleProperty"]["innerProperty"][0].str_property
2287
- == model.middle_property["innerProperty"][0].str_property
2288
- == model["middleProperty"].inner_property[0].str_property
2289
- == model.middle_property.inner_property[0].str_property
2290
- == "nihao"
2291
- )
2292
-
2293
-
2294
- def test_mutability_dict():
2295
- class Inner(Model):
2296
- str_property: str = rest_field(name="strProperty")
2297
-
2298
- @overload
2299
- def __init__(
2300
- self,
2301
- *,
2302
- str_property: str,
2303
- ): ...
2304
-
2305
- @overload
2306
- def __init__(self, mapping: Mapping[str, Any], /): ...
2307
-
2308
- def __init__(self, *args, **kwargs):
2309
- super().__init__(*args, **kwargs)
2310
-
2311
- class Middle(Model):
2312
- inner_property: dict[str, Inner] = rest_field(name="innerProperty")
2313
- prop: str = rest_field()
2314
-
2315
- @overload
2316
- def __init__(
2317
- self,
2318
- *,
2319
- inner_property: dict[str, Inner],
2320
- prop: str,
2321
- ): ...
2322
-
2323
- @overload
2324
- def __init__(self, mapping: Mapping[str, Any], /): ...
2325
-
2326
- def __init__(self, *args, **kwargs):
2327
- super().__init__(*args, **kwargs)
2328
-
2329
- class Outer(Model):
2330
- middle_property: Middle = rest_field(name="middleProperty")
2331
-
2332
- @overload
2333
- def __init__(
2334
- self,
2335
- *,
2336
- middle_property: Model,
2337
- ): ...
2338
-
2339
- @overload
2340
- def __init__(self, mapping: Mapping[str, Any], /): ...
2341
-
2342
- def __init__(self, *args, **kwargs):
2343
- super().__init__(*args, **kwargs)
2344
-
2345
- original_dict = {
2346
- "middleProperty": {
2347
- "innerProperty": {"inner": {"strProperty": "hello"}},
2348
- "prop": "original",
2349
- }
2350
- }
2351
- model = Outer(original_dict)
2352
- assert model is not original_dict
2353
-
2354
- # set with dict syntax
2355
- assert model.middle_property is model["middleProperty"]
2356
- middle_property = model.middle_property
2357
- middle_property["prop"] = "new"
2358
- assert model["middleProperty"] is model.middle_property is middle_property
2359
- assert (
2360
- model["middleProperty"]["prop"]
2361
- == model["middleProperty"].prop
2362
- == model.middle_property.prop
2363
- == model.middle_property["prop"]
2364
- == "new"
2365
- )
2366
-
2367
- # set with attr syntax
2368
- middle_property.prop = "newest"
2369
- assert model["middleProperty"] is model.middle_property is middle_property
2370
- assert model["middleProperty"]["prop"] == model.middle_property.prop == "newest"
2371
-
2372
- # modify innerproperty list
2373
- assert model["middleProperty"]["innerProperty"]["inner"] is model.middle_property.inner_property["inner"]
2374
- assert (
2375
- model["middleProperty"]["innerProperty"]["inner"]
2376
- is model.middle_property["innerProperty"]["inner"]
2377
- is model["middleProperty"].inner_property["inner"]
2378
- is model.middle_property.inner_property["inner"]
2379
- )
2380
- inner_property = model["middleProperty"]["innerProperty"]["inner"]
2381
-
2382
- # set with dict syntax
2383
- inner_property["strProperty"] = "nihao"
2384
- assert (
2385
- model["middleProperty"]["innerProperty"]["inner"]
2386
- is model.middle_property["innerProperty"]["inner"]
2387
- is model["middleProperty"].inner_property["inner"]
2388
- is model.middle_property.inner_property["inner"]
2389
- )
2390
- assert (
2391
- model["middleProperty"]["innerProperty"]["inner"]["strProperty"]
2392
- == model.middle_property["innerProperty"]["inner"]["strProperty"]
2393
- == model["middleProperty"].inner_property["inner"]["strProperty"]
2394
- == model.middle_property.inner_property["inner"]["strProperty"]
2395
- == model["middleProperty"]["innerProperty"]["inner"].str_property
2396
- == model.middle_property["innerProperty"]["inner"].str_property
2397
- == model["middleProperty"].inner_property["inner"].str_property
2398
- == model.middle_property.inner_property["inner"].str_property
2399
- == "nihao"
2400
- )
2401
-
2402
-
2403
- def test_del_model():
2404
- class TestModel(Model):
2405
- x: Optional[int] = rest_field()
2406
-
2407
- my_dict = {}
2408
- my_dict["x"] = None
2409
-
2410
- assert my_dict["x"] is None
2411
-
2412
- my_model = TestModel({})
2413
- my_model["x"] = None
2414
-
2415
- assert my_model["x"] is my_model.x is None
2416
-
2417
- my_model = TestModel({"x": 7})
2418
- my_model.x = None
2419
-
2420
- assert "x" not in my_model
2421
- assert my_model.x is None
2422
-
2423
- with pytest.raises(KeyError):
2424
- del my_model["x"]
2425
- my_model.x = 8
2426
-
2427
- del my_model["x"]
2428
- assert "x" not in my_model
2429
- assert my_model.x is my_model.get("x") is None
2430
-
2431
- with pytest.raises(AttributeError):
2432
- del my_model.x
2433
- my_model.x = None
2434
- assert "x" not in my_model
2435
- assert my_model.x is my_model.get("x") is None
2436
-
2437
-
2438
- def test_pop_model():
2439
- class Inner(Model):
2440
- str_property: str = rest_field(name="strProperty")
2441
-
2442
- @overload
2443
- def __init__(
2444
- self,
2445
- *,
2446
- str_property: str,
2447
- ): ...
2448
-
2449
- @overload
2450
- def __init__(self, mapping: Mapping[str, Any], /): ...
2451
-
2452
- def __init__(self, *args, **kwargs):
2453
- super().__init__(*args, **kwargs)
2454
-
2455
- class Middle(Model):
2456
- inner_property: dict[str, Inner] = rest_field(name="innerProperty")
2457
- prop: str = rest_field()
2458
-
2459
- @overload
2460
- def __init__(
2461
- self,
2462
- *,
2463
- inner_property: dict[str, Inner],
2464
- prop: str,
2465
- ): ...
2466
-
2467
- @overload
2468
- def __init__(self, mapping: Mapping[str, Any], /): ...
2469
-
2470
- def __init__(self, *args, **kwargs):
2471
- super().__init__(*args, **kwargs)
2472
-
2473
- class Outer(Model):
2474
- middle_property: Middle = rest_field(name="middleProperty")
2475
-
2476
- @overload
2477
- def __init__(
2478
- self,
2479
- *,
2480
- middle_property: Model,
2481
- ): ...
2482
-
2483
- @overload
2484
- def __init__(self, mapping: Mapping[str, Any], /): ...
2485
-
2486
- def __init__(self, *args, **kwargs):
2487
- super().__init__(*args, **kwargs)
2488
-
2489
- original_dict = {
2490
- "middleProperty": {
2491
- "innerProperty": {"inner": {"strProperty": "hello"}},
2492
- "prop": "original",
2493
- }
2494
- }
2495
- model_dict = Outer(original_dict) # model we will access with dict syntax
2496
- model_attr = Outer(original_dict) # model we will access with attr syntax
2497
-
2498
- assert model_dict is not original_dict is not model_attr
2499
- assert (
2500
- original_dict["middleProperty"]["innerProperty"]["inner"].pop("strProperty")
2501
- == model_dict["middleProperty"]["innerProperty"]["inner"].pop("strProperty")
2502
- == model_attr.middle_property.inner_property["inner"].pop("strProperty")
2503
- == "hello"
2504
- )
2505
-
2506
- with pytest.raises(KeyError):
2507
- original_dict["middleProperty"]["innerProperty"]["inner"].pop("strProperty")
2508
- with pytest.raises(KeyError):
2509
- model_dict["middleProperty"]["innerProperty"]["inner"].pop("strProperty")
2510
- with pytest.raises(KeyError):
2511
- model_attr.middle_property.inner_property["inner"].pop("strProperty")
2512
-
2513
-
2514
- def test_contains():
2515
- class ParentA(Model):
2516
- a_prop: str = rest_field(name="aProp")
2517
-
2518
- @overload
2519
- def __init__(
2520
- self,
2521
- *,
2522
- a_prop: str,
2523
- ): ...
2524
-
2525
- @overload
2526
- def __init__(self, mapping: Mapping[str, Any], /): ...
2527
-
2528
- def __init__(self, *args, **kwargs):
2529
- super().__init__(*args, **kwargs)
2530
-
2531
- class ParentB(Model):
2532
- b_prop: str = rest_field(name="bProp")
2533
-
2534
- @overload
2535
- def __init__(
2536
- self,
2537
- *,
2538
- b_prop: str,
2539
- ): ...
2540
-
2541
- @overload
2542
- def __init__(self, mapping: Mapping[str, Any], /): ...
2543
-
2544
- def __init__(self, *args, **kwargs):
2545
- super().__init__(*args, **kwargs)
2546
-
2547
- class ChildC(ParentA, ParentB):
2548
- c_prop: str = rest_field(name="cProp")
2549
-
2550
- @overload
2551
- def __init__(
2552
- self,
2553
- *,
2554
- a_prop: str,
2555
- b_prop: str,
2556
- c_prop: str,
2557
- ): ...
2558
-
2559
- @overload
2560
- def __init__(self, mapping: Mapping[str, Any], /): ...
2561
-
2562
- def __init__(self, *args, **kwargs):
2563
- super().__init__(*args, **kwargs)
2564
-
2565
- parent_a_dict = {"aProp": "a"}
2566
- assert "aProp" in parent_a_dict
2567
-
2568
- parent_a = ParentA(parent_a_dict)
2569
- assert "aProp" in parent_a
2570
- assert not "a_prop" in parent_a
2571
-
2572
- parent_a.a_prop = None # clear it out
2573
- assert "aProp" not in parent_a
2574
-
2575
- parent_b_dict = {"bProp": "b"}
2576
- assert "bProp" in parent_b_dict
2577
-
2578
- parent_b = ParentB(parent_b_dict)
2579
- assert "bProp" in parent_b
2580
- assert "b_prop" not in parent_b
2581
-
2582
- parent_b.b_prop = None # clear it out
2583
- assert "bProp" not in parent_b
2584
-
2585
- props = ["aProp", "bProp", "cProp"]
2586
- child_c_dict = {"aProp": "a", "bProp": "b", "cProp": "c"}
2587
- assert all(p for p in props if p in child_c_dict)
2588
-
2589
- child_c = ChildC(child_c_dict)
2590
- assert all(p for p in props if p in child_c)
2591
- assert not any(p for p in ["a_prop", "b_prop", "c_prop"] if p in child_c)
2592
-
2593
- child_c.a_prop = None
2594
- child_c.b_prop = None
2595
- child_c.c_prop = None
2596
-
2597
- assert not any(p for p in props if p in child_c)
2598
-
2599
-
2600
- def test_iter():
2601
- dict_response = {
2602
- "platformUpdateDomainCount": 5,
2603
- "platformFaultDomainCount": 3,
2604
- "virtualMachines": [],
2605
- }
2606
- assert isinstance(iter(dict_response), Iterable)
2607
- model = BasicResource(dict_response)
2608
- assert isinstance(iter(model), Iterable)
2609
-
2610
- assert (
2611
- list(iter(dict_response))
2612
- == list(iter(model))
2613
- == ["platformUpdateDomainCount", "platformFaultDomainCount", "virtualMachines"]
2614
- )
2615
-
2616
-
2617
- def test_len():
2618
- dict_response = {
2619
- "platformUpdateDomainCount": 5,
2620
- "platformFaultDomainCount": 3,
2621
- "virtualMachines": [],
2622
- }
2623
- model = BasicResource(dict_response)
2624
- assert len(dict_response) == len(model) == 3
2625
-
2626
- dict_response.pop("platformUpdateDomainCount")
2627
- model.pop("platformUpdateDomainCount")
2628
- assert len(dict_response) == len(model) == 2
2629
-
2630
-
2631
- def test_keys():
2632
- class Inner(Model):
2633
- str_prop: str = rest_field(name="strProp")
2634
-
2635
- class Outer(Model):
2636
- inner_prop: Inner = rest_field(name="innerProp")
2637
-
2638
- outer_dict = {"innerProp": {"strProp": "hello"}}
2639
- outer = Outer(outer_dict)
2640
- assert outer.keys() == outer_dict.keys()
2641
- outer_dict["newProp"] = "hello"
2642
- outer["newProp"] = "hello"
2643
-
2644
- assert outer.keys() == outer_dict.keys()
2645
-
2646
- outer_dict.pop("newProp")
2647
- outer.pop("newProp")
2648
- assert outer_dict.keys() == outer.keys()
2649
-
2650
-
2651
- def test_values():
2652
- class Inner(Model):
2653
- str_prop: str = rest_field(name="strProp")
2654
-
2655
- class Outer(Model):
2656
- inner_prop: Inner = rest_field(name="innerProp")
2657
-
2658
- outer_dict = {"innerProp": {"strProp": "hello"}}
2659
- outer = Outer(outer_dict)
2660
-
2661
- assert list(outer.values()) == list(outer_dict.values())
2662
- assert len(outer.values()) == len(outer_dict.values()) == 1
2663
- assert list(outer.values())[0]["strProp"] == list(outer_dict.values())[0]["strProp"] == "hello"
2664
-
2665
- outer_dict["innerProp"]["strProp"] = "goodbye"
2666
- outer.inner_prop.str_prop = "goodbye"
2667
-
2668
- assert list(outer.inner_prop.values()) == list(outer_dict["innerProp"].values())
2669
-
2670
-
2671
- def test_items():
2672
- class Inner(Model):
2673
- str_prop: str = rest_field(name="strProp")
2674
-
2675
- class Outer(Model):
2676
- inner_prop: Inner = rest_field(name="innerProp")
2677
-
2678
- outer_dict = {"innerProp": {"strProp": "hello"}}
2679
- outer = Outer(outer_dict)
2680
-
2681
- assert list(outer.items()) == list(outer_dict.items())
2682
-
2683
- outer_dict["innerProp"]["strProp"] = "goodbye"
2684
- outer.inner_prop.str_prop = "goodbye"
2685
-
2686
- assert list(outer.inner_prop.items()) == list(outer_dict["innerProp"].items())
2687
-
2688
- outer_dict["newProp"] = "bonjour"
2689
- outer["newProp"] = "bonjour"
2690
-
2691
- assert list(outer.items()) == list(outer_dict.items())
2692
-
2693
-
2694
- def test_get():
2695
- class MyModel(Model):
2696
- prop: str = rest_field()
2697
- rest_prop: str = rest_field(name="restProp")
2698
-
2699
- my_dict = {"prop": "hello", "restProp": "bonjour"}
2700
- my_model = MyModel(my_dict)
2701
-
2702
- assert my_dict.get("prop") == my_model.get("prop") == "hello"
2703
- my_dict["prop"] = "nihao"
2704
- my_model.prop = "nihao"
2705
-
2706
- assert my_dict.get("prop") == my_model.get("prop") == "nihao"
2707
-
2708
- my_dict["restProp"] = "buongiorno"
2709
- my_model.rest_prop = "buongiorno"
2710
-
2711
- assert my_dict.get("restProp") == my_model.get("restProp") == "buongiorno"
2712
- assert my_dict.get("rest_prop") is None # attr case should not work here
2713
-
2714
- my_dict["newProp"] = "i'm new"
2715
- my_model["newProp"] = "i'm new"
2716
-
2717
- assert my_dict.get("newProp") == my_model.get("newProp") == "i'm new"
2718
- assert my_dict.get("nonexistent") is my_model.get("nonexistent") is None
2719
-
2720
- assert my_dict.get("nonexistent", 0) == my_model.get("nonexistent", 0) == 0
2721
-
2722
-
2723
- def test_pop():
2724
- class MyModel(Model):
2725
- prop: str = rest_field()
2726
- rest_prop: str = rest_field(name="restProp")
2727
-
2728
- my_dict = {"prop": "hello", "restProp": "bonjour"}
2729
- my_model = MyModel(my_dict)
2730
-
2731
- assert my_dict.pop("prop") == my_model.pop("prop") == "hello"
2732
- with pytest.raises(KeyError):
2733
- my_dict.pop("prop")
2734
- with pytest.raises(KeyError):
2735
- my_model.pop("prop")
2736
-
2737
- my_dict["prop"] = "nihao"
2738
- my_model.prop = "nihao"
2739
-
2740
- assert my_dict.pop("prop") == my_model.pop("prop") == "nihao"
2741
-
2742
- with pytest.raises(KeyError):
2743
- my_dict.pop("prop")
2744
- with pytest.raises(KeyError):
2745
- my_model.pop("prop")
2746
-
2747
- my_dict["restProp"] = "buongiorno"
2748
- my_model.rest_prop = "buongiorno"
2749
-
2750
- assert my_dict.pop("restProp") == my_model.pop("restProp") == "buongiorno"
2751
- with pytest.raises(KeyError):
2752
- my_dict.pop("rest_prop") # attr case should not work here
2753
-
2754
- my_dict["newProp"] = "i'm new"
2755
- my_model["newProp"] = "i'm new"
2756
-
2757
- assert my_dict.pop("newProp") == my_model.pop("newProp") == "i'm new"
2758
- assert my_dict.pop("nonexistent", 0) == my_model.pop("nonexistent", 0) == 0
2759
-
2760
-
2761
- def test_popitem():
2762
- class ModelA(Model):
2763
- a_str_prop: str = rest_field(name="aStrProp")
2764
-
2765
- class ModelB(Model):
2766
- b_str_prop: str = rest_field(name="bStrProp")
2767
-
2768
- class ModelC(Model):
2769
- c_str_prop: str = rest_field(name="cStrProp")
2770
-
2771
- class MainModel(Model):
2772
- a_prop: ModelA = rest_field(name="aProp")
2773
- b_prop: ModelB = rest_field(name="bProp")
2774
- c_prop: ModelC = rest_field(name="cProp")
2775
-
2776
- my_dict = {
2777
- "aProp": {"aStrProp": "a"},
2778
- "bProp": {"bStrProp": "b"},
2779
- "cProp": {"cStrProp": "c"},
2780
- }
2781
-
2782
- def _tests(my_dict: dict[str, Any], my_model: MainModel):
2783
- my_dict = copy.deepcopy(my_dict) # so we don't get rid of the dict each time we run tests
2784
-
2785
- # pop c prop
2786
- dict_popitem = my_dict.popitem()
2787
- model_popitem = my_model.popitem()
2788
- assert dict_popitem[0] == model_popitem[0] == "cProp"
2789
- assert dict_popitem[1]["cStrProp"] == model_popitem[1]["cStrProp"] == model_popitem[1].c_str_prop == "c"
2790
-
2791
- # pop b prop
2792
- dict_popitem = my_dict.popitem()
2793
- model_popitem = my_model.popitem()
2794
- assert dict_popitem[0] == model_popitem[0] == "bProp"
2795
- assert dict_popitem[1]["bStrProp"] == model_popitem[1]["bStrProp"] == model_popitem[1].b_str_prop == "b"
2796
-
2797
- # pop a prop
2798
- dict_popitem = my_dict.popitem()
2799
- model_popitem = my_model.popitem()
2800
- assert dict_popitem[0] == model_popitem[0] == "aProp"
2801
- assert dict_popitem[1]["aStrProp"] == model_popitem[1]["aStrProp"] == model_popitem[1].a_str_prop == "a"
2802
-
2803
- with pytest.raises(KeyError):
2804
- my_dict.popitem()
2805
-
2806
- with pytest.raises(KeyError):
2807
- my_model.popitem()
2808
-
2809
- _tests(my_dict, MainModel(my_dict))
2810
- _tests(
2811
- my_dict,
2812
- MainModel(
2813
- a_prop=ModelA(a_str_prop="a"),
2814
- b_prop=ModelB(b_str_prop="b"),
2815
- c_prop=ModelC(c_str_prop="c"),
2816
- ),
2817
- )
2818
-
2819
-
2820
- def test_clear():
2821
- class ModelA(Model):
2822
- a_str_prop: str = rest_field(name="aStrProp")
2823
-
2824
- class ModelB(Model):
2825
- b_str_prop: str = rest_field(name="bStrProp")
2826
-
2827
- class ModelC(Model):
2828
- c_str_prop: str = rest_field(name="cStrProp")
2829
-
2830
- class MainModel(Model):
2831
- a_prop: ModelA = rest_field(name="aProp")
2832
- b_prop: ModelB = rest_field(name="bProp")
2833
- c_prop: ModelC = rest_field(name="cProp")
2834
-
2835
- my_dict = {
2836
- "aProp": {"aStrProp": "a"},
2837
- "bProp": {"bStrProp": "b"},
2838
- "cProp": {"cStrProp": "c"},
2839
- }
2840
-
2841
- def _tests(my_dict: dict[str, Any], my_model: MainModel):
2842
- my_dict = copy.deepcopy(my_dict) # so we don't get rid of the dict each time we run tests
2843
-
2844
- assert my_dict["aProp"] == my_model.a_prop == my_model["aProp"] == {"aStrProp": "a"}
2845
- my_dict.clear()
2846
- my_model.clear()
2847
- assert my_dict == my_model == {}
2848
-
2849
- assert my_model.a_prop is None
2850
- assert my_model.b_prop is None
2851
- assert my_model.c_prop is None
2852
-
2853
- my_dict.clear()
2854
- my_model.clear()
2855
- assert my_dict == my_model == {}
2856
-
2857
- _tests(my_dict, MainModel(my_dict))
2858
- _tests(
2859
- my_dict,
2860
- MainModel(
2861
- a_prop=ModelA(a_str_prop="a"),
2862
- b_prop=ModelB(b_str_prop="b"),
2863
- c_prop=ModelC(c_str_prop="c"),
2864
- ),
2865
- )
2866
-
2867
-
2868
- def test_update():
2869
- class ModelA(Model):
2870
- a_str_prop: str = rest_field(name="aStrProp")
2871
-
2872
- class ModelB(Model):
2873
- b_str_prop: str = rest_field(name="bStrProp")
2874
-
2875
- class ModelC(Model):
2876
- c_str_prop: str = rest_field(name="cStrProp")
2877
-
2878
- class MainModel(Model):
2879
- a_prop: ModelA = rest_field(name="aProp")
2880
- b_prop: ModelB = rest_field(name="bProp")
2881
- c_prop: ModelC = rest_field(name="cProp")
2882
-
2883
- my_dict = {
2884
- "aProp": {"aStrProp": "a"},
2885
- "bProp": {"bStrProp": "b"},
2886
- "cProp": {"cStrProp": "c"},
2887
- }
2888
-
2889
- def _tests(my_dict: dict[str, Any], my_model: MainModel):
2890
- my_dict = copy.deepcopy(my_dict) # so we don't get rid of the dict each time we run tests
2891
-
2892
- assert my_dict["aProp"] == my_model.a_prop == my_model["aProp"] == {"aStrProp": "a"}
2893
- my_dict.update({"aProp": {"aStrProp": "newA"}})
2894
- my_model.a_prop.update({"aStrProp": "newA"})
2895
- assert my_dict["aProp"] == my_model.a_prop == my_model["aProp"] == {"aStrProp": "newA"}
2896
-
2897
- my_dict["bProp"].update({"newBProp": "hello"})
2898
- my_model.b_prop.update({"newBProp": "hello"})
2899
-
2900
- assert my_dict["bProp"] == my_model.b_prop == my_model["bProp"] == {"bStrProp": "b", "newBProp": "hello"}
2901
-
2902
- my_dict.update({"dProp": "hello"})
2903
- my_model.update({"dProp": "hello"})
2904
-
2905
- assert my_dict["dProp"] == my_model["dProp"] == "hello"
2906
-
2907
- _tests(my_dict, MainModel(my_dict))
2908
- _tests(
2909
- my_dict,
2910
- MainModel(
2911
- a_prop=ModelA(a_str_prop="a"),
2912
- b_prop=ModelB(b_str_prop="b"),
2913
- c_prop=ModelC(c_str_prop="c"),
2914
- ),
2915
- )
2916
-
2917
-
2918
- def test_setdefault():
2919
- class Inner(Model):
2920
- str_prop: str = rest_field(name="strProp", default="modelDefault")
2921
-
2922
- class Outer(Model):
2923
- inner_prop: Inner = rest_field(name="innerProp")
2924
-
2925
- og_dict = {"innerProp": {}}
2926
- og_dict["innerProp"].setdefault("strProp", "actualDefault")
2927
- og_model = Outer(og_dict)
2928
- og_model.inner_prop.setdefault("strProp", "actualDefault")
2929
-
2930
- assert og_dict["innerProp"] == og_model["innerProp"] == og_model.inner_prop == {"strProp": "actualDefault"}
2931
-
2932
- assert (
2933
- og_dict["innerProp"].setdefault("strProp")
2934
- == og_model["innerProp"].setdefault("strProp")
2935
- == og_model.inner_prop.setdefault("strProp")
2936
- == "actualDefault"
2937
- )
2938
-
2939
- assert og_dict.setdefault("newProp") is og_model.setdefault("newProp") is None
2940
- assert og_dict["newProp"] is og_model["newProp"] is None
2941
-
2942
-
2943
- def test_repr():
2944
- class ModelA(Model):
2945
- a_str_prop: str = rest_field(name="aStrProp")
2946
-
2947
- class ModelB(Model):
2948
- b_str_prop: str = rest_field(name="bStrProp")
2949
-
2950
- class ModelC(Model):
2951
- c_str_prop: str = rest_field(name="cStrProp")
2952
-
2953
- class MainModel(Model):
2954
- a_prop: ModelA = rest_field(name="aProp")
2955
- b_prop: ModelB = rest_field(name="bProp")
2956
- c_prop: ModelC = rest_field(name="cProp")
2957
-
2958
- my_dict = {
2959
- "aProp": {"aStrProp": "a"},
2960
- "bProp": {"bStrProp": "b"},
2961
- "cProp": {"cStrProp": "c"},
2962
- }
2963
-
2964
- assert repr(my_dict) == repr(MainModel(my_dict))
2965
-
2966
-
2967
- ##### REWRITE BODY COMPLEX INTO THIS FILE #####
2968
-
2969
-
2970
- def test_complex_basic():
2971
- class Basic(Model):
2972
- id: Optional[int] = rest_field(default=None)
2973
- name: Optional[str] = rest_field(default=None)
2974
- color: Optional[Literal["cyan", "Magenta", "YELLOW", "blacK"]] = rest_field(default=None)
2975
-
2976
- @overload
2977
- def __init__(
2978
- self,
2979
- *,
2980
- id: Optional[int] = None,
2981
- name: Optional[str] = None,
2982
- color: Optional[Literal["cyan", "Magenta", "YELLOW", "blacK"]] = None,
2983
- ): ...
2984
-
2985
- @overload
2986
- def __init__(self, mapping: Mapping[str, Any], /): ...
2987
-
2988
- def __init__(self, *args, **kwargs):
2989
- super().__init__(*args, **kwargs)
2990
-
2991
- basic = Basic(id=2, name="abc", color="Magenta")
2992
- assert basic == {"id": 2, "name": "abc", "color": "Magenta"}
2993
-
2994
- basic.id = 3
2995
- basic.name = "new_name"
2996
- basic.color = "blacK"
2997
-
2998
- assert basic == {"id": 3, "name": "new_name", "color": "blacK"}
2999
-
3000
- basic["id"] = 4
3001
- basic["name"] = "newest_name"
3002
- basic["color"] = "YELLOW"
3003
-
3004
- assert basic == {"id": 4, "name": "newest_name", "color": "YELLOW"}
3005
-
3006
-
3007
- def test_complex_boolean_wrapper():
3008
- class BooleanWrapper(Model):
3009
- field_true: Optional[bool] = rest_field(default=None)
3010
- field_false: Optional[bool] = rest_field(default=None)
3011
-
3012
- @overload
3013
- def __init__(
3014
- self,
3015
- *,
3016
- field_true: Optional[bool] = None,
3017
- field_false: Optional[bool] = None,
3018
- ): ...
3019
-
3020
- @overload
3021
- def __init__(self, mapping: Mapping[str, Any], /): ...
3022
-
3023
- def __init__(self, *args, **kwargs):
3024
- super().__init__(*args, **kwargs)
3025
-
3026
- bool_model = BooleanWrapper(field_true=True, field_false=False)
3027
- assert bool_model == {"field_true": True, "field_false": False}
3028
- bool_model.field_true = False
3029
- bool_model.field_false = True
3030
- assert bool_model == {"field_true": False, "field_false": True}
3031
-
3032
- bool_model["field_true"] = True
3033
- bool_model["field_false"] = False
3034
- assert bool_model == {"field_true": True, "field_false": False}
3035
-
3036
-
3037
- def test_complex_byte_wrapper():
3038
- class ByteWrapper(Model):
3039
- default: Optional[bytes] = rest_field(default=None)
3040
- base64: Optional[bytes] = rest_field(default=None, format="base64")
3041
- base64url: Optional[bytes] = rest_field(default=None, format="base64url")
3042
- list_base64: Optional[list[bytes]] = rest_field(default=None, format="base64")
3043
- map_base64url: Optional[dict[str, bytes]] = rest_field(default=None, format="base64url")
3044
-
3045
- @overload
3046
- def __init__(
3047
- self,
3048
- *,
3049
- default: Optional[bytes] = None,
3050
- base64: Optional[bytes] = None,
3051
- base64url: Optional[bytes] = None,
3052
- list_base64: Optional[list[bytes]] = None,
3053
- map_base64url: Optional[dict[str, bytes]] = None,
3054
- ): ...
3055
-
3056
- @overload
3057
- def __init__(self, mapping: Mapping[str, Any], /): ...
3058
-
3059
- def __init__(self, *args, **kwargs):
3060
- super().__init__(*args, **kwargs)
3061
-
3062
- byte_string = bytes("test", "utf-8")
3063
- mod = ByteWrapper(
3064
- default=byte_string,
3065
- base64=byte_string,
3066
- base64url=byte_string,
3067
- list_base64=[byte_string, byte_string],
3068
- map_base64url={"key1": byte_string, "key2": byte_string},
3069
- )
3070
- decoded = "dGVzdA=="
3071
- decoded_urlsafe = "dGVzdA"
3072
-
3073
- def _tests(mod: ByteWrapper):
3074
- assert mod == {
3075
- "default": decoded,
3076
- "base64": decoded,
3077
- "base64url": decoded_urlsafe,
3078
- "list_base64": [decoded, decoded],
3079
- "map_base64url": {"key1": decoded_urlsafe, "key2": decoded_urlsafe},
3080
- }
3081
- assert mod.default == byte_string
3082
- assert mod.base64 == byte_string
3083
- assert mod.base64url == byte_string
3084
- assert mod.list_base64 == [byte_string, byte_string]
3085
- assert mod.map_base64url == {"key1": byte_string, "key2": byte_string}
3086
- assert mod["default"] == decoded
3087
- assert mod["base64"] == decoded
3088
- assert mod["base64url"] == decoded_urlsafe
3089
- assert mod["list_base64"] == [decoded, decoded]
3090
- assert mod["map_base64url"] == {
3091
- "key1": decoded_urlsafe,
3092
- "key2": decoded_urlsafe,
3093
- }
3094
-
3095
- _tests(mod)
3096
- mod.default = byte_string
3097
- mod.base64 = byte_string
3098
- mod.base64url = byte_string
3099
- mod.list_base64 = [byte_string, byte_string]
3100
- mod.map_base64url = {"key1": byte_string, "key2": byte_string}
3101
- _tests(mod)
3102
- mod["default"] = decoded
3103
- mod["base64"] = decoded
3104
- mod["base64url"] = decoded_urlsafe
3105
- mod["list_base64"] = [decoded, decoded]
3106
- mod["map_base64url"] = {"key1": decoded_urlsafe, "key2": decoded_urlsafe}
3107
- _tests(mod)
3108
-
3109
-
3110
- def test_complex_byte_array_wrapper():
3111
- class ByteArrayWrapper(Model):
3112
- default: Optional[bytearray] = rest_field(default=None)
3113
- base64: Optional[bytearray] = rest_field(default=None, format="base64")
3114
- base64url: Optional[bytearray] = rest_field(default=None, format="base64url")
3115
- list_base64: Optional[list[bytearray]] = rest_field(default=None, format="base64")
3116
- map_base64url: Optional[dict[str, bytearray]] = rest_field(default=None, format="base64url")
3117
-
3118
- @overload
3119
- def __init__(
3120
- self,
3121
- *,
3122
- default: Optional[bytearray] = None,
3123
- base64: Optional[bytearray] = None,
3124
- base64url: Optional[bytearray] = None,
3125
- list_base64: Optional[list[bytearray]] = None,
3126
- map_base64url: Optional[dict[str, bytearray]] = None,
3127
- ): ...
3128
-
3129
- @overload
3130
- def __init__(self, mapping: Mapping[str, Any], /): ...
3131
-
3132
- def __init__(self, *args, **kwargs):
3133
- super().__init__(*args, **kwargs)
3134
-
3135
- byte_array = bytearray("test".encode("utf-8"))
3136
- decoded = "dGVzdA=="
3137
- decoded_urlsafe = "dGVzdA"
3138
-
3139
- def _tests(model: ByteArrayWrapper):
3140
- assert model == {
3141
- "default": decoded,
3142
- "base64": decoded,
3143
- "base64url": decoded_urlsafe,
3144
- "list_base64": [decoded, decoded],
3145
- "map_base64url": {"key1": decoded_urlsafe, "key2": decoded_urlsafe},
3146
- }
3147
- assert model.default == byte_array
3148
- assert model.base64 == byte_array
3149
- assert model.base64url == byte_array
3150
- assert model.list_base64 == [byte_array, byte_array]
3151
- assert model.map_base64url == {"key1": byte_array, "key2": byte_array}
3152
- assert model["default"] == decoded
3153
- assert model["base64"] == decoded
3154
- assert model["base64url"] == decoded_urlsafe
3155
- assert model["list_base64"] == [decoded, decoded]
3156
- assert model["map_base64url"] == {
3157
- "key1": decoded_urlsafe,
3158
- "key2": decoded_urlsafe,
3159
- }
3160
-
3161
- _tests(
3162
- ByteArrayWrapper(
3163
- default=byte_array,
3164
- base64=byte_array,
3165
- base64url=byte_array,
3166
- list_base64=[byte_array, byte_array],
3167
- map_base64url={"key1": byte_array, "key2": byte_array},
3168
- )
3169
- )
3170
- _tests(
3171
- ByteArrayWrapper(
3172
- {
3173
- "default": decoded,
3174
- "base64": decoded,
3175
- "base64url": decoded_urlsafe,
3176
- "list_base64": [decoded, decoded],
3177
- "map_base64url": {"key1": decoded_urlsafe, "key2": decoded_urlsafe},
3178
- }
3179
- )
3180
- )
3181
-
3182
-
3183
- def test_complex_datetime_wrapper():
3184
- class DatetimeWrapper(Model):
3185
- default: datetime.datetime = rest_field(default=None)
3186
- rfc3339: datetime.datetime = rest_field(default=None, format="rfc3339")
3187
- rfc7231: datetime.datetime = rest_field(default=None, format="rfc7231")
3188
- unix: datetime.datetime = rest_field(default=None, format="unix-timestamp")
3189
- list_rfc3339: list[datetime.datetime] = rest_field(default=None, format="rfc3339")
3190
- dict_rfc7231: dict[str, datetime.datetime] = rest_field(default=None, format="rfc7231")
3191
-
3192
- @overload
3193
- def __init__(
3194
- self,
3195
- *,
3196
- default: Optional[datetime.datetime] = None,
3197
- rfc3339: Optional[datetime.datetime] = None,
3198
- rfc7231: Optional[datetime.datetime] = None,
3199
- unix: Optional[datetime.datetime] = None,
3200
- list_rfc3339: Optional[list[datetime.datetime]] = None,
3201
- dict_rfc7231: Optional[dict[str, datetime.datetime]] = None,
3202
- ): ...
3203
-
3204
- @overload
3205
- def __init__(self, mapping: Mapping[str, Any], /): ...
3206
-
3207
- def __init__(self, *args, **kwargs):
3208
- super().__init__(*args, **kwargs)
3209
-
3210
- rfc3339 = "2023-06-27T06:11:09Z"
3211
- rfc7231 = "Tue, 27 Jun 2023 06:11:09 GMT"
3212
- unix = 1687846269
3213
- dt = datetime.datetime(2023, 6, 27, 6, 11, 9, tzinfo=datetime.timezone.utc)
3214
-
3215
- def _tests(model: DatetimeWrapper):
3216
- assert model["default"] == rfc3339
3217
- assert model["rfc3339"] == rfc3339
3218
- assert model["rfc7231"] == rfc7231
3219
- assert model["unix"] == unix
3220
- assert model["list_rfc3339"] == [rfc3339, rfc3339]
3221
- assert model["dict_rfc7231"] == {"key1": rfc7231, "key2": rfc7231}
3222
- assert model.default == model.rfc3339 == model.rfc7231 == model.unix == dt
3223
- assert model.list_rfc3339 == [dt, dt]
3224
- assert model.dict_rfc7231 == {"key1": dt, "key2": dt}
3225
-
3226
- _tests(
3227
- DatetimeWrapper(
3228
- default=dt,
3229
- rfc3339=dt,
3230
- rfc7231=dt,
3231
- unix=dt,
3232
- list_rfc3339=[dt, dt],
3233
- dict_rfc7231={"key1": dt, "key2": dt},
3234
- )
3235
- )
3236
- _tests(
3237
- DatetimeWrapper(
3238
- {
3239
- "default": rfc3339,
3240
- "rfc3339": rfc3339,
3241
- "rfc7231": rfc7231,
3242
- "unix": unix,
3243
- "list_rfc3339": [rfc3339, rfc3339],
3244
- "dict_rfc7231": {"key1": rfc7231, "key2": rfc7231},
3245
- }
3246
- )
3247
- )
3248
-
3249
-
3250
- def test_complex_date_wrapper():
3251
- class DateWrapper(Model):
3252
- field: datetime.date = rest_field(default=None)
3253
- leap: datetime.date = rest_field(default=None)
3254
-
3255
- @overload
3256
- def __init__(
3257
- self,
3258
- *,
3259
- field: Optional[datetime.date] = None,
3260
- leap: Optional[datetime.date] = None,
3261
- ): ...
3262
-
3263
- @overload
3264
- def __init__(self, mapping: Mapping[str, Any], /): ...
3265
-
3266
- def __init__(self, *args, **kwargs):
3267
- super().__init__(*args, **kwargs)
3268
-
3269
- field = "0001-01-01"
3270
- leap = "2016-02-29"
3271
-
3272
- def _tests(model: DateWrapper):
3273
- assert model.field == isodate.parse_date(field)
3274
- assert model["field"] == field
3275
-
3276
- assert model.leap == isodate.parse_date(leap)
3277
- assert model["leap"] == leap
3278
-
3279
- model.field = isodate.parse_date(leap)
3280
- assert model.field == isodate.parse_date(leap)
3281
- assert model["field"] == leap
3282
-
3283
- model["field"] = field
3284
- assert model.field == isodate.parse_date(field)
3285
- assert model["field"] == field
3286
-
3287
- _tests(DateWrapper({"field": field, "leap": leap}))
3288
- _tests(DateWrapper(field=isodate.parse_date(field), leap=isodate.parse_date(leap)))
3289
-
3290
-
3291
- class DictionaryWrapper(Model):
3292
- default_program: dict[str, str] = rest_field(name="defaultProgram", default=None)
3293
-
3294
- @overload
3295
- def __init__(
3296
- self,
3297
- *,
3298
- default_program: Optional[dict[str, str]] = None,
3299
- ): ...
3300
-
3301
- @overload
3302
- def __init__(self, mapping: Mapping[str, Any], /): ...
3303
-
3304
- def __init__(self, *args, **kwargs):
3305
- super().__init__(*args, **kwargs)
3306
-
3307
-
3308
- default_program = {
3309
- "txt": "notepad",
3310
- "bmp": "mspaint",
3311
- "xls": "excel",
3312
- "exe": "",
3313
- "": None,
3314
- }
3315
-
3316
-
3317
- @pytest.mark.parametrize(
3318
- "model",
3319
- [
3320
- DictionaryWrapper({"defaultProgram": default_program}),
3321
- DictionaryWrapper(default_program=default_program),
3322
- ],
3323
- )
3324
- def test_complex_dictionary_wrapper(model: DictionaryWrapper):
3325
- assert model == {"defaultProgram": default_program}
3326
- assert model.default_program == model["defaultProgram"] == default_program
3327
-
3328
-
3329
- @pytest.mark.parametrize(
3330
- "model",
3331
- [DictionaryWrapper({"defaultProgram": {}}), DictionaryWrapper(default_program={})],
3332
- )
3333
- def test_complex_dictionary_wrapper_empty(model: DictionaryWrapper):
3334
- assert model == {"defaultProgram": {}}
3335
- assert model.default_program == model["defaultProgram"] == {}
3336
-
3337
-
3338
- @pytest.mark.parametrize(
3339
- "model",
3340
- [
3341
- DictionaryWrapper({"defaultProgram": None}),
3342
- DictionaryWrapper(default_program=None),
3343
- ],
3344
- )
3345
- def test_complex_dictionary_wrapper_none(model: DictionaryWrapper):
3346
- assert model == {"defaultProgram": None}
3347
- assert model.default_program is None
3348
-
3349
-
3350
- class ArrayWrapper(Model):
3351
- array: Optional[list[str]] = rest_field(default=None)
3352
-
3353
- @overload
3354
- def __init__(
3355
- self,
3356
- *,
3357
- array: Optional[list[str]] = None,
3358
- ): ...
3359
-
3360
- @overload
3361
- def __init__(self, mapping: Mapping[str, Any], /): ...
3362
-
3363
- def __init__(self, *args, **kwargs):
3364
- super().__init__(*args, **kwargs)
3365
-
3366
-
3367
- array_value = [
3368
- "1, 2, 3, 4",
3369
- "",
3370
- None,
3371
- "&S#$(*Y",
3372
- "The quick brown fox jumps over the lazy dog",
3373
- ]
3374
-
3375
-
3376
- @pytest.mark.parametrize("model", [ArrayWrapper(array=array_value), ArrayWrapper({"array": array_value})])
3377
- def test_complex_array_wrapper(model: ArrayWrapper):
3378
- assert model == {"array": array_value}
3379
- assert model.array == model["array"] == array_value
3380
-
3381
- model.array = None
3382
- with pytest.raises(KeyError):
3383
- model["array"]
3384
- assert model.array is None
3385
-
3386
- model["array"] = [1, 2, 3, 4, 5]
3387
- assert model.array == ["1", "2", "3", "4", "5"]
3388
- assert model["array"] == ["1", "2", "3", "4", "5"]
3389
-
3390
-
3391
- @pytest.mark.parametrize("model", [ArrayWrapper(array=[]), ArrayWrapper({"array": []})])
3392
- def test_complex_array_wrapper_empty(model: ArrayWrapper):
3393
- assert model == {"array": []}
3394
- assert model.array == model["array"] == []
3395
-
3396
- model.array = ["bonjour"]
3397
- assert model.array == model["array"] == ["bonjour"]
3398
-
3399
-
3400
- @pytest.mark.parametrize("model", [ArrayWrapper(array=None), ArrayWrapper({"array": None})])
3401
- def test_complex_array_wrapper_none(model: ArrayWrapper):
3402
- assert model == {"array": None}
3403
- assert model.array is model["array"] is None
3404
-
3405
- model.array = ["bonjour"]
3406
- assert model.array == model["array"] == ["bonjour"]
3407
-
3408
-
3409
- class PetComplex(Model):
3410
- id: Optional[int] = rest_field(default=None)
3411
- name: Optional[str] = rest_field(default=None)
3412
-
3413
- @overload
3414
- def __init__(
3415
- self,
3416
- *,
3417
- id: Optional[int] = None,
3418
- name: Optional[str] = None,
3419
- ): ...
3420
-
3421
- @overload
3422
- def __init__(self, mapping: Mapping[str, Any], /): ...
3423
-
3424
- def __init__(self, *args, **kwargs):
3425
- super().__init__(*args, **kwargs)
3426
-
3427
-
3428
- class DogComplex(PetComplex):
3429
- food: Optional[str] = rest_field(default=None)
3430
-
3431
- @overload
3432
- def __init__(
3433
- self,
3434
- *,
3435
- id: Optional[int] = None,
3436
- name: Optional[str] = None,
3437
- food: Optional[str] = None,
3438
- ): ...
3439
-
3440
- @overload
3441
- def __init__(self, mapping: Mapping[str, Any], /): ...
3442
-
3443
- def __init__(self, *args, **kwargs):
3444
- super().__init__(*args, **kwargs)
3445
-
3446
-
3447
- class CatComplex(PetComplex):
3448
- color: Optional[str] = rest_field(default=None)
3449
- hates: Optional[list[DogComplex]] = rest_field(default=None)
3450
-
3451
- @overload
3452
- def __init__(
3453
- self,
3454
- *,
3455
- id: Optional[int] = None,
3456
- name: Optional[str] = None,
3457
- food: Optional[str] = None,
3458
- color: Optional[str] = None,
3459
- hates: Optional[list[DogComplex]] = None,
3460
- ): ...
3461
-
3462
- @overload
3463
- def __init__(self, mapping: Mapping[str, Any], /): ...
3464
-
3465
- def __init__(self, *args, **kwargs):
3466
- super().__init__(*args, **kwargs)
3467
-
3468
-
3469
- @pytest.mark.parametrize(
3470
- "model",
3471
- [
3472
- CatComplex(
3473
- id=2,
3474
- name="Siamese",
3475
- hates=[
3476
- DogComplex(id=1, name="Potato", food="tomato"),
3477
- DogComplex(id=-1, name="Tomato", food="french fries"),
3478
- ],
3479
- ),
3480
- CatComplex(
3481
- id=2,
3482
- name="Siamese",
3483
- hates=[
3484
- DogComplex(id=1, name="Potato", food="tomato"),
3485
- {"id": -1, "name": "Tomato", "food": "french fries"},
3486
- ],
3487
- ),
3488
- CatComplex(
3489
- id=2,
3490
- name="Siamese",
3491
- hates=[
3492
- {"id": 1, "name": "Potato", "food": "tomato"},
3493
- {"id": -1, "name": "Tomato", "food": "french fries"},
3494
- ],
3495
- ),
3496
- ],
3497
- )
3498
- def test_complex_inheritance(model):
3499
- assert model.id == model["id"] == 2
3500
- assert model.name == model["name"] == "Siamese"
3501
- assert model.hates
3502
- assert model.hates[1] == model["hates"][1] == {"id": -1, "name": "Tomato", "food": "french fries"}
3503
- model["breed"] = "persian"
3504
- model["color"] = "green"
3505
- with pytest.raises(AttributeError):
3506
- model.breed
3507
- assert model == {
3508
- "id": 2,
3509
- "name": "Siamese",
3510
- "color": "green",
3511
- "breed": "persian",
3512
- "hates": [
3513
- DogComplex(id=1, name="Potato", food="tomato"),
3514
- DogComplex(id=-1, name="Tomato", food="french fries"),
3515
- ],
3516
- }
3517
-
3518
-
3519
- def test_required_prop_not_passed():
3520
- class ModelWithRequiredProperty(Model):
3521
- required_property: int = rest_field(name="requiredProperty")
3522
-
3523
- @overload
3524
- def __init__(
3525
- self,
3526
- *,
3527
- required_property: int,
3528
- ): ...
3529
-
3530
- @overload
3531
- def __init__(self, mapping: Mapping[str, Any], /): ...
3532
-
3533
- def __init__(self, *args, **kwargs):
3534
- super().__init__(*args, **kwargs)
3535
-
3536
- model = ModelWithRequiredProperty()
3537
- assert model.required_property is None
3538
- with pytest.raises(KeyError):
3539
- model["requiredProperty"]
3540
-
3541
- model = ModelWithRequiredProperty({})
3542
- assert model.required_property is None
3543
- with pytest.raises(KeyError):
3544
- model["requiredProperty"]
3545
-
3546
-
3547
- def test_null_serialization(core_library):
3548
- dict_response = {
3549
- "name": "it's me!",
3550
- "listOfMe": [
3551
- {
3552
- "name": "it's me!",
3553
- }
3554
- ],
3555
- "dictOfMe": {
3556
- "me": {
3557
- "name": "it's me!",
3558
- }
3559
- },
3560
- "dictOfListOfMe": {
3561
- "many mes": [
3562
- {
3563
- "name": "it's me!",
3564
- }
3565
- ]
3566
- },
3567
- "listOfDictOfMe": None,
3568
- }
3569
- model = RecursiveModel(dict_response)
3570
- assert json.loads(json.dumps(model, cls=SdkJSONEncoder)) == dict_response
3571
-
3572
- assert model.as_dict() == dict_response
3573
-
3574
- model.list_of_me = core_library.serialization.NULL
3575
- model.dict_of_me = None
3576
- model.list_of_dict_of_me = [
3577
- {
3578
- "me": {
3579
- "name": "it's me!",
3580
- }
3581
- }
3582
- ]
3583
- model.dict_of_list_of_me["many mes"][0].list_of_me = core_library.serialization.NULL
3584
- model.dict_of_list_of_me["many mes"][0].dict_of_me = None
3585
- model.list_of_dict_of_me[0]["me"].list_of_me = core_library.serialization.NULL
3586
- model.list_of_dict_of_me[0]["me"].dict_of_me = None
3587
-
3588
- assert json.loads(json.dumps(model, cls=SdkJSONEncoder)) == {
3589
- "name": "it's me!",
3590
- "listOfMe": None,
3591
- "dictOfListOfMe": {
3592
- "many mes": [
3593
- {
3594
- "name": "it's me!",
3595
- "listOfMe": None,
3596
- }
3597
- ]
3598
- },
3599
- "listOfDictOfMe": [
3600
- {
3601
- "me": {
3602
- "name": "it's me!",
3603
- "listOfMe": None,
3604
- }
3605
- }
3606
- ],
3607
- }
3608
-
3609
- assert model.as_dict() == {
3610
- "name": "it's me!",
3611
- "listOfMe": None,
3612
- "dictOfListOfMe": {
3613
- "many mes": [
3614
- {
3615
- "name": "it's me!",
3616
- "listOfMe": None,
3617
- }
3618
- ]
3619
- },
3620
- "listOfDictOfMe": [
3621
- {
3622
- "me": {
3623
- "name": "it's me!",
3624
- "listOfMe": None,
3625
- }
3626
- }
3627
- ],
3628
- }
3629
-
3630
-
3631
- class UnionBaseModel(Model):
3632
- name: str = rest_field()
3633
-
3634
- @overload
3635
- def __init__(self, *, name: str): ...
3636
-
3637
- @overload
3638
- def __init__(self, mapping: Mapping[str, Any]): ...
3639
-
3640
- def __init__(self, *args: Any, **kwargs: Any) -> None:
3641
- super().__init__(*args, **kwargs)
3642
-
3643
-
3644
- class UnionModel1(UnionBaseModel):
3645
- prop1: int = rest_field()
3646
-
3647
- @overload
3648
- def __init__(self, *, name: str, prop1: int): ...
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
-
3656
-
3657
- class UnionModel2(UnionBaseModel):
3658
- prop2: int = rest_field()
3659
-
3660
- @overload
3661
- def __init__(self, *, name: str, prop2: int): ...
3662
-
3663
- @overload
3664
- def __init__(self, mapping: Mapping[str, Any]): ...
3665
-
3666
- def __init__(self, *args: Any, **kwargs: Any) -> None:
3667
- super().__init__(*args, **kwargs)
3668
-
3669
-
3670
- MyNamedUnion = Union["UnionModel1", "UnionModel2"]
3671
-
3672
-
3673
- class ModelWithNamedUnionProperty(Model):
3674
- named_union: "MyNamedUnion" = rest_field(name="namedUnion")
3675
-
3676
- @overload
3677
- def __init__(self, *, named_union: "MyNamedUnion"): ...
3678
-
3679
- @overload
3680
- def __init__(self, mapping: Mapping[str, Any]): ...
3681
-
3682
- def __init__(self, *args: Any, **kwargs: Any) -> None:
3683
- super().__init__(*args, **kwargs)
3684
-
3685
-
3686
- class ModelWithSimpleUnionProperty(Model):
3687
- simple_union: Union[int, list[int]] = rest_field(name="simpleUnion")
3688
-
3689
- @overload
3690
- def __init__(self, *, simple_union: Union[int, list[int]]): ...
3691
-
3692
- @overload
3693
- def __init__(self, mapping: Mapping[str, Any]): ...
3694
-
3695
- def __init__(self, *args: Any, **kwargs: Any) -> None:
3696
- super().__init__(*args, **kwargs)
3697
-
3698
-
3699
- def test_union():
3700
- simple = ModelWithSimpleUnionProperty(simple_union=1)
3701
- assert simple.simple_union == simple["simpleUnion"] == 1
3702
- simple = ModelWithSimpleUnionProperty(simple_union=[1, 2])
3703
- assert simple.simple_union == simple["simpleUnion"] == [1, 2]
3704
- named = ModelWithNamedUnionProperty()
3705
- assert not _is_model(named.named_union)
3706
- named.named_union = UnionModel1(name="model1", prop1=1)
3707
- assert _is_model(named.named_union)
3708
- assert named.named_union == named["namedUnion"] == {"name": "model1", "prop1": 1}
3709
- named = ModelWithNamedUnionProperty(named_union=UnionModel2(name="model2", prop2=2))
3710
- assert named.named_union == named["namedUnion"] == {"name": "model2", "prop2": 2}
3711
- named = ModelWithNamedUnionProperty({"namedUnion": {"name": "model2", "prop2": 2}})
3712
- assert named.named_union == named["namedUnion"] == {"name": "model2", "prop2": 2}
3713
-
3714
-
3715
- def test_as_dict():
3716
- class CatComplex(PetComplex):
3717
- color: Optional[str] = rest_field(default=None)
3718
- hates: Optional[list[DogComplex]] = rest_field(default=None, visibility=["read"])
3719
-
3720
- @overload
3721
- def __init__(
3722
- self,
3723
- *,
3724
- id: Optional[int] = None,
3725
- name: Optional[str] = None,
3726
- food: Optional[str] = None,
3727
- color: Optional[str] = None,
3728
- hates: Optional[list[DogComplex]] = None,
3729
- ): ...
3730
-
3731
- @overload
3732
- def __init__(self, mapping: Mapping[str, Any], /): ...
3733
-
3734
- def __init__(self, *args, **kwargs):
3735
- super().__init__(*args, **kwargs)
3736
-
3737
- model = CatComplex(
3738
- id=2,
3739
- name="Siamese",
3740
- hates=[
3741
- DogComplex(id=1, name="Potato", food="tomato"),
3742
- DogComplex(id=-1, name="Tomato", food="french fries"),
3743
- ],
3744
- )
3745
- assert model.as_dict(exclude_readonly=True) == {
3746
- "id": 2,
3747
- "name": "Siamese",
3748
- "color": None,
3749
- }
3750
-
3751
-
3752
- class Fish(Model):
3753
- __mapping__: dict[str, Model] = {}
3754
- age: int = rest_field()
3755
- kind: Literal[None] = rest_discriminator(name="kind")
3756
-
3757
- @overload
3758
- def __init__(
3759
- self,
3760
- *,
3761
- age: int,
3762
- ): ...
3763
-
3764
- @overload
3765
- def __init__(self, mapping: Mapping[str, Any]): ...
3766
-
3767
- def __init__(self, *args: Any, **kwargs: Any) -> None:
3768
- super().__init__(*args, **kwargs)
3769
- self.kind: Literal[None] = None
3770
-
3771
-
3772
- class Shark(Fish, discriminator="shark"):
3773
- __mapping__: dict[str, Model] = {}
3774
- kind: Literal["shark"] = rest_discriminator(name="kind")
3775
- sharktype: Literal[None] = rest_discriminator(name="sharktype")
3776
-
3777
- @overload
3778
- def __init__(
3779
- self,
3780
- *,
3781
- age: int,
3782
- ): ...
3783
-
3784
- @overload
3785
- def __init__(self, mapping: Mapping[str, Any]): ...
3786
-
3787
- def __init__(self, *args: Any, **kwargs: Any) -> None:
3788
- super().__init__(*args, **kwargs)
3789
- self.kind: Literal["shark"] = "shark"
3790
- self.sharktype: Literal[None] = None
3791
-
3792
-
3793
- class GoblinShark(Shark, discriminator="goblin"):
3794
- sharktype: Literal["goblin"] = rest_discriminator(name="sharktype")
3795
-
3796
- @overload
3797
- def __init__(
3798
- self,
3799
- *,
3800
- age: int,
3801
- ): ...
3802
-
3803
- @overload
3804
- def __init__(self, mapping: Mapping[str, Any]): ...
3805
-
3806
- def __init__(self, *args: Any, **kwargs: Any) -> None:
3807
- super().__init__(*args, **kwargs)
3808
- self.sharktype: Literal["goblin"] = "goblin"
3809
-
3810
-
3811
- class Salmon(Fish, discriminator="salmon"):
3812
- kind: Literal["salmon"] = rest_discriminator(name="kind")
3813
- friends: Optional[list["Fish"]] = rest_field()
3814
- hate: Optional[dict[str, "Fish"]] = rest_field()
3815
- partner: Optional["Fish"] = rest_field()
3816
-
3817
- @overload
3818
- def __init__(
3819
- self,
3820
- *,
3821
- age: int,
3822
- friends: Optional[list["Fish"]] = None,
3823
- hate: Optional[dict[str, "Fish"]] = None,
3824
- partner: Optional["Fish"] = None,
3825
- ): ...
3826
-
3827
- @overload
3828
- def __init__(self, mapping: Mapping[str, Any]): ...
3829
-
3830
- def __init__(self, *args: Any, **kwargs: Any) -> None:
3831
- super().__init__(*args, **kwargs)
3832
- self.kind: Literal["salmon"] = "salmon"
3833
-
3834
-
3835
- class SawShark(Shark, discriminator="saw"):
3836
- sharktype: Literal["saw"] = rest_discriminator(name="sharktype")
3837
-
3838
- @overload
3839
- def __init__(
3840
- self,
3841
- *,
3842
- age: int,
3843
- ): ...
3844
-
3845
- @overload
3846
- def __init__(self, mapping: Mapping[str, Any]): ...
3847
-
3848
- def __init__(self, *args: Any, **kwargs: Any) -> None:
3849
- super().__init__(*args, **kwargs)
3850
- self.sharktype: Literal["saw"] = "saw"
3851
-
3852
-
3853
- def test_discriminator():
3854
- input = {
3855
- "age": 1,
3856
- "kind": "salmon",
3857
- "partner": {
3858
- "age": 2,
3859
- "kind": "shark",
3860
- "sharktype": "saw",
3861
- },
3862
- "friends": [
3863
- {
3864
- "age": 2,
3865
- "kind": "salmon",
3866
- "partner": {
3867
- "age": 3,
3868
- "kind": "salmon",
3869
- },
3870
- "hate": {
3871
- "key1": {
3872
- "age": 4,
3873
- "kind": "salmon",
3874
- },
3875
- "key2": {
3876
- "age": 2,
3877
- "kind": "shark",
3878
- "sharktype": "goblin",
3879
- },
3880
- },
3881
- },
3882
- {
3883
- "age": 3,
3884
- "kind": "shark",
3885
- "sharktype": "goblin",
3886
- },
3887
- ],
3888
- "hate": {
3889
- "key3": {
3890
- "age": 3,
3891
- "kind": "shark",
3892
- "sharktype": "saw",
3893
- },
3894
- "key4": {
3895
- "age": 2,
3896
- "kind": "salmon",
3897
- "friends": [
3898
- {
3899
- "age": 1,
3900
- "kind": "salmon",
3901
- },
3902
- {
3903
- "age": 4,
3904
- "kind": "shark",
3905
- "sharktype": "goblin",
3906
- },
3907
- ],
3908
- },
3909
- },
3910
- }
3911
-
3912
- model = Salmon(input)
3913
- assert model == input
3914
- assert model.partner.age == 2
3915
- assert model.partner == SawShark(age=2)
3916
- assert model.friends[0].hate["key2"] == GoblinShark(age=2)
3917
-
3918
-
3919
- def test_body_bytes_format():
3920
- assert json.dumps(bytes("test", "utf-8"), cls=SdkJSONEncoder) == '"dGVzdA=="'
3921
- assert json.dumps(bytearray("test", "utf-8"), cls=SdkJSONEncoder) == '"dGVzdA=="'
3922
- assert json.dumps(bytes("test", "utf-8"), cls=SdkJSONEncoder, format="base64") == '"dGVzdA=="'
3923
- assert json.dumps(bytes("test", "utf-8"), cls=SdkJSONEncoder, format="base64url") == '"dGVzdA"'
3924
- assert json.dumps(bytearray("test", "utf-8"), cls=SdkJSONEncoder, format="base64") == '"dGVzdA=="'
3925
- assert json.dumps(bytearray("test", "utf-8"), cls=SdkJSONEncoder, format="base64url") == '"dGVzdA"'
3926
-
3927
- assert (
3928
- json.dumps([bytes("test", "utf-8"), bytes("test", "utf-8")], cls=SdkJSONEncoder) == '["dGVzdA==", "dGVzdA=="]'
3929
- )
3930
- assert (
3931
- json.dumps([bytearray("test", "utf-8"), bytearray("test", "utf-8")], cls=SdkJSONEncoder)
3932
- == '["dGVzdA==", "dGVzdA=="]'
3933
- )
3934
- assert (
3935
- json.dumps(
3936
- [bytes("test", "utf-8"), bytes("test", "utf-8")],
3937
- cls=SdkJSONEncoder,
3938
- format="base64",
3939
- )
3940
- == '["dGVzdA==", "dGVzdA=="]'
3941
- )
3942
- assert (
3943
- json.dumps(
3944
- [bytes("test", "utf-8"), bytes("test", "utf-8")],
3945
- cls=SdkJSONEncoder,
3946
- format="base64url",
3947
- )
3948
- == '["dGVzdA", "dGVzdA"]'
3949
- )
3950
- assert (
3951
- json.dumps(
3952
- [bytearray("test", "utf-8"), bytearray("test", "utf-8")],
3953
- cls=SdkJSONEncoder,
3954
- format="base64",
3955
- )
3956
- == '["dGVzdA==", "dGVzdA=="]'
3957
- )
3958
- assert (
3959
- json.dumps(
3960
- [bytearray("test", "utf-8"), bytearray("test", "utf-8")],
3961
- cls=SdkJSONEncoder,
3962
- format="base64url",
3963
- )
3964
- == '["dGVzdA", "dGVzdA"]'
3965
- )
3966
-
3967
- assert (
3968
- json.dumps(
3969
- {"a": bytes("test", "utf-8"), "b": bytes("test", "utf-8")},
3970
- cls=SdkJSONEncoder,
3971
- )
3972
- == '{"a": "dGVzdA==", "b": "dGVzdA=="}'
3973
- )
3974
- assert (
3975
- json.dumps(
3976
- {"a": bytearray("test", "utf-8"), "b": bytearray("test", "utf-8")},
3977
- cls=SdkJSONEncoder,
3978
- )
3979
- == '{"a": "dGVzdA==", "b": "dGVzdA=="}'
3980
- )
3981
- assert (
3982
- json.dumps(
3983
- {"a": bytes("test", "utf-8"), "b": bytes("test", "utf-8")},
3984
- cls=SdkJSONEncoder,
3985
- format="base64",
3986
- )
3987
- == '{"a": "dGVzdA==", "b": "dGVzdA=="}'
3988
- )
3989
- assert (
3990
- json.dumps(
3991
- {"a": bytes("test", "utf-8"), "b": bytes("test", "utf-8")},
3992
- cls=SdkJSONEncoder,
3993
- format="base64url",
3994
- )
3995
- == '{"a": "dGVzdA", "b": "dGVzdA"}'
3996
- )
3997
- assert (
3998
- json.dumps(
3999
- {"a": bytearray("test", "utf-8"), "b": bytearray("test", "utf-8")},
4000
- cls=SdkJSONEncoder,
4001
- format="base64",
4002
- )
4003
- == '{"a": "dGVzdA==", "b": "dGVzdA=="}'
4004
- )
4005
- assert (
4006
- json.dumps(
4007
- {"a": bytearray("test", "utf-8"), "b": bytearray("test", "utf-8")},
4008
- cls=SdkJSONEncoder,
4009
- format="base64url",
4010
- )
4011
- == '{"a": "dGVzdA", "b": "dGVzdA"}'
4012
- )
4013
-
4014
-
4015
- def test_decimal_deserialization():
4016
- class DecimalModel(Model):
4017
- decimal_value: decimal.Decimal = rest_field(name="decimalValue")
4018
-
4019
- @overload
4020
- def __init__(self, *, decimal_value: decimal.Decimal): ...
4021
-
4022
- @overload
4023
- def __init__(self, mapping: Mapping[str, Any], /): ...
4024
-
4025
- def __init__(self, *args, **kwargs):
4026
- super().__init__(*args, **kwargs)
4027
-
4028
- model = DecimalModel({"decimalValue": 0.33333})
4029
- assert model["decimalValue"] == 0.33333
4030
- assert model.decimal_value == decimal.Decimal("0.33333")
4031
-
4032
- class BaseModel(Model):
4033
- my_prop: DecimalModel = rest_field(name="myProp")
4034
-
4035
- model = BaseModel({"myProp": {"decimalValue": 0.33333}})
4036
- assert isinstance(model.my_prop, DecimalModel)
4037
- assert model.my_prop["decimalValue"] == model["myProp"]["decimalValue"] == 0.33333
4038
- assert model.my_prop.decimal_value == decimal.Decimal("0.33333")
4039
-
4040
-
4041
- def test_decimal_serialization():
4042
- assert json.dumps(decimal.Decimal("0.33333"), cls=SdkJSONEncoder) == "0.33333"
4043
- assert (
4044
- json.dumps([decimal.Decimal("0.33333"), decimal.Decimal("0.33333")], cls=SdkJSONEncoder) == "[0.33333, 0.33333]"
4045
- )
4046
- assert (
4047
- json.dumps(
4048
- {"a": decimal.Decimal("0.33333"), "b": decimal.Decimal("0.33333")},
4049
- cls=SdkJSONEncoder,
4050
- )
4051
- == '{"a": 0.33333, "b": 0.33333}'
4052
- )
4053
-
4054
-
4055
- def test_int_as_str_deserialization():
4056
- class IntAsStrModel(Model):
4057
- int_as_str_value: int = rest_field(name="intAsStrValue", format="str")
4058
-
4059
- model = IntAsStrModel({"intAsStrValue": "123"})
4060
- assert model["intAsStrValue"] == "123"
4061
- assert model.int_as_str_value == 123
4062
-
4063
- class BaseModel(Model):
4064
- my_prop: IntAsStrModel = rest_field(name="myProp")
4065
-
4066
- model = BaseModel({"myProp": {"intAsStrValue": "123"}})
4067
- assert isinstance(model.my_prop, IntAsStrModel)
4068
- assert model.my_prop["intAsStrValue"] == model["myProp"]["intAsStrValue"] == "123"
4069
- assert model.my_prop.int_as_str_value == 123
4070
-
4071
-
4072
- def test_deserialize():
4073
- expected = {"name": "name", "role": "role"}
4074
- result = _deserialize(JSON, expected)
4075
- assert result == expected
4076
-
4077
-
4078
- def test_enum_deserialization():
4079
- class MyEnum(Enum):
4080
- A = "a"
4081
- B = "b"
4082
-
4083
- class ModelWithEnumProperty(Model):
4084
- enum_property: Union[str, MyEnum] = rest_field(name="enumProperty")
4085
- enum_property_optional: Optional[Union[str, MyEnum]] = rest_field(name="enumPropertyOptional")
4086
- enum_property_optional_none: Optional[Union[str, MyEnum]] = rest_field(name="enumPropertyOptionalNone")
4087
-
4088
- raw_input = {
4089
- "enumProperty": "a",
4090
- "enumPropertyOptional": "b",
4091
- "enumPropertyOptionalNone": None,
4092
- }
4093
-
4094
- def check_func(target: ModelWithEnumProperty):
4095
- assert target.enum_property == MyEnum.A
4096
- assert target["enumProperty"] == "a"
4097
- assert isinstance(target.enum_property, Enum)
4098
- assert isinstance(target["enumProperty"], str)
4099
-
4100
- assert target.enum_property_optional == MyEnum.B
4101
- assert target["enumPropertyOptional"] == "b"
4102
- assert isinstance(target.enum_property_optional, Enum)
4103
- assert isinstance(target["enumPropertyOptional"], str)
4104
-
4105
- assert target.enum_property_optional_none is None
4106
- assert target["enumPropertyOptionalNone"] is None
4107
-
4108
- model = ModelWithEnumProperty(raw_input)
4109
- check_func(model)
4110
-
4111
- result = _deserialize(list[ModelWithEnumProperty], [raw_input])
4112
- for item in result:
4113
- check_func(item)
4114
-
4115
-
4116
- def test_not_mutating_original_dict():
4117
- class MyInnerModel(Model):
4118
- property: str = rest_field()
4119
-
4120
- class MyModel(Model):
4121
- property: MyInnerModel = rest_field()
4122
-
4123
- origin = {"property": {"property": "hello"}}
4124
-
4125
- dpg_model = MyModel(origin)
4126
- assert dpg_model["property"]["property"] == "hello"
4127
-
4128
- origin["property"]["property"] = "world"
4129
- assert dpg_model["property"]["property"] == "hello"
4130
-
4131
-
4132
- def test_model_init_io():
4133
- class BytesModel(Model):
4134
- property: bytes = rest_field()
4135
-
4136
- JPG = Path(__file__).parent.parent / "data/image.jpg"
4137
- with open(JPG, "rb") as f:
4138
- b = BytesModel({"property": f})
4139
- assert b.property == f
4140
- assert b["property"] == f
4141
- with open(JPG, "rb") as f:
4142
- b = BytesModel(property=f)
4143
- assert b.property == f
4144
- assert b["property"] == f
4145
-
4146
-
4147
- def test_additional_properties_serialization():
4148
- value = {
4149
- "name": "test",
4150
- "modelProp": {"name": "test"},
4151
- "stringProp": "string",
4152
- "intProp": 1,
4153
- "floatProp": 1.0,
4154
- "boolProp": True,
4155
- "listProp": [1, 2, 3],
4156
- "dictProp": {"key": "value"},
4157
- "noneProp": None,
4158
- "datetimeProp": "2023-06-27T06:11:09Z",
4159
- "durationProp": "P1D",
4160
- }
4161
-
4162
- class NormalModel(Model):
4163
- prop: str = rest_field(name="name")
4164
-
4165
- class AdditionalPropertiesModel(Model):
4166
- name: str = rest_field(name="name")
4167
-
4168
- model = AdditionalPropertiesModel(name="test")
4169
- prop = NormalModel(prop="test")
4170
- model["modelProp"] = prop
4171
- model["stringProp"] = "string"
4172
- model["intProp"] = 1
4173
- model["floatProp"] = 1.0
4174
- model["boolProp"] = True
4175
- model["listProp"] = [1, 2, 3]
4176
- model["dictProp"] = {"key": "value"}
4177
- model["noneProp"] = None
4178
- model["datetimeProp"] = datetime.datetime(2023, 6, 27, 6, 11, 9, tzinfo=datetime.timezone.utc)
4179
- model["durationProp"] = datetime.timedelta(days=1)
4180
-
4181
- assert json.loads(json.dumps(model, cls=SdkJSONEncoder)) == value
4182
-
4183
-
4184
- class Animal(Model):
4185
- __mapping__: dict[str, Model] = {}
4186
- kind: str = rest_discriminator(name="kind")
4187
- name: str = rest_field()
4188
-
4189
- def __init__(self, *args: Any, **kwargs: Any) -> None:
4190
- super().__init__(*args, **kwargs)
4191
-
4192
-
4193
- class AnotherPet(Animal, discriminator="pet"):
4194
- __mapping__: dict[str, Model] = {}
4195
- kind: Literal["pet"] = rest_discriminator(name="kind")
4196
- trained: bool = rest_field()
4197
-
4198
- def __init__(self, *args: Any, **kwargs: Any) -> None:
4199
- super().__init__(*args, **kwargs)
4200
- self.kind = "pet"
4201
-
4202
-
4203
- class AnotherDog(AnotherPet, discriminator="dog"):
4204
- kind: Literal["dog"] = rest_discriminator(name="kind")
4205
- breed: str = rest_field()
4206
-
4207
- def __init__(self, *args: Any, **kwargs: Any) -> None:
4208
- super().__init__(*args, **kwargs)
4209
- self.kind = "dog"
4210
-
4211
-
4212
- def test_multi_layer_discriminator():
4213
- pet = {"kind": "pet", "name": "Buddy", "trained": True}
4214
-
4215
- dog = {"kind": "dog", "name": "Rex", "trained": True, "breed": "German Shepherd"}
4216
-
4217
- model_pet = AnotherPet(pet)
4218
- assert model_pet == pet
4219
-
4220
- model_dog = AnotherDog(dog)
4221
- assert model_dog == dog
4222
-
4223
- assert _deserialize(Animal, pet) == model_pet
4224
- assert _deserialize(Animal, dog) == model_dog
4225
- assert _deserialize(AnotherPet, dog) == model_dog
4226
-
4227
- assert AnotherPet(name="Buddy", trained=True) == model_pet
4228
- assert AnotherDog(name="Rex", trained=True, breed="German Shepherd") == model_dog
4229
-
4230
-
4231
- def test_array_encode_comma_delimited():
4232
- """Test commaDelimited format for array of strings"""
4233
-
4234
- class CommaDelimitedModel(Model):
4235
- colors: list[str] = rest_field(format="commaDelimited")
4236
-
4237
- @overload
4238
- def __init__(self, *, colors: list[str]): ...
4239
-
4240
- @overload
4241
- def __init__(self, mapping: Mapping[str, Any], /): ...
4242
-
4243
- def __init__(self, *args, **kwargs):
4244
- super().__init__(*args, **kwargs)
4245
-
4246
- # Test serialization: list[str] -> comma-delimited string
4247
- model = CommaDelimitedModel(colors=["blue", "red", "green"])
4248
- assert model.colors == ["blue", "red", "green"]
4249
- assert model["colors"] == "blue,red,green"
4250
-
4251
- # Test deserialization: comma-delimited string -> list[str]
4252
- model = CommaDelimitedModel({"colors": "blue,red,green"})
4253
- assert model.colors == ["blue", "red", "green"]
4254
- assert model["colors"] == "blue,red,green"
4255
-
4256
- # Test with empty list
4257
- model = CommaDelimitedModel(colors=[])
4258
- assert model.colors == []
4259
- assert model["colors"] == ""
4260
-
4261
- # Test with single item
4262
- model = CommaDelimitedModel(colors=["blue"])
4263
- assert model.colors == ["blue"]
4264
- assert model["colors"] == "blue"
4265
-
4266
-
4267
- def test_array_encode_pipe_delimited():
4268
- """Test pipeDelimited format for array of strings"""
4269
-
4270
- class PipeDelimitedModel(Model):
4271
- colors: list[str] = rest_field(format="pipeDelimited")
4272
-
4273
- @overload
4274
- def __init__(self, *, colors: list[str]): ...
4275
-
4276
- @overload
4277
- def __init__(self, mapping: Mapping[str, Any], /): ...
4278
-
4279
- def __init__(self, *args, **kwargs):
4280
- super().__init__(*args, **kwargs)
4281
-
4282
- # Test serialization: list[str] -> pipe-delimited string
4283
- model = PipeDelimitedModel(colors=["blue", "red", "green"])
4284
- assert model.colors == ["blue", "red", "green"]
4285
- assert model["colors"] == "blue|red|green"
4286
-
4287
- # Test deserialization: pipe-delimited string -> list[str]
4288
- model = PipeDelimitedModel({"colors": "blue|red|green"})
4289
- assert model.colors == ["blue", "red", "green"]
4290
- assert model["colors"] == "blue|red|green"
4291
-
4292
- # Test with empty list
4293
- model = PipeDelimitedModel(colors=[])
4294
- assert model.colors == []
4295
- assert model["colors"] == ""
4296
-
4297
-
4298
- def test_array_encode_space_delimited():
4299
- """Test spaceDelimited format for array of strings"""
4300
-
4301
- class SpaceDelimitedModel(Model):
4302
- colors: list[str] = rest_field(format="spaceDelimited")
4303
-
4304
- @overload
4305
- def __init__(self, *, colors: list[str]): ...
4306
-
4307
- @overload
4308
- def __init__(self, mapping: Mapping[str, Any], /): ...
4309
-
4310
- def __init__(self, *args, **kwargs):
4311
- super().__init__(*args, **kwargs)
4312
-
4313
- # Test serialization: list[str] -> space-delimited string
4314
- model = SpaceDelimitedModel(colors=["blue", "red", "green"])
4315
- assert model.colors == ["blue", "red", "green"]
4316
- assert model["colors"] == "blue red green"
4317
-
4318
- # Test deserialization: space-delimited string -> list[str]
4319
- model = SpaceDelimitedModel({"colors": "blue red green"})
4320
- assert model.colors == ["blue", "red", "green"]
4321
- assert model["colors"] == "blue red green"
4322
-
4323
- # Test with empty list
4324
- model = SpaceDelimitedModel(colors=[])
4325
- assert model.colors == []
4326
- assert model["colors"] == ""
4327
-
4328
-
4329
- def test_array_encode_newline_delimited():
4330
- """Test newlineDelimited format for array of strings"""
4331
-
4332
- class NewlineDelimitedModel(Model):
4333
- colors: list[str] = rest_field(format="newlineDelimited")
4334
-
4335
- @overload
4336
- def __init__(self, *, colors: list[str]): ...
4337
-
4338
- @overload
4339
- def __init__(self, mapping: Mapping[str, Any], /): ...
4340
-
4341
- def __init__(self, *args, **kwargs):
4342
- super().__init__(*args, **kwargs)
4343
-
4344
- # Test serialization: list[str] -> newline-delimited string
4345
- model = NewlineDelimitedModel(colors=["blue", "red", "green"])
4346
- assert model.colors == ["blue", "red", "green"]
4347
- assert model["colors"] == "blue\nred\ngreen"
4348
-
4349
- # Test deserialization: newline-delimited string -> list[str]
4350
- model = NewlineDelimitedModel({"colors": "blue\nred\ngreen"})
4351
- assert model.colors == ["blue", "red", "green"]
4352
- assert model["colors"] == "blue\nred\ngreen"
4353
-
4354
- # Test with empty list
4355
- model = NewlineDelimitedModel(colors=[])
4356
- assert model.colors == []
4357
- assert model["colors"] == ""
4358
-
4359
-
4360
- def test_array_encode_optional():
4361
- """Test array encoding with optional fields"""
4362
-
4363
- class OptionalEncodedModel(Model):
4364
- colors: Optional[list[str]] = rest_field(default=None, format="commaDelimited")
4365
-
4366
- @overload
4367
- def __init__(self, *, colors: Optional[list[str]] = None): ...
4368
-
4369
- @overload
4370
- def __init__(self, mapping: Mapping[str, Any], /): ...
4371
-
4372
- def __init__(self, *args, **kwargs):
4373
- super().__init__(*args, **kwargs)
4374
-
4375
- # Test with None
4376
- model = OptionalEncodedModel(colors=None)
4377
- assert model.colors is None
4378
- assert model["colors"] is None
4379
-
4380
- # Test with value
4381
- model = OptionalEncodedModel(colors=["blue", "red"])
4382
- assert model.colors == ["blue", "red"]
4383
- assert model["colors"] == "blue,red"
4384
-
4385
- # Test deserialization with None
4386
- model = OptionalEncodedModel({"colors": None})
4387
- assert model.colors is None
4388
-
4389
-
4390
- def test_array_encode_modification():
4391
- """Test modifying array-encoded fields"""
4392
-
4393
- class ModifiableModel(Model):
4394
- colors: list[str] = rest_field(format="commaDelimited")
4395
-
4396
- @overload
4397
- def __init__(self, *, colors: list[str]): ...
4398
-
4399
- @overload
4400
- def __init__(self, mapping: Mapping[str, Any], /): ...
4401
-
4402
- def __init__(self, *args, **kwargs):
4403
- super().__init__(*args, **kwargs)
4404
-
4405
- model = ModifiableModel(colors=["blue", "red"])
4406
- assert model.colors == ["blue", "red"]
4407
- assert model["colors"] == "blue,red"
4408
-
4409
- # Modify through property
4410
- model.colors = ["green", "yellow", "purple"]
4411
- assert model.colors == ["green", "yellow", "purple"]
4412
- assert model["colors"] == "green,yellow,purple"
4413
-
4414
- # Modify through dict access
4415
- model["colors"] = "orange,pink"
4416
- assert model.colors == ["orange", "pink"]
4417
- assert model["colors"] == "orange,pink"
4418
-
4419
-
4420
- def test_array_encode_json_roundtrip():
4421
- """Test JSON serialization and deserialization with array encoding"""
4422
-
4423
- class JsonModel(Model):
4424
- pipe_colors: list[str] = rest_field(name="pipeColors", format="pipeDelimited")
4425
- comma_colors: list[str] = rest_field(name="commaColors", format="commaDelimited")
4426
-
4427
- @overload
4428
- def __init__(
4429
- self,
4430
- *,
4431
- pipe_colors: list[str],
4432
- comma_colors: list[str],
4433
- ): ...
4434
-
4435
- @overload
4436
- def __init__(self, mapping: Mapping[str, Any], /): ...
4437
-
4438
- def __init__(self, *args, **kwargs):
4439
- super().__init__(*args, **kwargs)
4440
-
4441
- model = JsonModel(
4442
- pipe_colors=["blue", "red", "green"],
4443
- comma_colors=["small", "medium", "large"],
4444
- )
4445
-
4446
- # Serialize to JSON
4447
- json_str = json.dumps(dict(model), cls=SdkJSONEncoder)
4448
- assert json.loads(json_str) == {
4449
- "pipeColors": "blue|red|green",
4450
- "commaColors": "small,medium,large",
4451
- }
4452
-
4453
- # Deserialize from JSON
4454
- deserialized = JsonModel(json.loads(json_str))
4455
- assert deserialized.pipe_colors == ["blue", "red", "green"]
4456
- assert deserialized.comma_colors == ["small", "medium", "large"]
4457
-
4458
-
4459
- def test_array_encode_with_special_characters():
4460
- """Test array encoding with strings containing special characters"""
4461
-
4462
- class SpecialCharsModel(Model):
4463
- comma_values: list[str] = rest_field(name="commaValues", format="commaDelimited")
4464
- pipe_values: list[str] = rest_field(name="pipeValues", format="pipeDelimited")
4465
-
4466
- @overload
4467
- def __init__(
4468
- self,
4469
- *,
4470
- comma_values: list[str],
4471
- pipe_values: list[str],
4472
- ): ...
4473
-
4474
- @overload
4475
- def __init__(self, mapping: Mapping[str, Any], /): ...
4476
-
4477
- def __init__(self, *args, **kwargs):
4478
- super().__init__(*args, **kwargs)
4479
-
4480
- # Test with strings that might contain delimiters
4481
- # Note: In real usage, the strings should not contain the delimiter character
4482
- # This test documents current behavior
4483
- model = SpecialCharsModel(
4484
- comma_values=["value with spaces", "another-value", "value_3"],
4485
- pipe_values=["path/to/file", "another-path", "final.path"],
4486
- )
4487
-
4488
- assert model.comma_values == ["value with spaces", "another-value", "value_3"]
4489
- assert model["commaValues"] == "value with spaces,another-value,value_3"
4490
-
4491
- assert model.pipe_values == ["path/to/file", "another-path", "final.path"]
4492
- assert model["pipeValues"] == "path/to/file|another-path|final.path"
4493
-
4494
-
4495
- def test_dictionary_set():
4496
- """Test that dictionary mutations via attribute syntax persist and sync to dictionary syntax."""
4497
-
4498
- class MyModel(Model):
4499
- my_dict: dict[str, int] = rest_field(visibility=["read", "create", "update", "delete", "query"])
4500
-
4501
- # Test 1: Basic mutation via attribute syntax
4502
- m = MyModel(my_dict={"a": 1, "b": 2})
4503
- assert m.my_dict == {"a": 1, "b": 2}
4504
-
4505
- # Test 2: Add new key via attribute syntax and verify it persists
4506
- m.my_dict["c"] = 3
4507
- assert m.my_dict["c"] == 3
4508
- assert m.my_dict == {"a": 1, "b": 2, "c": 3}
4509
-
4510
- # Test 3: Verify mutation is reflected in dictionary syntax
4511
- assert m["my_dict"] == {"a": 1, "b": 2, "c": 3}
4512
-
4513
- # Test 4: Modify existing key via attribute syntax
4514
- m.my_dict["a"] = 100
4515
- assert m.my_dict["a"] == 100
4516
- assert m["my_dict"]["a"] == 100
4517
-
4518
- # Test 5: Delete key via attribute syntax
4519
- del m.my_dict["b"]
4520
- assert "b" not in m.my_dict
4521
- assert "b" not in m["my_dict"]
4522
-
4523
- # Test 6: Update via dict methods
4524
- m.my_dict.update({"d": 4, "e": 5})
4525
- assert m.my_dict["d"] == 4
4526
- assert m.my_dict["e"] == 5
4527
- assert m["my_dict"]["d"] == 4
4528
-
4529
- # Test 7: Clear via attribute syntax and verify via dictionary syntax
4530
- m.my_dict.clear()
4531
- assert len(m.my_dict) == 0
4532
- assert len(m["my_dict"]) == 0
4533
-
4534
- # Test 8: Reassign entire dictionary via attribute syntax
4535
- m.my_dict = {"x": 10, "y": 20}
4536
- assert m.my_dict == {"x": 10, "y": 20}
4537
- assert m["my_dict"] == {"x": 10, "y": 20}
4538
-
4539
- # Test 9: Mutation after reassignment
4540
- m.my_dict["z"] = 30
4541
- assert m.my_dict["z"] == 30
4542
- assert m["my_dict"]["z"] == 30
4543
-
4544
- # Test 10: Access via dictionary syntax first, then mutate via attribute syntax
4545
- m.my_dict["w"] = 40
4546
- assert m["my_dict"]["w"] == 40
4547
-
4548
- # Test 11: Multiple accesses maintain same cached object
4549
- dict_ref1 = m.my_dict
4550
- dict_ref2 = m.my_dict
4551
- assert dict_ref1 is dict_ref2
4552
- dict_ref1["new_key"] = 999
4553
- assert dict_ref2["new_key"] == 999
4554
- assert m.my_dict["new_key"] == 999
4555
-
4556
-
4557
- def test_list_set():
4558
- """Test that list mutations via attribute syntax persist and sync to dictionary syntax."""
4559
-
4560
- class MyModel(Model):
4561
- my_list: list[int] = rest_field(visibility=["read", "create", "update", "delete", "query"])
4562
-
4563
- # Test 1: Basic mutation via attribute syntax
4564
- m = MyModel(my_list=[1, 2, 3])
4565
- assert m.my_list == [1, 2, 3]
4566
-
4567
- # Test 2: Append via attribute syntax and verify it persists
4568
- m.my_list.append(4)
4569
- assert m.my_list == [1, 2, 3, 4]
4570
-
4571
- # Test 3: Verify mutation is reflected in dictionary syntax
4572
- assert m["my_list"] == [1, 2, 3, 4]
4573
-
4574
- # Test 4: Modify existing element via attribute syntax
4575
- m.my_list[0] = 100
4576
- assert m.my_list[0] == 100
4577
- assert m["my_list"][0] == 100
4578
-
4579
- # Test 5: Extend list via attribute syntax
4580
- m.my_list.extend([5, 6])
4581
- assert m.my_list == [100, 2, 3, 4, 5, 6]
4582
- assert m["my_list"] == [100, 2, 3, 4, 5, 6]
4583
-
4584
- # Test 6: Remove element via attribute syntax
4585
- m.my_list.remove(2)
4586
- assert 2 not in m.my_list
4587
- assert 2 not in m["my_list"]
4588
-
4589
- # Test 7: Pop element
4590
- popped = m.my_list.pop()
4591
- assert popped == 6
4592
- assert 6 not in m.my_list
4593
- assert 6 not in m["my_list"]
4594
-
4595
- # Test 8: Insert element
4596
- m.my_list.insert(0, 999)
4597
- assert m.my_list[0] == 999
4598
- assert m["my_list"][0] == 999
4599
-
4600
- # Test 9: Clear via attribute syntax
4601
- m.my_list.clear()
4602
- assert len(m.my_list) == 0
4603
- assert len(m["my_list"]) == 0
4604
-
4605
- # Test 10: Reassign entire list via attribute syntax
4606
- m.my_list = [10, 20, 30]
4607
- assert m.my_list == [10, 20, 30]
4608
- assert m["my_list"] == [10, 20, 30]
4609
-
4610
- # Test 11: Mutation after reassignment
4611
- m.my_list.append(40)
4612
- assert m.my_list == [10, 20, 30, 40]
4613
- assert m["my_list"] == [10, 20, 30, 40]
4614
-
4615
- # Test 12: Multiple accesses maintain same cached object
4616
- list_ref1 = m.my_list
4617
- list_ref2 = m.my_list
4618
- assert list_ref1 is list_ref2
4619
- list_ref1.append(50)
4620
- assert 50 in list_ref2
4621
- assert 50 in m.my_list
4622
-
4623
-
4624
- def test_set_collection():
4625
- """Test that set mutations via attribute syntax persist and sync to dictionary syntax."""
4626
-
4627
- class MyModel(Model):
4628
- my_set: set[int] = rest_field(visibility=["read", "create", "update", "delete", "query"])
4629
-
4630
- # Test 1: Basic mutation via attribute syntax
4631
- m = MyModel(my_set={1, 2, 3})
4632
- assert m.my_set == {1, 2, 3}
4633
-
4634
- # Test 2: Add via attribute syntax and verify it persists
4635
- m.my_set.add(4)
4636
- assert 4 in m.my_set
4637
-
4638
- # Test 3: Verify mutation is reflected in dictionary syntax
4639
- assert 4 in m["my_set"]
4640
-
4641
- # Test 4: Remove element via attribute syntax
4642
- m.my_set.remove(2)
4643
- assert 2 not in m.my_set
4644
- assert 2 not in m["my_set"]
4645
-
4646
- # Test 5: Update set via attribute syntax
4647
- m.my_set.update({5, 6, 7})
4648
- assert m.my_set == {1, 3, 4, 5, 6, 7}
4649
- assert m["my_set"] == {1, 3, 4, 5, 6, 7}
4650
-
4651
- # Test 6: Discard element
4652
- m.my_set.discard(1)
4653
- assert 1 not in m.my_set
4654
- assert 1 not in m["my_set"]
4655
-
4656
- # Test 7: Clear via attribute syntax
4657
- m.my_set.clear()
4658
- assert len(m.my_set) == 0
4659
- assert len(m["my_set"]) == 0
4660
-
4661
- # Test 8: Reassign entire set via attribute syntax
4662
- m.my_set = {10, 20, 30}
4663
- assert m.my_set == {10, 20, 30}
4664
- assert m["my_set"] == {10, 20, 30}
4665
-
4666
- # Test 9: Mutation after reassignment
4667
- m.my_set.add(40)
4668
- assert 40 in m.my_set
4669
- assert 40 in m["my_set"]
4670
-
4671
- # Test 10: Multiple accesses maintain same cached object
4672
- set_ref1 = m.my_set
4673
- set_ref2 = m.my_set
4674
- assert set_ref1 is set_ref2
4675
- set_ref1.add(50)
4676
- assert 50 in set_ref2
4677
- assert 50 in m.my_set
4678
-
4679
-
4680
- def test_dictionary_set_datetime():
4681
- """Test that dictionary with datetime values properly serializes/deserializes."""
4682
- from datetime import datetime, timezone
4683
-
4684
- class MyModel(Model):
4685
- my_dict: dict[str, datetime] = rest_field(visibility=["read", "create", "update", "delete", "query"])
4686
-
4687
- # Test 1: Initialize with datetime values
4688
- dt1 = datetime(2023, 1, 15, 10, 30, 45, tzinfo=timezone.utc)
4689
- dt2 = datetime(2023, 6, 20, 14, 15, 30, tzinfo=timezone.utc)
4690
- m = MyModel(my_dict={"created": dt1, "updated": dt2})
4691
-
4692
- # Test 2: Access via attribute syntax returns datetime objects
4693
- assert isinstance(m.my_dict["created"], datetime)
4694
- assert isinstance(m.my_dict["updated"], datetime)
4695
- assert m.my_dict["created"] == dt1
4696
- assert m.my_dict["updated"] == dt2
4697
-
4698
- # Test 3: Access via dictionary syntax returns serialized strings (ISO format)
4699
- dict_access = m["my_dict"]
4700
- assert isinstance(dict_access["created"], str)
4701
- assert isinstance(dict_access["updated"], str)
4702
- assert dict_access["created"] == "2023-01-15T10:30:45Z"
4703
- assert dict_access["updated"] == "2023-06-20T14:15:30Z"
4704
-
4705
- # Test 4: Mutate via attribute syntax with new datetime
4706
- dt3 = datetime(2023, 12, 25, 18, 0, 0, tzinfo=timezone.utc)
4707
- m.my_dict["holiday"] = dt3
4708
- assert m.my_dict["holiday"] == dt3
4709
-
4710
- # Test 5: Verify mutation is serialized in dictionary syntax
4711
- assert m["my_dict"]["holiday"] == "2023-12-25T18:00:00Z"
4712
-
4713
- # Test 6: Update existing datetime via attribute syntax
4714
- dt4 = datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
4715
- m.my_dict["created"] = dt4
4716
- assert m.my_dict["created"] == dt4
4717
- assert m["my_dict"]["created"] == "2024-01-01T00:00:00Z"
4718
-
4719
- # Test 7: Verify all datetimes are deserialized correctly after mutation
4720
- assert isinstance(m.my_dict["created"], datetime)
4721
- assert isinstance(m.my_dict["updated"], datetime)
4722
- assert isinstance(m.my_dict["holiday"], datetime)
4723
-
4724
- # Test 8: Use dict update method with datetimes
4725
- dt5 = datetime(2024, 6, 15, 12, 30, 0, tzinfo=timezone.utc)
4726
- dt6 = datetime(2024, 7, 4, 16, 45, 0, tzinfo=timezone.utc)
4727
- m.my_dict.update({"event1": dt5, "event2": dt6})
4728
- assert m.my_dict["event1"] == dt5
4729
- assert m["my_dict"]["event1"] == "2024-06-15T12:30:00Z"
4730
-
4731
- # Test 9: Reassign entire dictionary with new datetimes
4732
- dt7 = datetime(2025, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
4733
- dt8 = datetime(2025, 12, 31, 23, 59, 59, tzinfo=timezone.utc)
4734
- m.my_dict = {"start": dt7, "end": dt8}
4735
- assert m.my_dict["start"] == dt7
4736
- assert m.my_dict["end"] == dt8
4737
- assert m["my_dict"]["start"] == "2025-01-01T00:00:00Z"
4738
- assert m["my_dict"]["end"] == "2025-12-31T23:59:59Z"
4739
-
4740
- # Test 10: Cached object maintains datetime type
4741
- dict_ref1 = m.my_dict
4742
- dict_ref2 = m.my_dict
4743
- assert dict_ref1 is dict_ref2
4744
- assert isinstance(dict_ref1["start"], datetime)
4745
- assert isinstance(dict_ref2["start"], datetime)
4746
-
4747
-
4748
- def test_eq_same_model_instances():
4749
- """Two model instances with the same data should be equal."""
4750
- model1 = BasicResource(
4751
- platform_update_domain_count=5,
4752
- platform_fault_domain_count=3,
4753
- virtual_machines=[],
4754
- )
4755
- model2 = BasicResource(
4756
- platform_update_domain_count=5,
4757
- platform_fault_domain_count=3,
4758
- virtual_machines=[],
4759
- )
4760
- assert model1 == model2
4761
-
4762
-
4763
- def test_eq_different_model_data():
4764
- """Two model instances with different data should not be equal."""
4765
- model1 = BasicResource(
4766
- platform_update_domain_count=5,
4767
- platform_fault_domain_count=3,
4768
- virtual_machines=[],
4769
- )
4770
- model2 = BasicResource(
4771
- platform_update_domain_count=10,
4772
- platform_fault_domain_count=3,
4773
- virtual_machines=[],
4774
- )
4775
- assert model1 != model2
4776
-
4777
-
4778
- def test_eq_model_constructed_from_dict():
4779
- """A model created via kwargs and a model created from a dict should be equal."""
4780
- dict_response = {
4781
- "platformUpdateDomainCount": 5,
4782
- "platformFaultDomainCount": 3,
4783
- "virtualMachines": [],
4784
- }
4785
- model_from_kwargs = BasicResource(
4786
- platform_update_domain_count=5,
4787
- platform_fault_domain_count=3,
4788
- virtual_machines=[],
4789
- )
4790
- model_from_dict = BasicResource(dict_response)
4791
- assert model_from_kwargs == model_from_dict
4792
-
4793
-
4794
- def test_eq_model_equal_to_plain_dict():
4795
- """A model should be equal to a plain dict with matching data (backward compat)."""
4796
- dict_response = {
4797
- "platformUpdateDomainCount": 5,
4798
- "platformFaultDomainCount": 3,
4799
- "virtualMachines": [],
4800
- }
4801
- model = BasicResource(
4802
- platform_update_domain_count=5,
4803
- platform_fault_domain_count=3,
4804
- virtual_machines=[],
4805
- )
4806
- assert model == dict_response
4807
-
4808
-
4809
- def test_eq_model_not_equal_to_non_model_types():
4810
- """A model should not be equal to strings, ints, None, or lists."""
4811
- model = BasicResource(
4812
- platform_update_domain_count=5,
4813
- platform_fault_domain_count=3,
4814
- virtual_machines=[],
4815
- )
4816
- assert model != "some string"
4817
- assert model != 42
4818
- assert model != None # noqa: E711
4819
- assert model != []
4820
- assert model != True # noqa: E712
4821
-
4822
-
4823
- def test_eq_different_model_types_same_data():
4824
- """Two different model types with the same underlying _data should be equal
4825
- since isinstance checks against _MyMutableMapping, not the specific subclass."""
4826
- model1 = Pet(name="test", species="dog")
4827
-
4828
- class AnotherModel(Model):
4829
- name: str = rest_field()
4830
- species: str = rest_field()
4831
-
4832
- @overload
4833
- def __init__(self, *, name: str, species: str): ...
4834
-
4835
- @overload
4836
- def __init__(self, mapping: Mapping[str, Any], /): ...
4837
-
4838
- def __init__(self, *args, **kwargs):
4839
- super().__init__(*args, **kwargs)
4840
-
4841
- model2 = AnotherModel(name="test", species="dog")
4842
- assert model1 == model2
4843
-
4844
-
4845
- def test_ne_is_inverse_of_eq():
4846
- """__ne__ should return the inverse of __eq__."""
4847
- model1 = BasicResource(
4848
- platform_update_domain_count=5,
4849
- platform_fault_domain_count=3,
4850
- virtual_machines=[],
4851
- )
4852
- model2 = BasicResource(
4853
- platform_update_domain_count=5,
4854
- platform_fault_domain_count=3,
4855
- virtual_machines=[],
4856
- )
4857
- model3 = BasicResource(
4858
- platform_update_domain_count=10,
4859
- platform_fault_domain_count=3,
4860
- virtual_machines=[],
4861
- )
4862
- assert not (model1 != model2)
4863
- assert model1 != model3
4864
-
4865
-
4866
- def test_eq_nested_models():
4867
- """Two models with identical nested model children should be equal."""
4868
- model1 = OptionalModel(
4869
- optional_str="hello",
4870
- optional_time=datetime.time(11, 34, 56),
4871
- optional_dict={
4872
- "buddy": Pet(name="Buddy", species="Dog"),
4873
- },
4874
- optional_myself=OptionalModel(optional_str="inner"),
4875
- )
4876
- model2 = OptionalModel(
4877
- optional_str="hello",
4878
- optional_time=datetime.time(11, 34, 56),
4879
- optional_dict={
4880
- "buddy": Pet(name="Buddy", species="Dog"),
4881
- },
4882
- optional_myself=OptionalModel(optional_str="inner"),
4883
- )
4884
- assert model1 == model2
4885
-
4886
- # Change the nested model's data — they should no longer be equal
4887
- model2_different_inner = OptionalModel(
4888
- optional_str="hello",
4889
- optional_time=datetime.time(11, 34, 56),
4890
- optional_dict={
4891
- "buddy": Pet(name="Buddy", species="Dog"),
4892
- },
4893
- optional_myself=OptionalModel(optional_str="different"),
4894
- )
4895
- assert model1 != model2_different_inner
4896
-
4897
- # Change the nested Pet — they should no longer be equal
4898
- model2_different_pet = OptionalModel(
4899
- optional_str="hello",
4900
- optional_time=datetime.time(11, 34, 56),
4901
- optional_dict={
4902
- "buddy": Pet(name="Max", species="Cat"),
4903
- },
4904
- optional_myself=OptionalModel(optional_str="inner"),
4905
- )
4906
- assert model1 != model2_different_pet