boto3-assist 0.32.0__tar.gz → 0.34.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.
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/PKG-INFO +1 -1
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/pyproject.toml +1 -1
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/connection.py +4 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/dynamodb_model_base.py +220 -1
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/s3/s3_connection.py +10 -0
- boto3_assist-0.34.0/src/boto3_assist/version.py +1 -0
- boto3_assist-0.34.0/tests/unit/dynamodb_tests/dynamodb_model_merge_test.py +427 -0
- boto3_assist-0.32.0/src/boto3_assist/version.py +0 -1
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/.env.docker +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/.env.docker.001 +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/.env.docker.nosql.workbench +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/.env.unittest +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/.gitignore +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/.vscode/launch.json +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/.vscode/settings.json +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/.vscode/tasks.json +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/.windsurf/rules/cascade.yaml +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/LICENSE-EXPLAINED.txt +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/LICENSE.txt +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/README.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/aws_regions_with_status.csv +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/aws_regions_with_status.json +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/devops/build.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/devops/readme.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/design-patterns.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/help/dynamodb/001-guide-single-table-design.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/help/dynamodb/002-guide-defining-models.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/help/dynamodb/003-guide-service-layers.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/help/dynamodb/004-guide-testing-with-moto.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/help/dynamodb/005-guide-projections-and-reserved-keywords.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/help/dynamodb/006-guide-how-dynamodb-stores-data.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/help/dynamodb/007-guide-batch-operations.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/help/dynamodb/008-guide-transactions.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/help/dynamodb/009-guide-conditional-writes.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/help/dynamodb/010-guide-update-expressions.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/issues/BOTO3_ASSIST_BEFORE_AFTER.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/issues/BOTO3_ASSIST_DECIMAL_PATTERN.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/issues/BOTO3_ASSIST_IMPLEMENTATION_CHECKLIST.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/overview.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/roadmap.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/tech-debt.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/docs/unit-test-patterns.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/__init__.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/cloudwatch/log_report.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/QUICK_REFERENCE_KEY_DEBUGGING.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/RUNTIME_KEY_DEBUGGING_SUMMARY.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/batch_operations_example.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/conditional_writes_example.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/debug_keys_example.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/decimal_conversion_example.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/models/order_item_model.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/models/order_model.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/models/product_model.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/models/user_model.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/models/user_post_model.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/order_example/main.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/order_example/products.json +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/runtime_key_debugging_example.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/services/order_item_service.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/services/order_service.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/services/product_service.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/services/table_service.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/services/user_post_service.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/services/user_service.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/services/user_service_client_example.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/services/user_service_resource_example.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/transactions_example.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/update_expressions_example.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/dynamodb/user_post_example/main.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/examples/ec2/regions_report.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/module-headers.txt +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/mypy.ini +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/publish_to_pypi.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/publish_to_pypi.sh +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/pysetup.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/pysetup.sh +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/requirements.dev.txt +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/requirements.txt +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/run-checks.sh +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/run-unit-tests.sh +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/__init__.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/aws_config.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/aws_lambda/event_info.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/aws_lambda/mock_context.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/boto3session.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/cloudwatch/cloudwatch_connection.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/cloudwatch/cloudwatch_connection_tracker.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/cloudwatch/cloudwatch_log_connection.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/cloudwatch/cloudwatch_logs.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/cloudwatch/cloudwatch_query.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/cognito/cognito_authorizer.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/cognito/cognito_connection.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/cognito/cognito_utility.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/cognito/jwks_cache.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/cognito/user.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/connection_tracker.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/dynamodb.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/dynamodb_connection.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/dynamodb_helpers.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/dynamodb_importer.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/dynamodb_index.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/dynamodb_iservice.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/dynamodb_key.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/dynamodb_model_base_interfaces.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/dynamodb_re_indexer.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/dynamodb_reindexer.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/dynamodb_reserved_words.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/dynamodb_reserved_words.txt +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/readme.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/troubleshooting.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/ec2/ec2_connection.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/environment_services/__init__.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/environment_services/environment_loader.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/environment_services/environment_variables.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/erc/__init__.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/erc/ecr_connection.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/errors/custom_exceptions.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/http_status_codes.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/models/serializable_model.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/role_assumption_mixin.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/s3/s3.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/s3/s3_bucket.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/s3/s3_event_data.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/s3/s3_object.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/securityhub/securityhub.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/securityhub/securityhub_connection.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/session_setup_mixin.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/ssm/connection.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/ssm/parameter_store/parameter_store.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/utilities/datetime_utility.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/utilities/decimal_conversion_utility.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/utilities/dictionary_utility.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/utilities/file_operations.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/utilities/http_utility.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/utilities/logging_utility.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/utilities/numbers_utility.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/utilities/serialization_utility.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/utilities/string_utility.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/__init__.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/integration/cross_account_connection_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/integration/tenant.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/integration/tenant_services.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/aws_config_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/common/db_test_helpers.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb/decimal_backward_compatibility_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb/decimal_conversion_integration_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb/test_dynamodb_key_to_dict.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/__init__.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/db_models/cms/base.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/db_models/cms/content_block.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/db_models/cms/page.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/db_models/cms/template.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/db_models/simple_model.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/db_models/task.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/db_models/user_model.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/db_models/user_required_fields_model.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/dynamodb_batch_operations_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/dynamodb_conditional_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/dynamodb_fail_if_exists_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/dynamodb_model_base_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/dynamodb_model_projections_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/dynamodb_model_serializtion_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/dynamodb_moto_sorting_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/dynamodb_primary_key_get_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/dynamodb_primary_key_sort_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/dynamodb_query_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/dynamodb_reindex_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/dynamodb_transactions_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/dynamodb_tests/dynamodb_update_expressions_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/examples_test/README.md +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/examples_test/__init__.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/examples_test/order_service_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/examples_test/user_service_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/lambda_tests/__init__.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/lambda_tests/event_info_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/models_tests/__init__.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/models_tests/models/person.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/models_tests/models/user.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/models_tests/serializable_model_person_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/models_tests/serializable_model_user_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/models_tests/serializable_model_wide_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/parameter_store/__init__.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/parameter_store/parameter_store_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/s3/__init__.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/s3/files/test.txt +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/s3/s3_event_data_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/s3/s3_file_delete_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/s3/s3_file_upload_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/session_tests/test_boto3_session_manager.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/utilities/__init__.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/utilities/decimal_conversion_utility_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/utilities/serialization_utility_test.py +0 -0
- {boto3_assist-0.32.0 → boto3_assist-0.34.0}/tests/unit/utilities/string_utility_test.py +0 -0
|
@@ -7,6 +7,7 @@ MIT License. See Project Root for the license information.
|
|
|
7
7
|
from typing import Optional, List
|
|
8
8
|
|
|
9
9
|
from aws_lambda_powertools import Logger
|
|
10
|
+
from botocore.config import Config
|
|
10
11
|
from boto3_assist.boto3session import Boto3SessionManager
|
|
11
12
|
from boto3_assist.environment_services.environment_variables import (
|
|
12
13
|
EnvironmentVariables,
|
|
@@ -33,6 +34,7 @@ class Connection:
|
|
|
33
34
|
assume_role_arn: Optional[str] = None,
|
|
34
35
|
assume_role_chain: Optional[List[str]] = None,
|
|
35
36
|
assume_role_duration_seconds: Optional[int] = 3600,
|
|
37
|
+
config: Optional[Config] = None,
|
|
36
38
|
) -> None:
|
|
37
39
|
self.__aws_profile = aws_profile
|
|
38
40
|
self.__aws_region = aws_region
|
|
@@ -44,6 +46,7 @@ class Connection:
|
|
|
44
46
|
self.__service_name: str | None = service_name
|
|
45
47
|
self.__assume_role_chain = assume_role_chain
|
|
46
48
|
self.__assume_role_duration_seconds = assume_role_duration_seconds
|
|
49
|
+
self.__config = config
|
|
47
50
|
if self.__service_name is None:
|
|
48
51
|
raise RuntimeError(
|
|
49
52
|
"Service Name is not available. The service name is required."
|
|
@@ -80,6 +83,7 @@ class Connection:
|
|
|
80
83
|
assume_role_arn=self.__assume_role_arn,
|
|
81
84
|
assume_role_chain=self.__assume_role_chain,
|
|
82
85
|
assume_role_duration_seconds=self.__assume_role_duration_seconds,
|
|
86
|
+
config=self.__config,
|
|
83
87
|
)
|
|
84
88
|
|
|
85
89
|
tracker.add(service_name=self.service_name)
|
{boto3_assist-0.32.0 → boto3_assist-0.34.0}/src/boto3_assist/dynamodb/dynamodb_model_base.py
RENAMED
|
@@ -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.
|
|
@@ -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
|
|
@@ -4,10 +4,12 @@ Maintainers: Eric Wilson
|
|
|
4
4
|
MIT License. See Project Root for the license information.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import os
|
|
7
8
|
from typing import Optional
|
|
8
9
|
from typing import TYPE_CHECKING
|
|
9
10
|
|
|
10
11
|
from aws_lambda_powertools import Logger
|
|
12
|
+
from botocore.config import Config
|
|
11
13
|
|
|
12
14
|
from boto3_assist.connection import Connection
|
|
13
15
|
|
|
@@ -32,7 +34,14 @@ class S3Connection(Connection):
|
|
|
32
34
|
aws_end_point_url: Optional[str] = None,
|
|
33
35
|
aws_access_key_id: Optional[str] = None,
|
|
34
36
|
aws_secret_access_key: Optional[str] = None,
|
|
37
|
+
signature_version: Optional[str] = None,
|
|
35
38
|
) -> None:
|
|
39
|
+
# Build S3-specific config if signature_version is specified
|
|
40
|
+
config: Optional[Config] = None
|
|
41
|
+
signature_version = signature_version or os.getenv("AWS_S3_SIGNATURE_VERSION")
|
|
42
|
+
if signature_version:
|
|
43
|
+
config = Config(signature_version=signature_version)
|
|
44
|
+
|
|
36
45
|
super().__init__(
|
|
37
46
|
service_name="s3",
|
|
38
47
|
aws_profile=aws_profile,
|
|
@@ -40,6 +49,7 @@ class S3Connection(Connection):
|
|
|
40
49
|
aws_access_key_id=aws_access_key_id,
|
|
41
50
|
aws_secret_access_key=aws_secret_access_key,
|
|
42
51
|
aws_end_point_url=aws_end_point_url,
|
|
52
|
+
config=config,
|
|
43
53
|
)
|
|
44
54
|
|
|
45
55
|
self.__client: S3Client | None = None
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.34.0"
|