boto3-assist 0.31.0__tar.gz → 0.33.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/PKG-INFO +1 -1
  2. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/pyproject.toml +1 -1
  3. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/run-unit-tests.sh +1 -1
  4. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/dynamodb_model_base.py +221 -2
  5. boto3_assist-0.33.0/src/boto3_assist/version.py +1 -0
  6. boto3_assist-0.33.0/tests/unit/dynamodb_tests/dynamodb_model_merge_test.py +427 -0
  7. boto3_assist-0.31.0/src/boto3_assist/version.py +0 -1
  8. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/.env.docker +0 -0
  9. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/.env.docker.001 +0 -0
  10. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/.env.docker.nosql.workbench +0 -0
  11. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/.env.unittest +0 -0
  12. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/.gitignore +0 -0
  13. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/.vscode/launch.json +0 -0
  14. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/.vscode/settings.json +0 -0
  15. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/.vscode/tasks.json +0 -0
  16. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/.windsurf/rules/cascade.yaml +0 -0
  17. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/LICENSE-EXPLAINED.txt +0 -0
  18. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/LICENSE.txt +0 -0
  19. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/README.md +0 -0
  20. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/aws_regions_with_status.csv +0 -0
  21. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/aws_regions_with_status.json +0 -0
  22. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/devops/build.py +0 -0
  23. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/devops/readme.md +0 -0
  24. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/design-patterns.md +0 -0
  25. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/help/dynamodb/001-guide-single-table-design.md +0 -0
  26. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/help/dynamodb/002-guide-defining-models.md +0 -0
  27. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/help/dynamodb/003-guide-service-layers.md +0 -0
  28. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/help/dynamodb/004-guide-testing-with-moto.md +0 -0
  29. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/help/dynamodb/005-guide-projections-and-reserved-keywords.md +0 -0
  30. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/help/dynamodb/006-guide-how-dynamodb-stores-data.md +0 -0
  31. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/help/dynamodb/007-guide-batch-operations.md +0 -0
  32. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/help/dynamodb/008-guide-transactions.md +0 -0
  33. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/help/dynamodb/009-guide-conditional-writes.md +0 -0
  34. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/help/dynamodb/010-guide-update-expressions.md +0 -0
  35. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/issues/BOTO3_ASSIST_BEFORE_AFTER.md +0 -0
  36. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/issues/BOTO3_ASSIST_DECIMAL_PATTERN.md +0 -0
  37. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/issues/BOTO3_ASSIST_IMPLEMENTATION_CHECKLIST.md +0 -0
  38. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/overview.md +0 -0
  39. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/roadmap.md +0 -0
  40. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/tech-debt.md +0 -0
  41. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/docs/unit-test-patterns.md +0 -0
  42. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/__init__.py +0 -0
  43. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/cloudwatch/log_report.py +0 -0
  44. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/QUICK_REFERENCE_KEY_DEBUGGING.md +0 -0
  45. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/RUNTIME_KEY_DEBUGGING_SUMMARY.md +0 -0
  46. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/batch_operations_example.py +0 -0
  47. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/conditional_writes_example.py +0 -0
  48. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/debug_keys_example.py +0 -0
  49. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/decimal_conversion_example.py +0 -0
  50. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/models/order_item_model.py +0 -0
  51. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/models/order_model.py +0 -0
  52. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/models/product_model.py +0 -0
  53. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/models/user_model.py +0 -0
  54. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/models/user_post_model.py +0 -0
  55. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/order_example/main.py +0 -0
  56. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/order_example/products.json +0 -0
  57. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/runtime_key_debugging_example.py +0 -0
  58. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/services/order_item_service.py +0 -0
  59. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/services/order_service.py +0 -0
  60. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/services/product_service.py +0 -0
  61. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/services/table_service.py +0 -0
  62. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/services/user_post_service.py +0 -0
  63. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/services/user_service.py +0 -0
  64. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/services/user_service_client_example.py +0 -0
  65. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/services/user_service_resource_example.py +0 -0
  66. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/transactions_example.py +0 -0
  67. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/update_expressions_example.py +0 -0
  68. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/dynamodb/user_post_example/main.py +0 -0
  69. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/examples/ec2/regions_report.py +0 -0
  70. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/module-headers.txt +0 -0
  71. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/mypy.ini +0 -0
  72. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/publish_to_pypi.py +0 -0
  73. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/publish_to_pypi.sh +0 -0
  74. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/pysetup.py +0 -0
  75. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/pysetup.sh +0 -0
  76. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/requirements.dev.txt +0 -0
  77. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/requirements.txt +0 -0
  78. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/run-checks.sh +0 -0
  79. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/__init__.py +0 -0
  80. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/aws_config.py +0 -0
  81. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/aws_lambda/event_info.py +0 -0
  82. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/aws_lambda/mock_context.py +0 -0
  83. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/boto3session.py +0 -0
  84. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/cloudwatch/cloudwatch_connection.py +0 -0
  85. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/cloudwatch/cloudwatch_connection_tracker.py +0 -0
  86. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/cloudwatch/cloudwatch_log_connection.py +0 -0
  87. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/cloudwatch/cloudwatch_logs.py +0 -0
  88. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/cloudwatch/cloudwatch_query.py +0 -0
  89. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/cognito/cognito_authorizer.py +0 -0
  90. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/cognito/cognito_connection.py +0 -0
  91. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/cognito/cognito_utility.py +0 -0
  92. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/cognito/jwks_cache.py +0 -0
  93. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/cognito/user.py +0 -0
  94. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/connection.py +0 -0
  95. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/connection_tracker.py +0 -0
  96. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/dynamodb.py +0 -0
  97. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/dynamodb_connection.py +0 -0
  98. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/dynamodb_helpers.py +0 -0
  99. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/dynamodb_importer.py +0 -0
  100. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/dynamodb_index.py +0 -0
  101. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/dynamodb_iservice.py +0 -0
  102. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/dynamodb_key.py +0 -0
  103. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/dynamodb_model_base_interfaces.py +0 -0
  104. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/dynamodb_re_indexer.py +0 -0
  105. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/dynamodb_reindexer.py +0 -0
  106. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/dynamodb_reserved_words.py +0 -0
  107. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/dynamodb_reserved_words.txt +0 -0
  108. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/readme.md +0 -0
  109. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/dynamodb/troubleshooting.md +0 -0
  110. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/ec2/ec2_connection.py +0 -0
  111. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/environment_services/__init__.py +0 -0
  112. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/environment_services/environment_loader.py +0 -0
  113. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/environment_services/environment_variables.py +0 -0
  114. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/erc/__init__.py +0 -0
  115. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/erc/ecr_connection.py +0 -0
  116. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/errors/custom_exceptions.py +0 -0
  117. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/http_status_codes.py +0 -0
  118. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/models/serializable_model.py +0 -0
  119. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/role_assumption_mixin.py +0 -0
  120. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/s3/s3.py +0 -0
  121. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/s3/s3_bucket.py +0 -0
  122. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/s3/s3_connection.py +0 -0
  123. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/s3/s3_event_data.py +0 -0
  124. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/s3/s3_object.py +0 -0
  125. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/securityhub/securityhub.py +0 -0
  126. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/securityhub/securityhub_connection.py +0 -0
  127. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/session_setup_mixin.py +0 -0
  128. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/ssm/connection.py +0 -0
  129. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/ssm/parameter_store/parameter_store.py +0 -0
  130. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/utilities/datetime_utility.py +0 -0
  131. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/utilities/decimal_conversion_utility.py +0 -0
  132. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/utilities/dictionary_utility.py +0 -0
  133. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/utilities/file_operations.py +0 -0
  134. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/utilities/http_utility.py +0 -0
  135. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/utilities/logging_utility.py +0 -0
  136. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/utilities/numbers_utility.py +0 -0
  137. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/utilities/serialization_utility.py +0 -0
  138. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/src/boto3_assist/utilities/string_utility.py +0 -0
  139. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/__init__.py +0 -0
  140. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/integration/cross_account_connection_test.py +0 -0
  141. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/integration/tenant.py +0 -0
  142. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/integration/tenant_services.py +0 -0
  143. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/aws_config_test.py +0 -0
  144. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/common/db_test_helpers.py +0 -0
  145. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb/decimal_backward_compatibility_test.py +0 -0
  146. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb/decimal_conversion_integration_test.py +0 -0
  147. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb/test_dynamodb_key_to_dict.py +0 -0
  148. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/__init__.py +0 -0
  149. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/db_models/cms/base.py +0 -0
  150. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/db_models/cms/content_block.py +0 -0
  151. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/db_models/cms/page.py +0 -0
  152. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/db_models/cms/template.py +0 -0
  153. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/db_models/simple_model.py +0 -0
  154. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/db_models/task.py +0 -0
  155. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/db_models/user_model.py +0 -0
  156. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/db_models/user_required_fields_model.py +0 -0
  157. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/dynamodb_batch_operations_test.py +0 -0
  158. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/dynamodb_conditional_test.py +0 -0
  159. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/dynamodb_fail_if_exists_test.py +0 -0
  160. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/dynamodb_model_base_test.py +0 -0
  161. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/dynamodb_model_projections_test.py +0 -0
  162. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/dynamodb_model_serializtion_test.py +0 -0
  163. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/dynamodb_moto_sorting_test.py +0 -0
  164. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/dynamodb_primary_key_get_test.py +0 -0
  165. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/dynamodb_primary_key_sort_test.py +0 -0
  166. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/dynamodb_query_test.py +0 -0
  167. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/dynamodb_reindex_test.py +0 -0
  168. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/dynamodb_transactions_test.py +0 -0
  169. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/dynamodb_tests/dynamodb_update_expressions_test.py +0 -0
  170. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/examples_test/README.md +0 -0
  171. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/examples_test/__init__.py +0 -0
  172. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/examples_test/order_service_test.py +0 -0
  173. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/examples_test/user_service_test.py +0 -0
  174. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/lambda_tests/__init__.py +0 -0
  175. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/lambda_tests/event_info_test.py +0 -0
  176. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/models_tests/__init__.py +0 -0
  177. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/models_tests/models/person.py +0 -0
  178. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/models_tests/models/user.py +0 -0
  179. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/models_tests/serializable_model_person_test.py +0 -0
  180. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/models_tests/serializable_model_user_test.py +0 -0
  181. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/models_tests/serializable_model_wide_test.py +0 -0
  182. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/parameter_store/__init__.py +0 -0
  183. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/parameter_store/parameter_store_test.py +0 -0
  184. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/s3/__init__.py +0 -0
  185. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/s3/files/test.txt +0 -0
  186. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/s3/s3_event_data_test.py +0 -0
  187. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/s3/s3_file_delete_test.py +0 -0
  188. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/s3/s3_file_upload_test.py +0 -0
  189. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/session_tests/test_boto3_session_manager.py +0 -0
  190. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/utilities/__init__.py +0 -0
  191. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/utilities/decimal_conversion_utility_test.py +0 -0
  192. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/utilities/serialization_utility_test.py +0 -0
  193. {boto3_assist-0.31.0 → boto3_assist-0.33.0}/tests/unit/utilities/string_utility_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boto3_assist
