From 328aae25fcf9fc53281a0f8e0788da6de89f9271 Mon Sep 17 00:00:00 2001 From: Yuxuan Chen Date: Mon, 8 Jun 2026 18:24:51 -0400 Subject: [PATCH 1/3] fix(smithy-aws-core): omit Result wrapper for empty awsQuery output --- .../src/smithy_aws_core/aio/protocols.py | 10 +++-- .../tests/unit/aio/test_protocols.py | 42 +++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/packages/smithy-aws-core/src/smithy_aws_core/aio/protocols.py b/packages/smithy-aws-core/src/smithy_aws_core/aio/protocols.py index 4e7dfe25f..afaac61eb 100644 --- a/packages/smithy-aws-core/src/smithy_aws_core/aio/protocols.py +++ b/packages/smithy-aws-core/src/smithy_aws_core/aio/protocols.py @@ -364,10 +364,14 @@ def _action_name( def _response_wrapper_elements( self, operation: APIOperation[SerializeableShape, DeserializeableShape], - ) -> tuple[str, str]: + ) -> tuple[str, ...]: + name = operation.schema.id.name + # Operations with no output members will omit the element. + if not operation.output_schema.members: + return (f"{name}Response",) return ( - f"{operation.schema.id.name}Response", - f"{operation.schema.id.name}Result", + f"{name}Response", + f"{name}Result", ) def _error_wrapper_elements(self) -> tuple[str, ...]: diff --git a/packages/smithy-aws-core/tests/unit/aio/test_protocols.py b/packages/smithy-aws-core/tests/unit/aio/test_protocols.py index 689095823..f90411b14 100644 --- a/packages/smithy-aws-core/tests/unit/aio/test_protocols.py +++ b/packages/smithy-aws-core/tests/unit/aio/test_protocols.py @@ -131,6 +131,9 @@ def test_aws_json_document_discriminator( ], members={"message": {"target": STRING}}, ) +_EMPTY_OUTPUT_SCHEMA = Schema.collection( + id=ShapeID("com.test#EmptyOutput"), +) @dataclass @@ -160,6 +163,16 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: return cls(**kwargs) +class _EmptyOutput: + @classmethod + def deserialize(cls, deserializer: ShapeDeserializer) -> "_EmptyOutput": + def _consumer(schema: Schema, de: ShapeDeserializer) -> None: + pass + + deserializer.read_struct(_EMPTY_OUTPUT_SCHEMA, consumer=_consumer) + return cls() + + def _operation_schema(name: str) -> Schema: return Schema( id=ShapeID(f"com.test#{name}"), @@ -171,10 +184,14 @@ def _mock_operation( schema: Schema, *, error_schemas: list[Schema] | None = None, + output: Any = None, + output_schema: Schema | None = None, ) -> APIOperation[Any, Any]: operation = Mock(spec=APIOperation) operation.schema = schema operation.error_schemas = error_schemas or [] + operation.output = output + operation.output_schema = output_schema return cast("APIOperation[Any, Any]", operation) @@ -272,3 +289,28 @@ async def test_aws_query_returns_generic_error_for_unknown_code() -> None: "Unknown error for operation com.test#FailingOperation" " - status: 500, code: UnknownThing" ) + + +async def test_aws_query_deserializes_empty_output_without_result_wrapper() -> None: + protocol = AwsQueryClientProtocol(_SERVICE_SCHEMA, "2020-01-08") + result = await protocol.deserialize_response( + operation=_mock_operation( + _operation_schema("EmptyOperation"), + output=_EmptyOutput, + output_schema=_EMPTY_OUTPUT_SCHEMA, + ), + request=cast(HTTPRequest, Mock()), + response=HTTPResponse( + status=200, + fields=tuples_to_fields([]), + body=( + b"" + b"abc-123" + b"" + ), + ), + error_registry=TypeRegistry({}), + context=TypedProperties(), + ) + + assert isinstance(result, _EmptyOutput) From 06cb461016cac9cb75ef8c5b317ed8d9b66203b9 Mon Sep 17 00:00:00 2001 From: Yuxuan Chen Date: Mon, 8 Jun 2026 18:59:46 -0400 Subject: [PATCH 2/3] Add changelog entry --- ...ithy-aws-core-bugfix-05435b3fb9a54b86b4ebb4d61b0d13ae.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 packages/smithy-aws-core/.changes/next-release/smithy-aws-core-bugfix-05435b3fb9a54b86b4ebb4d61b0d13ae.json diff --git a/packages/smithy-aws-core/.changes/next-release/smithy-aws-core-bugfix-05435b3fb9a54b86b4ebb4d61b0d13ae.json b/packages/smithy-aws-core/.changes/next-release/smithy-aws-core-bugfix-05435b3fb9a54b86b4ebb4d61b0d13ae.json new file mode 100644 index 000000000..a429fdad3 --- /dev/null +++ b/packages/smithy-aws-core/.changes/next-release/smithy-aws-core-bugfix-05435b3fb9a54b86b4ebb4d61b0d13ae.json @@ -0,0 +1,4 @@ +{ + "type": "bugfix", + "description": "Fixed awsQuery response deserialization for operations with no output members." +} From 9adab01fa1d6c7f2ce8e947ec6ed702a683e50ec Mon Sep 17 00:00:00 2001 From: Yuxuan Chen Date: Wed, 10 Jun 2026 15:28:14 -0400 Subject: [PATCH 3/3] Remove unit test of the fix --- .../tests/unit/aio/test_protocols.py | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/packages/smithy-aws-core/tests/unit/aio/test_protocols.py b/packages/smithy-aws-core/tests/unit/aio/test_protocols.py index f90411b14..689095823 100644 --- a/packages/smithy-aws-core/tests/unit/aio/test_protocols.py +++ b/packages/smithy-aws-core/tests/unit/aio/test_protocols.py @@ -131,9 +131,6 @@ def test_aws_json_document_discriminator( ], members={"message": {"target": STRING}}, ) -_EMPTY_OUTPUT_SCHEMA = Schema.collection( - id=ShapeID("com.test#EmptyOutput"), -) @dataclass @@ -163,16 +160,6 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: return cls(**kwargs) -class _EmptyOutput: - @classmethod - def deserialize(cls, deserializer: ShapeDeserializer) -> "_EmptyOutput": - def _consumer(schema: Schema, de: ShapeDeserializer) -> None: - pass - - deserializer.read_struct(_EMPTY_OUTPUT_SCHEMA, consumer=_consumer) - return cls() - - def _operation_schema(name: str) -> Schema: return Schema( id=ShapeID(f"com.test#{name}"), @@ -184,14 +171,10 @@ def _mock_operation( schema: Schema, *, error_schemas: list[Schema] | None = None, - output: Any = None, - output_schema: Schema | None = None, ) -> APIOperation[Any, Any]: operation = Mock(spec=APIOperation) operation.schema = schema operation.error_schemas = error_schemas or [] - operation.output = output - operation.output_schema = output_schema return cast("APIOperation[Any, Any]", operation) @@ -289,28 +272,3 @@ async def test_aws_query_returns_generic_error_for_unknown_code() -> None: "Unknown error for operation com.test#FailingOperation" " - status: 500, code: UnknownThing" ) - - -async def test_aws_query_deserializes_empty_output_without_result_wrapper() -> None: - protocol = AwsQueryClientProtocol(_SERVICE_SCHEMA, "2020-01-08") - result = await protocol.deserialize_response( - operation=_mock_operation( - _operation_schema("EmptyOperation"), - output=_EmptyOutput, - output_schema=_EMPTY_OUTPUT_SCHEMA, - ), - request=cast(HTTPRequest, Mock()), - response=HTTPResponse( - status=200, - fields=tuples_to_fields([]), - body=( - b"" - b"abc-123" - b"" - ), - ), - error_registry=TypeRegistry({}), - context=TypedProperties(), - ) - - assert isinstance(result, _EmptyOutput)