@typespec/http-client-python 0.28.2 → 0.28.3
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.
- package/dist/emitter/code-model.d.ts.map +1 -1
- package/dist/emitter/code-model.js +6 -6
- package/dist/emitter/code-model.js.map +1 -1
- package/dist/emitter/emitter.d.ts.map +1 -1
- package/dist/emitter/emitter.js +27 -28
- package/dist/emitter/emitter.js.map +1 -1
- package/dist/emitter/lib.d.ts +4 -1
- package/dist/emitter/lib.d.ts.map +1 -1
- package/dist/emitter/lib.js.map +1 -1
- package/dist/emitter/system-requirements.js +6 -4
- package/dist/emitter/system-requirements.js.map +1 -1
- package/dist/emitter/types.d.ts +2 -5
- package/dist/emitter/types.d.ts.map +1 -1
- package/dist/emitter/types.js +55 -58
- package/dist/emitter/types.js.map +1 -1
- package/dist/emitter/utils.d.ts.map +1 -1
- package/dist/emitter/utils.js +16 -17
- package/dist/emitter/utils.js.map +1 -1
- package/emitter/src/code-model.ts +6 -13
- package/emitter/src/emitter.ts +29 -31
- package/emitter/src/lib.ts +4 -0
- package/emitter/src/system-requirements.ts +4 -4
- package/emitter/src/types.ts +67 -57
- package/emitter/src/utils.ts +16 -18
- package/emitter/temp/tsconfig.tsbuildinfo +1 -1
- package/eng/scripts/Build-Packages.ps1 +62 -37
- package/eng/scripts/Check-GitChanges.ps1 +30 -3
- package/eng/scripts/Functions.ps1 +54 -15
- package/eng/scripts/Generate.ps1 +42 -8
- package/eng/scripts/Generation.psm1 +44 -15
- package/eng/scripts/Initialize-Repository.ps1 +29 -11
- package/eng/scripts/Test-Packages.ps1 +47 -31
- package/eng/scripts/ci/config/eslint-ci.config.mjs +54 -0
- package/eng/scripts/ci/dev_requirements.txt +1 -0
- package/eng/scripts/ci/format.ts +232 -2
- package/eng/scripts/ci/lint.ts +216 -20
- package/eng/scripts/ci/regenerate-common.ts +18 -14
- package/eng/scripts/ci/regenerate.ts +305 -68
- package/eng/scripts/ci/run-tests.ts +402 -0
- package/eng/scripts/ci/run_mypy.py +4 -3
- package/eng/scripts/ci/run_pylint.py +4 -3
- package/eng/scripts/ci/run_pyright.py +4 -3
- package/eng/scripts/ci/typecheck.ts +225 -0
- package/eng/scripts/ci/util.py +1 -1
- package/eng/scripts/ci/utils.ts +5 -5
- package/eng/scripts/setup/__pycache__/package_manager.cpython-311.pyc +0 -0
- package/eng/scripts/setup/__pycache__/venvtools.cpython-311.pyc +0 -0
- package/eng/scripts/setup/build.ts +1 -1
- package/eng/scripts/setup/build_pygen_wheel.py +1 -0
- package/eng/scripts/setup/install.py +1 -0
- package/eng/scripts/setup/prepare.py +1 -0
- package/eng/scripts/setup/prepare.ts +1 -1
- package/eng/scripts/setup/run_tsp.py +3 -3
- package/eng/scripts/setup/system-requirements.ts +8 -4
- package/eng/scripts/setup/venvtools.py +1 -0
- package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +157 -64
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen/codegen/templates/model_base.py.jinja2 +157 -64
- package/package.json +48 -42
- package/eng/scripts/ci/run-ci.ts +0 -89
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_commonproperties_async.py +0 -94
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_largeheader_async.py +0 -30
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_nonresource_async.py +0 -36
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_operationtemplates_async.py +0 -205
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_arm_resource_async.py +0 -478
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_access_async.py +0 -101
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_api_version_header_async.py +0 -18
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_api_version_path_async.py +0 -18
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_api_version_query_async.py +0 -18
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_client_default_value_async.py +0 -46
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_client_initialization_async.py +0 -58
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_client_location_async.py +0 -82
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_deserialize_empty_string_as_null_async.py +0 -20
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_flatten_async.py +0 -115
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_hierrarchy_building_async.py +0 -53
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_next_link_verb_async.py +0 -28
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_override_async.py +0 -106
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_usage_async.py +0 -38
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_basic_async.py +0 -76
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_lro_rpc_async.py +0 -22
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_lro_standard_async.py +0 -39
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_model_async.py +0 -33
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_page_async.py +0 -80
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_scalar_async.py +0 -41
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_core_traits_async.py +0 -87
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_encode_duration_async.py +0 -19
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_example_basic_async.py +0 -30
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_payload_pageable_async.py +0 -19
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_resource_manager_method_subscription_id_async.py +0 -248
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_resource_manager_multi_service_async.py +0 -110
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_resource_manager_multi_service_shared_models_async.py +0 -132
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_special_headers_client_request_id_async.py +0 -30
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_versioning_previewversion_async.py +0 -53
- package/generator/test/azure/mock_api_tests/asynctests/test_client_namespace_async.py +0 -34
- package/generator/test/azure/mock_api_tests/asynctests/test_client_naming_async.py +0 -69
- package/generator/test/azure/mock_api_tests/asynctests/test_client_naming_enum_conflict_async.py +0 -37
- package/generator/test/azure/mock_api_tests/asynctests/test_client_overload_async.py +0 -27
- package/generator/test/azure/mock_api_tests/asynctests/test_client_structure_async.py +0 -64
- package/generator/test/azure/mock_api_tests/asynctests/test_client_structure_clientoperationgroup_async.py +0 -31
- package/generator/test/azure/mock_api_tests/asynctests/test_encode_duration_async.py +0 -63
- package/generator/test/azure/mock_api_tests/asynctests/test_encode_numeric_async.py +0 -35
- package/generator/test/azure/mock_api_tests/asynctests/test_parameters_basic_async.py +0 -24
- package/generator/test/azure/mock_api_tests/asynctests/test_parameters_spread_async.py +0 -76
- package/generator/test/azure/mock_api_tests/asynctests/test_payload_content_negotiation_async.py +0 -37
- package/generator/test/azure/mock_api_tests/asynctests/test_payload_multipart_async.py +0 -216
- package/generator/test/azure/mock_api_tests/asynctests/test_resiliency_srv_driven_async.py +0 -130
- package/generator/test/azure/mock_api_tests/asynctests/test_serialization_encoded_name_json_async.py +0 -24
- package/generator/test/azure/mock_api_tests/asynctests/test_service_multi_service_async.py +0 -31
- package/generator/test/azure/mock_api_tests/asynctests/test_special_words_async.py +0 -71
- package/generator/test/azure/mock_api_tests/conftest.py +0 -207
- package/generator/test/azure/mock_api_tests/data/image.jpg +0 -0
- package/generator/test/azure/mock_api_tests/data/image.png +0 -0
- package/generator/test/azure/mock_api_tests/test_azure_arm_commonproperties.py +0 -89
- package/generator/test/azure/mock_api_tests/test_azure_arm_largeheader.py +0 -27
- package/generator/test/azure/mock_api_tests/test_azure_arm_nonresource.py +0 -34
- package/generator/test/azure/mock_api_tests/test_azure_arm_operationtemplates.py +0 -180
- package/generator/test/azure/mock_api_tests/test_azure_arm_resource.py +0 -436
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_access.py +0 -92
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_api_version_header.py +0 -17
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_api_version_path.py +0 -17
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_api_version_query.py +0 -17
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_client_default_value.py +0 -42
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_client_initialization.py +0 -51
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_client_location.py +0 -70
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_deserialize_empty_string_as_null.py +0 -19
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_flatten.py +0 -104
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_hierrarchy_building.py +0 -45
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_next_link_verb.py +0 -27
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_override.py +0 -102
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_usage.py +0 -32
- package/generator/test/azure/mock_api_tests/test_azure_core_basic.py +0 -70
- package/generator/test/azure/mock_api_tests/test_azure_core_lro_rpc.py +0 -20
- package/generator/test/azure/mock_api_tests/test_azure_core_lro_standard.py +0 -32
- package/generator/test/azure/mock_api_tests/test_azure_core_model.py +0 -30
- package/generator/test/azure/mock_api_tests/test_azure_core_page.py +0 -71
- package/generator/test/azure/mock_api_tests/test_azure_core_scalar.py +0 -35
- package/generator/test/azure/mock_api_tests/test_azure_core_traits.py +0 -85
- package/generator/test/azure/mock_api_tests/test_azure_encode_duration.py +0 -18
- package/generator/test/azure/mock_api_tests/test_azure_example_basic.py +0 -29
- package/generator/test/azure/mock_api_tests/test_azure_payload_pageable.py +0 -18
- package/generator/test/azure/mock_api_tests/test_azure_resource_manager_method_subscription_id.py +0 -235
- package/generator/test/azure/mock_api_tests/test_azure_resource_manager_multi_service.py +0 -104
- package/generator/test/azure/mock_api_tests/test_azure_resource_manager_multi_service_shared_models.py +0 -128
- package/generator/test/azure/mock_api_tests/test_azure_special_headers_client_request_id.py +0 -29
- package/generator/test/azure/mock_api_tests/test_azure_versioning_previewversion.py +0 -50
- package/generator/test/azure/mock_api_tests/test_clear_output_folder.py +0 -23
- package/generator/test/azure/mock_api_tests/test_client_namespace.py +0 -32
- package/generator/test/azure/mock_api_tests/test_client_naming.py +0 -57
- package/generator/test/azure/mock_api_tests/test_client_naming_enum_conflict.py +0 -35
- package/generator/test/azure/mock_api_tests/test_client_overload.py +0 -25
- package/generator/test/azure/mock_api_tests/test_client_structure.py +0 -60
- package/generator/test/azure/mock_api_tests/test_client_structure_clientoperationgroup.py +0 -29
- package/generator/test/azure/mock_api_tests/test_encode_duration.py +0 -60
- package/generator/test/azure/mock_api_tests/test_encode_numeric.py +0 -31
- package/generator/test/azure/mock_api_tests/test_model_base_flatten_compatibility.py +0 -251
- package/generator/test/azure/mock_api_tests/test_parameters_basic.py +0 -22
- package/generator/test/azure/mock_api_tests/test_parameters_spread.py +0 -66
- package/generator/test/azure/mock_api_tests/test_payload_content_negotiation.py +0 -33
- package/generator/test/azure/mock_api_tests/test_payload_multipart.py +0 -198
- package/generator/test/azure/mock_api_tests/test_resiliency_srv_driven.py +0 -124
- package/generator/test/azure/mock_api_tests/test_serialization_encoded_name_json.py +0 -21
- package/generator/test/azure/mock_api_tests/test_service_multi_service.py +0 -29
- package/generator/test/azure/mock_api_tests/test_special_words.py +0 -63
- package/generator/test/azure/requirements.txt +0 -125
- package/generator/test/azure/tox.ini +0 -64
- package/generator/test/data/image.jpg +0 -0
- package/generator/test/data/image.png +0 -0
- package/generator/test/dev_requirements.txt +0 -8
- package/generator/test/generic_mock_api_tests/asynctests/test_authentication_async.py +0 -140
- package/generator/test/generic_mock_api_tests/asynctests/test_encode_array_async.py +0 -139
- package/generator/test/generic_mock_api_tests/asynctests/test_encode_bytes_async.py +0 -132
- package/generator/test/generic_mock_api_tests/asynctests/test_encode_datetime_async.py +0 -127
- package/generator/test/generic_mock_api_tests/asynctests/test_headasboolean_async.py +0 -35
- package/generator/test/generic_mock_api_tests/asynctests/test_parameters_body_optionality_async.py +0 -30
- package/generator/test/generic_mock_api_tests/asynctests/test_parameters_collection_format_async.py +0 -38
- package/generator/test/generic_mock_api_tests/asynctests/test_parameters_path_async.py +0 -24
- package/generator/test/generic_mock_api_tests/asynctests/test_parameters_query_async.py +0 -18
- package/generator/test/generic_mock_api_tests/asynctests/test_payload_json_merge_patch_async.py +0 -98
- package/generator/test/generic_mock_api_tests/asynctests/test_payload_media_type_async.py +0 -27
- package/generator/test/generic_mock_api_tests/asynctests/test_payload_pageable_async.py +0 -134
- package/generator/test/generic_mock_api_tests/asynctests/test_payload_xml_async.py +0 -150
- package/generator/test/generic_mock_api_tests/asynctests/test_response_status_code_range_async.py +0 -38
- package/generator/test/generic_mock_api_tests/asynctests/test_routes_async.py +0 -331
- package/generator/test/generic_mock_api_tests/asynctests/test_server_endpoint_not_defined_async.py +0 -18
- package/generator/test/generic_mock_api_tests/asynctests/test_server_path_multiple_async.py +0 -25
- package/generator/test/generic_mock_api_tests/asynctests/test_server_path_single_async.py +0 -18
- package/generator/test/generic_mock_api_tests/asynctests/test_server_versions_not_versioned_async.py +0 -28
- package/generator/test/generic_mock_api_tests/asynctests/test_server_versions_versioned_async.py +0 -34
- package/generator/test/generic_mock_api_tests/asynctests/test_special_headers_conditional_request_async.py +0 -38
- package/generator/test/generic_mock_api_tests/asynctests/test_special_headers_repeatability_async.py +0 -19
- package/generator/test/generic_mock_api_tests/asynctests/test_specs_documentation_async.py +0 -59
- package/generator/test/generic_mock_api_tests/asynctests/test_streaming_jsonl_async.py +0 -27
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_array_async.py +0 -127
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_dictionary_async.py +0 -98
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_enum_extensible_async.py +0 -25
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_enum_fixed_async.py +0 -27
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_file_async.py +0 -59
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_empty_async.py +0 -32
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_enum_discriminator_async.py +0 -70
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_nested_discriminator_async.py +0 -85
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_not_discriminated_async.py +0 -34
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_recursive_async.py +0 -34
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_inheritance_single_discriminator_async.py +0 -67
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_usage_async.py +0 -32
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_model_visibility_async.py +0 -47
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_additionalproperties_async.py +0 -353
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_nullable_async.py +0 -110
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_optional_async.py +0 -197
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_valuetypes_async.py +0 -315
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_scalar_async.py +0 -60
- package/generator/test/generic_mock_api_tests/asynctests/test_typetest_union_async.py +0 -90
- package/generator/test/generic_mock_api_tests/asynctests/test_versioning_added_async.py +0 -36
- package/generator/test/generic_mock_api_tests/asynctests/test_versioning_made_optional_async.py +0 -21
- package/generator/test/generic_mock_api_tests/asynctests/test_versioning_removed_async.py +0 -39
- package/generator/test/generic_mock_api_tests/asynctests/test_versioning_renamed_from_async.py +0 -29
- package/generator/test/generic_mock_api_tests/asynctests/test_versioning_return_type_changed_from_async.py +0 -18
- package/generator/test/generic_mock_api_tests/asynctests/test_versioning_type_changed_from_async.py +0 -22
- package/generator/test/generic_mock_api_tests/conftest.py +0 -75
- package/generator/test/generic_mock_api_tests/data/image.jpg +0 -0
- package/generator/test/generic_mock_api_tests/data/image.png +0 -0
- package/generator/test/generic_mock_api_tests/test_authentication.py +0 -130
- package/generator/test/generic_mock_api_tests/test_encode_array.py +0 -126
- package/generator/test/generic_mock_api_tests/test_encode_bytes.py +0 -128
- package/generator/test/generic_mock_api_tests/test_encode_datetime.py +0 -123
- package/generator/test/generic_mock_api_tests/test_headasboolean.py +0 -33
- package/generator/test/generic_mock_api_tests/test_parameters_body_optionality.py +0 -27
- package/generator/test/generic_mock_api_tests/test_parameters_collection_format.py +0 -33
- package/generator/test/generic_mock_api_tests/test_parameters_path.py +0 -22
- package/generator/test/generic_mock_api_tests/test_parameters_query.py +0 -17
- package/generator/test/generic_mock_api_tests/test_payload_json_merge_patch.py +0 -93
- package/generator/test/generic_mock_api_tests/test_payload_media_type.py +0 -25
- package/generator/test/generic_mock_api_tests/test_payload_pageable.py +0 -99
- package/generator/test/generic_mock_api_tests/test_payload_xml.py +0 -135
- package/generator/test/generic_mock_api_tests/test_response_status_code_range.py +0 -36
- package/generator/test/generic_mock_api_tests/test_routes.py +0 -285
- package/generator/test/generic_mock_api_tests/test_server_endpoint_not_defined.py +0 -17
- package/generator/test/generic_mock_api_tests/test_server_path_multiple.py +0 -21
- package/generator/test/generic_mock_api_tests/test_server_path_single.py +0 -17
- package/generator/test/generic_mock_api_tests/test_server_versions_not_versioned.py +0 -25
- package/generator/test/generic_mock_api_tests/test_server_versions_versioned.py +0 -30
- package/generator/test/generic_mock_api_tests/test_special_headers_conditional_request.py +0 -34
- package/generator/test/generic_mock_api_tests/test_special_headers_repeatability.py +0 -18
- package/generator/test/generic_mock_api_tests/test_specs_documentation.py +0 -51
- package/generator/test/generic_mock_api_tests/test_streaming_jsonl.py +0 -25
- package/generator/test/generic_mock_api_tests/test_typetest_array.py +0 -111
- package/generator/test/generic_mock_api_tests/test_typetest_dictionary.py +0 -86
- package/generator/test/generic_mock_api_tests/test_typetest_enum_extensible.py +0 -23
- package/generator/test/generic_mock_api_tests/test_typetest_enum_fixed.py +0 -25
- package/generator/test/generic_mock_api_tests/test_typetest_file.py +0 -52
- package/generator/test/generic_mock_api_tests/test_typetest_model_empty.py +0 -29
- package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_enum_discriminator.py +0 -58
- package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_nested_discriminator.py +0 -79
- package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_not_discriminated.py +0 -31
- package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_recursive.py +0 -32
- package/generator/test/generic_mock_api_tests/test_typetest_model_inheritance_single_discriminator.py +0 -60
- package/generator/test/generic_mock_api_tests/test_typetest_model_usage.py +0 -28
- package/generator/test/generic_mock_api_tests/test_typetest_model_visibility.py +0 -40
- package/generator/test/generic_mock_api_tests/test_typetest_property_additionalproperties.py +0 -314
- package/generator/test/generic_mock_api_tests/test_typetest_property_nullable.py +0 -102
- package/generator/test/generic_mock_api_tests/test_typetest_property_optional.py +0 -174
- package/generator/test/generic_mock_api_tests/test_typetest_property_valuetypes.py +0 -286
- package/generator/test/generic_mock_api_tests/test_typetest_scalar.py +0 -53
- package/generator/test/generic_mock_api_tests/test_typetest_union.py +0 -80
- package/generator/test/generic_mock_api_tests/test_typetest_union_discriminated.py +0 -290
- package/generator/test/generic_mock_api_tests/test_versioning_added.py +0 -33
- package/generator/test/generic_mock_api_tests/test_versioning_made_optional.py +0 -20
- package/generator/test/generic_mock_api_tests/test_versioning_removed.py +0 -37
- package/generator/test/generic_mock_api_tests/test_versioning_renamed_from.py +0 -27
- package/generator/test/generic_mock_api_tests/test_versioning_return_type_changed_from.py +0 -17
- package/generator/test/generic_mock_api_tests/test_versioning_type_changed_from.py +0 -21
- package/generator/test/unbranded/mock_api_tests/asynctests/test_auth_flow_async.py +0 -19
- package/generator/test/unbranded/mock_api_tests/asynctests/test_encode_duration_async.py +0 -63
- package/generator/test/unbranded/mock_api_tests/asynctests/test_encode_numeric_async.py +0 -35
- package/generator/test/unbranded/mock_api_tests/asynctests/test_parameters_basic_async.py +0 -24
- package/generator/test/unbranded/mock_api_tests/asynctests/test_parameters_spread_async.py +0 -76
- package/generator/test/unbranded/mock_api_tests/asynctests/test_payload_content_negotiation_async.py +0 -37
- package/generator/test/unbranded/mock_api_tests/asynctests/test_payload_multipart_async.py +0 -218
- package/generator/test/unbranded/mock_api_tests/asynctests/test_serialization_encoded_name_json_async.py +0 -24
- package/generator/test/unbranded/mock_api_tests/asynctests/test_special_words_async.py +0 -73
- package/generator/test/unbranded/mock_api_tests/asynctests/test_unbranded_async.py +0 -24
- package/generator/test/unbranded/mock_api_tests/conftest.py +0 -91
- package/generator/test/unbranded/mock_api_tests/data/image.jpg +0 -0
- package/generator/test/unbranded/mock_api_tests/data/image.png +0 -0
- package/generator/test/unbranded/mock_api_tests/test_auth_flow.py +0 -17
- package/generator/test/unbranded/mock_api_tests/test_encode_duration.py +0 -60
- package/generator/test/unbranded/mock_api_tests/test_encode_numeric.py +0 -32
- package/generator/test/unbranded/mock_api_tests/test_parameters_basic.py +0 -22
- package/generator/test/unbranded/mock_api_tests/test_parameters_spread.py +0 -66
- package/generator/test/unbranded/mock_api_tests/test_payload_content_negotiation.py +0 -33
- package/generator/test/unbranded/mock_api_tests/test_payload_multipart.py +0 -200
- package/generator/test/unbranded/mock_api_tests/test_serialization_encoded_name_json.py +0 -22
- package/generator/test/unbranded/mock_api_tests/test_special_words.py +0 -66
- package/generator/test/unbranded/mock_api_tests/test_unbranded.py +0 -65
- package/generator/test/unbranded/requirements.txt +0 -69
- package/generator/test/unbranded/tox.ini +0 -64
- package/generator/test/unittests/conftest.py +0 -15
- package/generator/test/unittests/requirements.txt +0 -3
- package/generator/test/unittests/test_enums.py +0 -89
- package/generator/test/unittests/test_model_base_serialization.py +0 -4906
- package/generator/test/unittests/test_model_base_xml_serialization.py +0 -1239
- package/generator/test/unittests/test_name_converter.py +0 -35
- package/generator/test/unittests/test_optional_return_type.py +0 -268
- package/generator/test/unittests/test_parameter_ordering.py +0 -104
- package/generator/test/unittests/test_sort_schema.py +0 -93
- package/generator/test/unittests/tox.ini +0 -5
- /package/eng/scripts/ci/{mypy.ini → config/mypy.ini} +0 -0
- /package/eng/scripts/ci/{pylintrc → config/pylintrc} +0 -0
- /package/eng/scripts/ci/{pyproject.toml → config/pyproject.toml} +0 -0
- /package/eng/scripts/ci/{pyrightconfig.json → config/pyrightconfig.json} +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
|