3
- Version: 0.31.0
3
+ Version: 0.33.0
4
4
  Summary: Additional boto3 wrappers to make your life a little easier
5
5
  Author-email: Eric Wilson <boto3-assist@geekcafe.com>
6
6
  License-File: LICENSE-EXPLAINED.txt
@@ -15,7 +15,7 @@ addopts = "-m 'not integration'"
15
15
 
16
16
  [project]
17
17
  name = "boto3_assist"
18
- version = "0.31.0"
18
+ version = "0.33.0"
19
19
 
20
20
  authors = [
21
21
  { name="Eric Wilson", email="boto3-assist@geekcafe.com" }
@@ -8,7 +8,7 @@ which python
8
8
 
9
9
  pip install --upgrade pip
10
10
  pip install -r ./requirements.txt
11
- pip install -r ./requirements-dev.txt
11
+ pip install -r ./requirements.dev.txt
12
12
 
13
13
 
14
14
  echo "running unit test"
@@ -6,11 +6,12 @@ MIT License. See Project Root for the license information.
6
6
 
7
7
  from __future__ import annotations
8
8
  import datetime as dt
9
+ from enum import Enum
9
10
 
10
11
  # import decimal
11
12
  # import inspect
12
13
  # import uuid
13
- from typing import TypeVar, List, Dict, Any
14
+ from typing import TypeVar, List, Dict, Any, Set
14
15
  from boto3.dynamodb.types import TypeSerializer, TypeDeserializer
15
16
  from boto3_assist.utilities.serialization_utility import Serialization
16
17
  from boto3_assist.utilities.decimal_conversion_utility import DecimalConversionUtility
@@ -25,6 +26,37 @@ from boto3_assist.models.serializable_model import SerializableModel
25
26
  from boto3_assist.utilities.string_utility import StringUtility
26
27
 
27
28
 
29
+ class MergeStrategy(Enum):
30
+ """Strategy for merging updates into an existing model."""
31
+
32
+ NON_NULL_WINS = "non_null_wins"
33
+ """Only overwrite if the update value is not None (default, most common)."""
34
+
35
+ UPDATES_WIN = "updates_win"
36
+ """Update values always win, even if None."""
37
+
38
+ EXISTING_WINS = "existing_wins"
39
+ """Only fill in fields that are currently None in the existing model."""
40
+
41
+
42
+ class _ClearFieldSentinel:
43
+ """Sentinel class to explicitly mark a field for clearing to None."""
44
+
45
+ _instance = None
46
+
47
+ def __new__(cls):
48
+ if cls._instance is None:
49
+ cls._instance = super().__new__(cls)
50
+ return cls._instance
51
+
52
+ def __repr__(self) -> str:
53
+ return "CLEAR_FIELD"
54
+
55
+
56
+ # Singleton sentinel value - use this to explicitly clear a field to None
57
+ CLEAR_FIELD = _ClearFieldSentinel()
58
+
59
+
28
60
  def exclude_from_serialization(method):
29
61
  """
30
62
  Decorator to mark methods or properties to be excluded from serialization.
@@ -179,6 +211,80 @@ class DynamoDBModelBase(SerializableModel):
179
211
  # attempt to map it
180
212
  return DynamoDBSerializer.map(source=item, target=self)
181
213
 
214
+ def merge(
215
+ self: T,
216
+ updates: Dict[str, Any] | DynamoDBModelBase | None,
217
+ strategy: MergeStrategy = MergeStrategy.NON_NULL_WINS,
218
+ include_fields: Set[str] | List[str] | None = None,
219
+ exclude_fields: Set[str] | List[str] | None = None,
220
+ ) -> T:
221
+ """
222
+ Merge updates into this instance based on the specified strategy.
223
+
224
+ Unlike map() which overwrites all fields, merge() selectively updates
225
+ fields based on the strategy and handles the common case where you want
226
+ to apply partial updates from an API request.
227
+
228
+ Args:
229
+ updates: The source of updates - can be a dict or another model instance.
230
+ strategy: How to handle the merge:
231
+ - NON_NULL_WINS (default): Only overwrite if update value is not None.
232
+ Use CLEAR_FIELD sentinel to explicitly set a field to None.
233
+ - UPDATES_WIN: Update values always win, even if None.
234
+ - EXISTING_WINS: Only fill in fields that are currently None.
235
+ include_fields: If provided, only these fields will be considered for merge.
236
+ exclude_fields: Fields to exclude from the merge (e.g., 'id', 'created_at').
237
+
238
+ Returns:
239
+ Self with merged updates applied.
240
+
241
+ Example:
242
+ # Load existing from DB
243
+ existing = Product().map(db_response)
244
+
245
+ # Merge partial updates (only non-null fields applied)
246
+ existing.merge({"name": "New Name", "price": None}) # price unchanged
247
+
248
+ # Explicitly clear a field
249
+ from boto3_assist.dynamodb import CLEAR_FIELD
250
+ existing.merge({"description": CLEAR_FIELD}) # description set to None
251
+
252
+ # Fill gaps only (useful for defaults)
253
+ existing.merge(defaults, strategy=MergeStrategy.EXISTING_WINS)
254
+ """
255
+ if updates is None:
256
+ return self
257
+
258
+ # Convert to dict if needed
259
+ updates_dict: Dict[str, Any]
260
+ if isinstance(updates, DynamoDBModelBase):
261
+ updates_dict = updates.to_resource_dictionary(include_indexes=False)
262
+ elif isinstance(updates, dict):
263
+ updates_dict = updates.copy()
264
+ else:
265
+ raise ValueError("Updates must be a dictionary or DynamoDBModelBase")
266
+
267
+ # Convert decimals if present
268
+ updates_dict = DecimalConversionUtility.convert_decimals_to_native_types(
269
+ updates_dict
270
+ )
271
+
272
+ # Apply field filters
273
+ if include_fields is not None:
274
+ include_set = set(include_fields)
275
+ updates_dict = {k: v for k, v in updates_dict.items() if k in include_set}
276
+
277
+ if exclude_fields is not None:
278
+ exclude_set = set(exclude_fields)
279
+ updates_dict = {
280
+ k: v for k, v in updates_dict.items() if k not in exclude_set
281
+ }
282
+
283
+ # Apply merge based on strategy
284
+ return DynamoDBSerializer.merge(
285
+ updates=updates_dict, target=self, strategy=strategy
286
+ )
287
+
182
288
  def to_client_dictionary(self, include_indexes: bool = True):
183
289
  """
184
290
  Convert the instance to a dictionary suitable for DynamoDB client.
@@ -201,7 +307,7 @@ class DynamoDBModelBase(SerializableModel):
201
307
  """
202
308
  Convert the instance to a dictionary suitable for DynamoDB client.
203
309
  """
204
- return self.to_client_dictionary(include_none=include_none)
310
+ return self.to_dictionary(include_none=include_none)
205
311
 
206
312
  def to_dictionary(self, include_none: bool = True):
207
313
  """
@@ -380,3 +486,116 @@ class DynamoDBSerializer:
380
486
  instance_dict[key.sort_key.attribute_name] = key.sort_key.value
381
487
 
382
488
  return instance_dict
489
+
490
+ @staticmethod
491
+ def merge(updates: Dict[str, Any], target: T, strategy: MergeStrategy) -> T:
492
+ """
493
+ Merge updates into the target object based on the specified strategy.
494
+
495
+ Args:
496
+ updates: Dictionary of field updates to apply.
497
+ target: The target object to merge into.
498
+ strategy: The merge strategy to use.
499
+
500
+ Returns:
501
+ The target object with updates merged.
502
+ """
503
+ for key, update_value in updates.items():
504
+ if not Serialization.has_attribute(target, key):
505
+ continue
506
+
507
+ current_value = getattr(target, key, None)
508
+
509
+ # Handle CLEAR_FIELD sentinel - always clears to None
510
+ if isinstance(update_value, _ClearFieldSentinel):
511
+ try:
512
+ setattr(target, key, None)
513
+ except (AttributeError, TypeError):
514
+ pass # Property without setter or type issue
515
+ continue
516
+
517
+ # Apply strategy
518
+ should_update = False
519
+
520
+ if strategy == MergeStrategy.UPDATES_WIN:
521
+ # Updates always win
522
+ should_update = True
523
+
524
+ elif strategy == MergeStrategy.NON_NULL_WINS:
525
+ # Only update if the new value is not None
526
+ should_update = update_value is not None
527
+
528
+ elif strategy == MergeStrategy.EXISTING_WINS:
529
+ # Only update if current value is None
530
+ should_update = current_value is None
531
+
532
+ if should_update:
533
+ try:
534
+ # Handle nested objects/dicts
535
+ if (
536
+ isinstance(current_value, dict)
537
+ and isinstance(update_value, dict)
538
+ and strategy != MergeStrategy.UPDATES_WIN
539
+ ):
540
+ # Recursively merge dicts
541
+ DynamoDBSerializer._merge_dict(
542
+ current_value, update_value, strategy
543
+ )
544
+ elif hasattr(current_value, "__dict__") and isinstance(
545
+ update_value, dict
546
+ ):
547
+ # Nested object - recursively merge
548
+ DynamoDBSerializer.merge(
549
+ updates=update_value,
550
+ target=current_value,
551
+ strategy=strategy,
552
+ )
553
+ else:
554
+ setattr(target, key, update_value)
555
+ except (AttributeError, TypeError):
556
+ pass # Property without setter or type issue
557
+
558
+ return target
559
+
560
+ @staticmethod
561
+ def _merge_dict(
562
+ target_dict: Dict[str, Any],
563
+ updates_dict: Dict[str, Any],
564
+ strategy: MergeStrategy,
565
+ ) -> None:
566
+ """
567
+ Merge updates into a target dictionary based on strategy.
568
+
569
+ Args:
570
+ target_dict: The dictionary to merge into (modified in place).
571
+ updates_dict: The dictionary of updates.
572
+ strategy: The merge strategy to use.
573
+ """
574
+ for key, update_value in updates_dict.items():
575
+ current_value = target_dict.get(key)
576
+
577
+ # Handle CLEAR_FIELD sentinel
578
+ if isinstance(update_value, _ClearFieldSentinel):
579
+ target_dict[key] = None
580
+ continue
581
+
582
+ should_update = False
583
+
584
+ if strategy == MergeStrategy.UPDATES_WIN:
585
+ should_update = True
586
+ elif strategy == MergeStrategy.NON_NULL_WINS:
587
+ should_update = update_value is not None
588
+ elif strategy == MergeStrategy.EXISTING_WINS:
589
+ should_update = current_value is None
590
+
591
+ if should_update:
592
+ if (
593
+ isinstance(current_value, dict)
594
+ and isinstance(update_value, dict)
595
+ and strategy != MergeStrategy.UPDATES_WIN
596
+ ):
597
+ DynamoDBSerializer._merge_dict(
598
+ current_value, update_value, strategy
599
+ )
600
+ else:
601
+ target_dict[key] = update_value
@@ -0,0 +1 @@
1
+ __version__ = "0.33.0"