From 908d8abfa768ba65d4e9260dafbad525944c1fd0 Mon Sep 17 00:00:00 2001 From: JordonPhillips Date: Thu, 5 Jun 2025 12:34:24 +0200 Subject: [PATCH 1/3] Use natural dict ordering for member index This updates schemas to use the natural ordering of a dict to set member index. Recursive shapes are accommodated by using a None value to reserve the member's spot in the dictionary until the value can be actually set. This has two primary effects. Firstly, it allows the member index to be confidently used at runtime to actually retrieve a member. This isn't needed in most cases, but can come in handy occasionally. This also cuts out a huge amount of boilerplate in the generated code. --- .../codegen/generators/SchemaGenerator.java | 7 +- designs/serialization.md | 1 - .../tests/unit/_private/__init__.py | 35 +++--- .../smithy-core/src/smithy_core/schemas.py | 32 +++-- .../smithy-core/tests/unit/test_documents.py | 118 ++++++++++++++---- .../smithy-core/tests/unit/test_schemas.py | 6 +- .../tests/unit/test_type_registry.py | 2 +- .../smithy-http/tests/unit/test_bindings.py | 22 ++-- .../tests/unit/test_serializers.py | 78 +++--------- packages/smithy-json/tests/unit/__init__.py | 82 ++++++++---- 10 files changed, 220 insertions(+), 163 deletions(-) diff --git a/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/SchemaGenerator.java b/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/SchemaGenerator.java index daaccadaa..4f97023ae 100644 --- a/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/SchemaGenerator.java +++ b/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/SchemaGenerator.java @@ -162,6 +162,11 @@ private void writeSchemaMembers(PythonWriter writer, Shape shape) { for (MemberShape member : shape.members()) { if (!generatedShapes.contains(member.getTarget()) && !Prelude.isPreludeShape(member.getTarget())) { deferredMembers.put(member, index++); + writer.write(""" + # This needs to reference a schema that isn't defined yet. + # It will be populated with a non-null value at the end of the file. + $S: None, + """, member.getMemberName()); continue; } @@ -183,7 +188,6 @@ private void writeSchemaMembers(PythonWriter writer, Shape shape) { writer.write(""" $S: { "target": $T, - "index": $L, ${?hasTraits} "traits": [ ${C|} @@ -193,7 +197,6 @@ private void writeSchemaMembers(PythonWriter writer, Shape shape) { """, member.getMemberName(), targetSchemaSymbol, - index, writer.consumer(w -> writeTraits(w, traits))); index++; diff --git a/designs/serialization.md b/designs/serialization.md index e970260f7..10858849b 100644 --- a/designs/serialization.md +++ b/designs/serialization.md @@ -121,7 +121,6 @@ EXAMPLE_STRUCTURE_SCHEMA = Schema.collection( members={ "member": { "target": INTEGER, - "index": 0, "traits": [ DefaultTrait(0), ], diff --git a/packages/smithy-aws-event-stream/tests/unit/_private/__init__.py b/packages/smithy-aws-event-stream/tests/unit/_private/__init__.py index 0ed16259b..09162ecce 100644 --- a/packages/smithy-aws-event-stream/tests/unit/_private/__init__.py +++ b/packages/smithy-aws-event-stream/tests/unit/_private/__init__.py @@ -38,19 +38,18 @@ SCHEMA_MESSAGE_EVENT = Schema.collection( id=ShapeID("smithy.example#MessageEvent"), members={ - "boolHeader": {"index": 0, "target": BOOLEAN, "traits": [EVENT_HEADER_TRAIT]}, - "byteHeader": {"index": 1, "target": BYTE, "traits": [EVENT_HEADER_TRAIT]}, - "shortHeader": {"index": 2, "target": SHORT, "traits": [EVENT_HEADER_TRAIT]}, - "intHeader": {"index": 3, "target": INTEGER, "traits": [EVENT_HEADER_TRAIT]}, - "longHeader": {"index": 4, "target": LONG, "traits": [EVENT_HEADER_TRAIT]}, - "blobHeader": {"index": 5, "target": BLOB, "traits": [EVENT_HEADER_TRAIT]}, - "stringHeader": {"index": 6, "target": STRING, "traits": [EVENT_HEADER_TRAIT]}, + "boolHeader": {"target": BOOLEAN, "traits": [EVENT_HEADER_TRAIT]}, + "byteHeader": {"target": BYTE, "traits": [EVENT_HEADER_TRAIT]}, + "shortHeader": {"target": SHORT, "traits": [EVENT_HEADER_TRAIT]}, + "intHeader": {"target": INTEGER, "traits": [EVENT_HEADER_TRAIT]}, + "longHeader": {"target": LONG, "traits": [EVENT_HEADER_TRAIT]}, + "blobHeader": {"target": BLOB, "traits": [EVENT_HEADER_TRAIT]}, + "stringHeader": {"target": STRING, "traits": [EVENT_HEADER_TRAIT]}, "timestampHeader": { - "index": 7, "target": TIMESTAMP, "traits": [EVENT_HEADER_TRAIT], }, - "bodyMember": {"index": 8, "target": STRING}, + "bodyMember": {"target": STRING}, }, ) @@ -58,12 +57,10 @@ id=ShapeID("smithy.example#PayloadEvent"), members={ "header": { - "index": 0, "target": STRING, "traits": [EVENT_HEADER_TRAIT, REQUIRED_TRAIT], }, "payload": { - "index": 1, "target": STRING, "traits": [EVENT_PAYLOAD_TRAIT, REQUIRED_TRAIT], }, @@ -74,12 +71,10 @@ id=ShapeID("smithy.example#BlobPayloadEvent"), members={ "header": { - "index": 0, "target": STRING, "traits": [EVENT_HEADER_TRAIT, REQUIRED_TRAIT], }, "payload": { - "index": 1, "target": BLOB, "traits": [EVENT_PAYLOAD_TRAIT, REQUIRED_TRAIT], }, @@ -88,7 +83,7 @@ SCHEMA_ERROR_EVENT = Schema.collection( id=ShapeID("smithy.example#ErrorEvent"), - members={"message": {"index": 0, "target": STRING, "traits": [REQUIRED_TRAIT]}}, + members={"message": {"target": STRING, "traits": [REQUIRED_TRAIT]}}, traits=[ERROR_TRAIT], ) @@ -97,21 +92,21 @@ shape_type=ShapeType.UNION, traits=[STREAMING_TRAIT], members={ - "message": {"index": 0, "target": SCHEMA_MESSAGE_EVENT}, - "payload": {"index": 1, "target": SCHEMA_PAYLOAD_EVENT}, - "blobPayload": {"index": 2, "target": SCHEMA_BLOB_PAYLOAD_EVENT}, - "error": {"index": 3, "target": SCHEMA_ERROR_EVENT}, + "message": {"target": SCHEMA_MESSAGE_EVENT}, + "payload": {"target": SCHEMA_PAYLOAD_EVENT}, + "blobPayload": {"target": SCHEMA_BLOB_PAYLOAD_EVENT}, + "error": {"target": SCHEMA_ERROR_EVENT}, }, ) SCHEMA_INITIAL_MESSAGE = Schema.collection( id=ShapeID("smithy.example#EventStreamOperationInputOutput"), members={ - "message": {"index": 0, "target": STRING}, + "message": {"target": STRING}, # Event stream members will not be part of the operation input / output # shape schemas. # "stream": { - # "index": 1, + # # "target": SCHEMA_EVENT_STREAM # }, }, diff --git a/packages/smithy-core/src/smithy_core/schemas.py b/packages/smithy-core/src/smithy_core/schemas.py index a1b93d828..4d306a481 100644 --- a/packages/smithy-core/src/smithy_core/schemas.py +++ b/packages/smithy-core/src/smithy_core/schemas.py @@ -74,14 +74,13 @@ def __init__( traits = {} object.__setattr__(self, "traits", traits) - if members: - if isinstance(members, list): - m: dict[str, Schema] = {} - for member in members: - m[member.expect_member_name()] = member - members = m - else: + if members is None: members = {} + elif isinstance(members, list): + m: dict[str, Schema] = {} + for member in sorted(members, key=lambda m: m.expect_member_index()): + m[member.expect_member_name()] = member + members = m object.__setattr__(self, "members", members) if member_target is not None: @@ -194,7 +193,7 @@ def collection( id: ShapeID, shape_type: ShapeType = ShapeType.STRUCTURE, traits: list["Trait | DynamicTrait"] | None = None, - members: Mapping[str, "MemberSchema"] | None = None, + members: Mapping[str, "MemberSchema | None"] | None = None, ) -> Self: """Create a schema for a collection shape. @@ -205,15 +204,23 @@ def collection( properties, map keys/values, and union variants. In contrast to the main constructor, this is a dict of member names to a simplified dict containing only ``traits`` and a ``target``. Member schemas will be generated from this. + + If the value is None, it MUST be populated later. This is to allow a preservation + of modeled order without having to explicitly provide it and therefore generate + a ton of boilerplate. """ struct_members: dict[str, Schema] = {} if members: - for k in members.keys(): + for i, (k, member) in enumerate(members.items()): + if member is None: + struct_members[k] = None # type: ignore + continue + struct_members[k] = cls.member( id=id.with_member(k), - target=members[k]["target"], - index=members[k]["index"], - member_traits=members[k].get("traits"), + target=member["target"], + index=i, + member_traits=member.get("traits"), ) result = cls( @@ -268,7 +275,6 @@ class MemberSchema(TypedDict): """ target: Required[Schema] - index: Required[int] traits: NotRequired[list["Trait | DynamicTrait"]] diff --git a/packages/smithy-core/tests/unit/test_documents.py b/packages/smithy-core/tests/unit/test_documents.py index 8a1e3ccf1..0b4ff052c 100644 --- a/packages/smithy-core/tests/unit/test_documents.py +++ b/packages/smithy-core/tests/unit/test_documents.py @@ -402,7 +402,11 @@ def test_wrap_list_passes_schema_to_member_documents() -> None: list_schema = Schema.collection( id=id, shape_type=ShapeType.LIST, - members={"member": {"target": target_schema, "index": 0}}, + members={ + "member": { + "target": target_schema, + } + }, ) document = Document(["foo"], schema=list_schema) actual = document[0]._schema # pyright: ignore[reportPrivateUsage] @@ -421,7 +425,11 @@ def test_setitem_on_list_passes_schema_to_member_documents() -> None: list_schema = Schema.collection( id=id, shape_type=ShapeType.LIST, - members={"member": {"target": target_schema, "index": 0}}, + members={ + "member": { + "target": target_schema, + } + }, ) document = Document(["foo"], schema=list_schema) document[0] = "bar" @@ -440,7 +448,11 @@ def test_wrap_structure_passes_schema_to_member_documents() -> None: target_schema = Schema(id=ShapeID("smithy.api#String"), shape_type=ShapeType.STRING) struct_schema = Schema.collection( id=id, - members={"stringMember": {"target": target_schema, "index": 0}}, + members={ + "stringMember": { + "target": target_schema, + } + }, ) document = Document({"stringMember": "foo"}, schema=struct_schema) actual = document["stringMember"]._schema # pyright: ignore[reportPrivateUsage] @@ -458,7 +470,11 @@ def test_setitem_on_structure_passes_schema_to_member_documents() -> None: target_schema = Schema(id=ShapeID("smithy.api#String"), shape_type=ShapeType.STRING) struct_schema = Schema.collection( id=id, - members={"stringMember": {"target": target_schema, "index": 0}}, + members={ + "stringMember": { + "target": target_schema, + } + }, ) document = Document({"stringMember": "foo"}, schema=struct_schema) document["stringMember"] = "spam" @@ -478,8 +494,12 @@ def test_wrap_map_passes_schema_to_member_documents() -> None: id=id, shape_type=ShapeType.MAP, members={ - "key": {"target": STRING, "index": 0}, - "value": {"target": STRING, "index": 1}, + "key": { + "target": STRING, + }, + "value": { + "target": STRING, + }, }, ) document = Document({"spam": "eggs"}, schema=map_schema) @@ -496,8 +516,12 @@ def test_setitem_on_map_passes_schema_to_member_documents() -> None: id=id, shape_type=ShapeType.MAP, members={ - "key": {"target": STRING, "index": 0}, - "value": {"target": STRING, "index": 1}, + "key": { + "target": STRING, + }, + "value": { + "target": STRING, + }, }, ) document = Document({"spam": "eggs"}, schema=map_schema) @@ -517,47 +541,87 @@ def test_is_none(): STRING_LIST_SCHEMA = Schema.collection( id=ShapeID("smithy.example#StringList"), shape_type=ShapeType.LIST, - members={"member": {"target": STRING, "index": 0}}, + members={ + "member": { + "target": STRING, + } + }, ) STRING_MAP_SCHEMA = Schema.collection( id=ShapeID("smithy.example#StringMap"), shape_type=ShapeType.MAP, members={ - "key": {"target": STRING, "index": 0}, - "value": {"target": STRING, "index": 1}, + "key": { + "target": STRING, + }, + "value": { + "target": STRING, + }, }, ) SPARSE_STRING_LIST_SCHEMA = Schema.collection( id=ShapeID("smithy.example#StringList"), shape_type=ShapeType.LIST, - members={"member": {"target": STRING, "index": 0}}, + members={ + "member": { + "target": STRING, + } + }, traits=[SparseTrait()], ) SPARSE_STRING_MAP_SCHEMA = Schema.collection( id=ShapeID("smithy.example#StringMap"), shape_type=ShapeType.MAP, members={ - "key": {"target": STRING, "index": 0}, - "value": {"target": STRING, "index": 1}, + "key": { + "target": STRING, + }, + "value": { + "target": STRING, + }, }, traits=[SparseTrait()], ) SCHEMA: Schema = Schema.collection( id=ShapeID("smithy.example#DocumentSerdeShape"), members={ - "booleanMember": {"target": BOOLEAN, "index": 0}, - "integerMember": {"target": INTEGER, "index": 1}, - "floatMember": {"target": FLOAT, "index": 2}, - "bigDecimalMember": {"target": BIG_DECIMAL, "index": 3}, - "stringMember": {"target": STRING, "index": 4}, - "blobMember": {"target": BLOB, "index": 5}, - "timestampMember": {"target": TIMESTAMP, "index": 6}, - "documentMember": {"target": DOCUMENT, "index": 7}, - "listMember": {"target": STRING_LIST_SCHEMA, "index": 8}, - "mapMember": {"target": STRING_MAP_SCHEMA, "index": 9}, - # Index 10 is set below because it's a self-referential member. - "sparseListMember": {"target": SPARSE_STRING_LIST_SCHEMA, "index": 11}, - "sparseMapMember": {"target": SPARSE_STRING_MAP_SCHEMA, "index": 12}, + "booleanMember": { + "target": BOOLEAN, + }, + "integerMember": { + "target": INTEGER, + }, + "floatMember": { + "target": FLOAT, + }, + "bigDecimalMember": { + "target": BIG_DECIMAL, + }, + "stringMember": { + "target": STRING, + }, + "blobMember": { + "target": BLOB, + }, + "timestampMember": { + "target": TIMESTAMP, + }, + "documentMember": { + "target": DOCUMENT, + }, + "listMember": { + "target": STRING_LIST_SCHEMA, + }, + "mapMember": { + "target": STRING_MAP_SCHEMA, + }, + "structMember": None, + "sparseListMember": { + "target": SPARSE_STRING_LIST_SCHEMA, + }, + "sparseMapMember": { + "target": SPARSE_STRING_MAP_SCHEMA, + }, }, ) SCHEMA.members["structMember"] = Schema.member( diff --git a/packages/smithy-core/tests/unit/test_schemas.py b/packages/smithy-core/tests/unit/test_schemas.py index 212a98a46..b15fe472a 100644 --- a/packages/smithy-core/tests/unit/test_schemas.py +++ b/packages/smithy-core/tests/unit/test_schemas.py @@ -97,7 +97,7 @@ def test_collection_constructor(): ) schema = Schema.collection( id=ID, - members={member_name: {"target": STRING, "index": 0, "traits": [trait_value]}}, + members={member_name: {"target": STRING, "traits": [trait_value]}}, ) assert schema.members == {member_name: member} @@ -109,7 +109,7 @@ def test_member_constructor(): SensitiveTrait(), DynamicTrait(id=ShapeID("smithy.example#foo"), document_value="bar"), ], - members={"spam": {"target": STRING, "index": 0}}, + members={"spam": {"target": STRING}}, ) member_id = ShapeID("smithy.example#Spam$eggs") @@ -164,7 +164,7 @@ def test_member_constructor_asserts_target_is_not_member(): def test_contains(item: Any, contains: bool): schema = Schema.collection( id=ID, - members={"baz": {"target": STRING, "index": 0}}, + members={"baz": {"target": STRING}}, traits=[SensitiveTrait()], ) diff --git a/packages/smithy-core/tests/unit/test_type_registry.py b/packages/smithy-core/tests/unit/test_type_registry.py index 6a2a4ffc2..7375abaaa 100644 --- a/packages/smithy-core/tests/unit/test_type_registry.py +++ b/packages/smithy-core/tests/unit/test_type_registry.py @@ -63,7 +63,7 @@ class TestShape(DeserializeableShape): __test__ = False schema = Schema.collection( id=ShapeID("com.example#Test"), - members={"value": {"index": 0, "target": STRING, "traits": [RequiredTrait()]}}, + members={"value": {"target": STRING, "traits": [RequiredTrait()]}}, ) def __init__(self, value: str): diff --git a/packages/smithy-http/tests/unit/test_bindings.py b/packages/smithy-http/tests/unit/test_bindings.py index 5d84b2e05..f4820dcc2 100644 --- a/packages/smithy-http/tests/unit/test_bindings.py +++ b/packages/smithy-http/tests/unit/test_bindings.py @@ -21,7 +21,7 @@ PAYLOAD_BINDING = Schema.collection( id=ShapeID("com.example#Payload"), - members={"payload": {"index": 0, "target": STRING, "traits": [HTTPPayloadTrait()]}}, + members={"payload": {"target": STRING, "traits": [HTTPPayloadTrait()]}}, ) EVENT_STREAM_SCHEMA = Schema.collection( @@ -29,7 +29,6 @@ shape_type=ShapeType.UNION, members={ "stream": { - "index": 0, "target": Schema.collection(id=ShapeID("com.example#Event")), } }, @@ -37,41 +36,38 @@ ) EVENT_STREAM_BINDING = Schema.collection( id=ShapeID("com.example#Events"), - members={"stream": {"index": 0, "target": EVENT_STREAM_SCHEMA}}, + members={"stream": {"target": EVENT_STREAM_SCHEMA}}, ) STRING_MAP = Schema.collection( id=ShapeID("com.example#StringMap"), shape_type=ShapeType.MAP, members={ - "key": {"index": 0, "target": STRING}, - "value": {"index": 0, "target": STRING}, + "key": {"target": STRING}, + "value": {"target": STRING}, }, ) GENERAL_BINDINGS = Schema.collection( id=ShapeID("com.example#BodyBindings"), members={ - "label": {"index": 0, "target": STRING, "traits": [HTTPLabelTrait()]}, - "query": {"index": 1, "target": STRING, "traits": [HTTPQueryTrait()]}, + "label": {"target": STRING, "traits": [HTTPLabelTrait()]}, + "query": {"target": STRING, "traits": [HTTPQueryTrait()]}, "queryParams": { - "index": 2, "target": STRING_MAP, "traits": [HTTPQueryParamsTrait()], }, - "header": {"index": 3, "target": STRING, "traits": [HTTPHeaderTrait()]}, + "header": {"target": STRING, "traits": [HTTPHeaderTrait()]}, "prefixHeaders": { - "index": 4, "target": STRING_MAP, "traits": [HTTPPrefixHeadersTrait("foo")], }, - "hostLabel": {"index": 5, "target": STRING, "traits": [HostLabelTrait()]}, + "hostLabel": {"target": STRING, "traits": [HostLabelTrait()]}, "status": { - "index": 6, "target": INTEGER, "traits": [HTTPResponseCodeTrait()], }, - "body": {"index": 7, "target": STRING}, + "body": {"target": STRING}, }, ) diff --git a/packages/smithy-http/tests/unit/test_serializers.py b/packages/smithy-http/tests/unit/test_serializers.py index 0dd5eea6f..d477a7e9b 100644 --- a/packages/smithy-http/tests/unit/test_serializers.py +++ b/packages/smithy-http/tests/unit/test_serializers.py @@ -51,39 +51,38 @@ BOOLEAN_LIST = Schema.collection( id=ShapeID("com.smithy#BooleanList"), shape_type=ShapeType.LIST, - members={"member": {"index": 0, "target": BOOLEAN}}, + members={"member": {"target": BOOLEAN}}, ) STRING_LIST = Schema.collection( id=ShapeID("com.smithy#StringList"), shape_type=ShapeType.LIST, - members={"member": {"index": 0, "target": STRING}}, + members={"member": {"target": STRING}}, ) INTEGER_LIST = Schema.collection( id=ShapeID("com.smithy#IntegerList"), shape_type=ShapeType.LIST, - members={"member": {"index": 0, "target": INTEGER}}, + members={"member": {"target": INTEGER}}, ) FLOAT_LIST = Schema.collection( id=ShapeID("com.smithy#FloatList"), shape_type=ShapeType.LIST, - members={"member": {"index": 0, "target": FLOAT}}, + members={"member": {"target": FLOAT}}, ) BIG_DECIMAL_LIST = Schema.collection( id=ShapeID("com.smithy#BigDecimalList"), shape_type=ShapeType.LIST, - members={"member": {"index": 0, "target": BIG_DECIMAL}}, + members={"member": {"target": BIG_DECIMAL}}, ) BARE_TIMESTAMP_LIST = Schema.collection( id=ShapeID("com.smithy#BareTimestampList"), shape_type=ShapeType.LIST, - members={"member": {"index": 0, "target": TIMESTAMP}}, + members={"member": {"target": TIMESTAMP}}, ) HTTP_DATE_TIMESTAMP_LIST = Schema.collection( id=ShapeID("com.smithy#HttpDateTimestampList"), shape_type=ShapeType.LIST, members={ "member": { - "index": 0, "target": TIMESTAMP, "traits": [TimestampFormatTrait("http-date")], } @@ -94,7 +93,6 @@ shape_type=ShapeType.LIST, members={ "member": { - "index": 0, "target": TIMESTAMP, "traits": [TimestampFormatTrait("date-time")], } @@ -105,7 +103,6 @@ shape_type=ShapeType.LIST, members={ "member": { - "index": 0, "target": TIMESTAMP, "traits": [TimestampFormatTrait("epoch-seconds")], } @@ -115,8 +112,8 @@ id=ShapeID("com.smithy#StringMap"), shape_type=ShapeType.MAP, members={ - "key": {"index": 0, "target": STRING}, - "value": {"index": 1, "target": STRING}, + "key": {"target": STRING}, + "value": {"target": STRING}, }, ) @@ -159,62 +156,50 @@ def __init_subclass__( id=id, members={ "boolean_member": { - "index": 0, "target": BOOLEAN, "traits": [trait("boolean")], }, "boolean_list_member": { - "index": 1, "target": BOOLEAN_LIST, "traits": [trait("booleanList")], }, "integer_member": { - "index": 2, "target": INTEGER, "traits": [trait("integer")], }, "integer_list_member": { - "index": 3, "target": INTEGER_LIST, "traits": [trait("integerList")], }, "float_member": { - "index": 4, "target": FLOAT, "traits": [trait("float")], }, "float_list_member": { - "index": 5, "target": FLOAT_LIST, "traits": [trait("floatList")], }, "big_decimal_member": { - "index": 6, "target": BIG_DECIMAL, "traits": [trait("bigDecimal")], }, "big_decimal_list_member": { - "index": 7, "target": BIG_DECIMAL_LIST, "traits": [trait("bigDecimalList")], }, "string_member": { - "index": 8, "target": STRING, "traits": [trait("string")], }, "string_list_member": { - "index": 9, "target": STRING_LIST, "traits": [trait("stringList")], }, "default_timestamp_member": { - "index": 10, "target": TIMESTAMP, "traits": [trait("defaultTimestamp")], }, "http_date_timestamp_member": { - "index": 11, "target": TIMESTAMP, "traits": [ trait("httpDateTimestamp"), @@ -222,12 +207,10 @@ def __init_subclass__( ], }, "http_date_list_timestamp_member": { - "index": 12, "target": HTTP_DATE_TIMESTAMP_LIST, "traits": [trait("httpDateListTimestamp")], }, "date_time_timestamp_member": { - "index": 13, "target": TIMESTAMP, "traits": [ trait("dateTimeTimestamp"), @@ -235,12 +218,10 @@ def __init_subclass__( ], }, "date_time_list_timestamp_member": { - "index": 14, "target": DATE_TIME_TIMESTAMP_LIST, "traits": [trait("dateTimeListTimestamp")], }, "epoch_timestamp_member": { - "index": 15, "target": TIMESTAMP, "traits": [ trait("epochTimestamp"), @@ -248,12 +229,10 @@ def __init_subclass__( ], }, "epoch_list_timestamp_member": { - "index": 16, "target": EPOCH_TIMESTAMP_LIST, "traits": [trait("epochListTimestamp")], }, "string_map_member": { - "index": 17, "target": STRING_MAP, "traits": [map_trait], }, @@ -527,9 +506,7 @@ class HTTPResponseCode: ID: ClassVar[ShapeID] = ShapeID("com.smithy#HTTPResponseCode") SCHEMA: ClassVar[Schema] = Schema.collection( id=ID, - members={ - "code": {"index": 0, "target": INTEGER, "traits": [HTTPResponseCodeTrait()]} - }, + members={"code": {"target": INTEGER, "traits": [HTTPResponseCodeTrait()]}}, ) def serialize(self, serializer: ShapeSerializer) -> None: @@ -564,11 +541,10 @@ class HTTPImplicitPayload: id=ID, members={ "header": { - "index": 0, "target": STRING, "traits": [HTTPHeaderTrait("header")], }, - "payload_member": {"index": 1, "target": STRING}, + "payload_member": {"target": STRING}, }, ) @@ -610,9 +586,7 @@ class HTTPStringPayload: ID: ClassVar[ShapeID] = ShapeID("com.smithy#HTTPStringPayload") SCHEMA: ClassVar[Schema] = Schema.collection( id=ID, - members={ - "payload": {"index": 0, "target": STRING, "traits": [HTTPPayloadTrait()]} - }, + members={"payload": {"target": STRING, "traits": [HTTPPayloadTrait()]}}, ) def serialize(self, serializer: ShapeSerializer) -> None: @@ -644,9 +618,7 @@ class HTTPBlobPayload: ID: ClassVar[ShapeID] = ShapeID("com.smithy#HTTPBlobPayload") SCHEMA: ClassVar[Schema] = Schema.collection( id=ID, - members={ - "payload": {"index": 0, "target": BLOB, "traits": [HTTPPayloadTrait()]} - }, + members={"payload": {"target": BLOB, "traits": [HTTPPayloadTrait()]}}, ) def serialize(self, serializer: ShapeSerializer) -> None: @@ -680,7 +652,6 @@ class HTTPStreamingPayload: id=ID, members={ "payload": { - "index": 0, "target": BLOB, "traits": [HTTPPayloadTrait(), StreamingTrait()], } @@ -720,7 +691,6 @@ class HTTPStructuredPayload: id=ID, members={ "payload": { - "index": 0, "target": HTTPStringPayload.SCHEMA, "traits": [HTTPPayloadTrait()], } @@ -756,7 +726,7 @@ class HTTPStringLabel: ID: ClassVar[ShapeID] = ShapeID("com.smithy#HTTPStringLabel") SCHEMA: ClassVar[Schema] = Schema.collection( id=ID, - members={"label": {"index": 0, "target": STRING, "traits": [HTTPLabelTrait()]}}, + members={"label": {"target": STRING, "traits": [HTTPLabelTrait()]}}, ) def serialize(self, serializer: ShapeSerializer) -> None: @@ -788,9 +758,7 @@ class HTTPIntegerLabel: ID: ClassVar[ShapeID] = ShapeID("com.smithy#HTTPIntegerLabel") SCHEMA: ClassVar[Schema] = Schema.collection( id=ID, - members={ - "label": {"index": 0, "target": INTEGER, "traits": [HTTPLabelTrait()]} - }, + members={"label": {"target": INTEGER, "traits": [HTTPLabelTrait()]}}, ) def serialize(self, serializer: ShapeSerializer) -> None: @@ -822,7 +790,7 @@ class HTTPFloatLabel: ID: ClassVar[ShapeID] = ShapeID("com.smithy#HTTPFloatLabel") SCHEMA: ClassVar[Schema] = Schema.collection( id=ID, - members={"label": {"index": 0, "target": FLOAT, "traits": [HTTPLabelTrait()]}}, + members={"label": {"target": FLOAT, "traits": [HTTPLabelTrait()]}}, ) def serialize(self, serializer: ShapeSerializer) -> None: @@ -854,9 +822,7 @@ class HTTPBigDecimalLabel: ID: ClassVar[ShapeID] = ShapeID("com.smithy#HTTPBigDecimalLabel") SCHEMA: ClassVar[Schema] = Schema.collection( id=ID, - members={ - "label": {"index": 0, "target": BIG_DECIMAL, "traits": [HTTPLabelTrait()]} - }, + members={"label": {"target": BIG_DECIMAL, "traits": [HTTPLabelTrait()]}}, ) def serialize(self, serializer: ShapeSerializer) -> None: @@ -888,9 +854,7 @@ class HTTPBooleanLabel: ID: ClassVar[ShapeID] = ShapeID("com.smithy#HTTPBooleanLabel") SCHEMA: ClassVar[Schema] = Schema.collection( id=ID, - members={ - "label": {"index": 0, "target": BOOLEAN, "traits": [HTTPLabelTrait()]} - }, + members={"label": {"target": BOOLEAN, "traits": [HTTPLabelTrait()]}}, ) def serialize(self, serializer: ShapeSerializer) -> None: @@ -922,9 +886,7 @@ class HTTPDefaultTimestampLabel: ID: ClassVar[ShapeID] = ShapeID("com.smithy#HTTPDefaultTimestampLabel") SCHEMA: ClassVar[Schema] = Schema.collection( id=ID, - members={ - "label": {"index": 0, "target": TIMESTAMP, "traits": [HTTPLabelTrait()]} - }, + members={"label": {"target": TIMESTAMP, "traits": [HTTPLabelTrait()]}}, ) def serialize(self, serializer: ShapeSerializer) -> None: @@ -958,7 +920,6 @@ class HTTPEpochTimestampLabel: id=ID, members={ "label": { - "index": 0, "target": TIMESTAMP, "traits": [HTTPLabelTrait(), TimestampFormatTrait("epoch-seconds")], } @@ -996,7 +957,6 @@ class HTTPDateTimestampLabel: id=ID, members={ "label": { - "index": 0, "target": TIMESTAMP, "traits": [HTTPLabelTrait(), TimestampFormatTrait("http-date")], } @@ -1034,7 +994,6 @@ class HTTPDateTimeTimestampLabel: id=ID, members={ "label": { - "index": 0, "target": TIMESTAMP, "traits": [HTTPLabelTrait(), TimestampFormatTrait("date-time")], } @@ -1072,7 +1031,6 @@ class HostLabel: id=ID, members={ "label": { - "index": 0, "target": STRING, "traits": [HostLabelTrait()], } diff --git a/packages/smithy-json/tests/unit/__init__.py b/packages/smithy-json/tests/unit/__init__.py index 9cdff545b..34d1362ec 100644 --- a/packages/smithy-json/tests/unit/__init__.py +++ b/packages/smithy-json/tests/unit/__init__.py @@ -23,67 +23,103 @@ STRING_LIST_SCHEMA = Schema.collection( id=ShapeID("smithy.example#StringList"), shape_type=ShapeType.LIST, - members={"member": {"target": STRING, "index": 0}}, + members={ + "member": { + "target": STRING, + } + }, ) STRING_MAP_SCHEMA = Schema.collection( id=ShapeID("smithy.example#StringMap"), shape_type=ShapeType.MAP, members={ - "key": {"target": STRING, "index": 0}, - "value": {"target": STRING, "index": 1}, + "key": { + "target": STRING, + }, + "value": { + "target": STRING, + }, }, ) SPARSE_STRING_LIST_SCHEMA = Schema.collection( id=ShapeID("smithy.example#StringList"), shape_type=ShapeType.LIST, - members={"member": {"target": STRING, "index": 0}}, + members={ + "member": { + "target": STRING, + } + }, traits=[SparseTrait()], ) SPARSE_STRING_MAP_SCHEMA = Schema.collection( id=ShapeID("smithy.example#StringMap"), shape_type=ShapeType.MAP, members={ - "key": {"target": STRING, "index": 0}, - "value": {"target": STRING, "index": 1}, + "key": { + "target": STRING, + }, + "value": { + "target": STRING, + }, }, traits=[SparseTrait()], ) SCHEMA: Schema = Schema.collection( id=ShapeID("smithy.example#SerdeShape"), members={ - "booleanMember": {"target": BOOLEAN, "index": 0}, - "integerMember": {"target": INTEGER, "index": 1}, - "floatMember": {"target": FLOAT, "index": 2}, - "bigDecimalMember": {"target": BIG_DECIMAL, "index": 3}, - "stringMember": {"target": STRING, "index": 4}, + "booleanMember": { + "target": BOOLEAN, + }, + "integerMember": { + "target": INTEGER, + }, + "floatMember": { + "target": FLOAT, + }, + "bigDecimalMember": { + "target": BIG_DECIMAL, + }, + "stringMember": { + "target": STRING, + }, "jsonNameMember": { "target": STRING, "traits": [JSONNameTrait("jsonName")], - "index": 5, }, - "blobMember": {"target": BLOB, "index": 6}, - "timestampMember": {"target": TIMESTAMP, "index": 7}, + "blobMember": { + "target": BLOB, + }, + "timestampMember": { + "target": TIMESTAMP, + }, "dateTimeMember": { "target": TIMESTAMP, "traits": [TimestampFormatTrait("date-time")], - "index": 8, }, "httpDateMember": { "target": TIMESTAMP, "traits": [TimestampFormatTrait("http-date")], - "index": 9, }, "epochSecondsMember": { "target": TIMESTAMP, "traits": [TimestampFormatTrait("epoch-seconds")], - "index": 10, }, - "documentMember": {"target": DOCUMENT, "index": 11}, - "listMember": {"target": STRING_LIST_SCHEMA, "index": 12}, - "mapMember": {"target": STRING_MAP_SCHEMA, "index": 13}, - # Index 14 is set below because it's a self-referential member. - "sparseListMember": {"target": SPARSE_STRING_LIST_SCHEMA, "index": 15}, - "sparseMapMember": {"target": SPARSE_STRING_MAP_SCHEMA, "index": 16}, + "documentMember": { + "target": DOCUMENT, + }, + "listMember": { + "target": STRING_LIST_SCHEMA, + }, + "mapMember": { + "target": STRING_MAP_SCHEMA, + }, + "structMember": None, + "sparseListMember": { + "target": SPARSE_STRING_LIST_SCHEMA, + }, + "sparseMapMember": { + "target": SPARSE_STRING_MAP_SCHEMA, + }, }, ) SCHEMA.members["structMember"] = Schema.member( From bbaf50abcb3df2dcedb238051f3097c4d87b37c3 Mon Sep 17 00:00:00 2001 From: JordonPhillips Date: Thu, 5 Jun 2025 12:59:30 +0200 Subject: [PATCH 2/3] Use natural member order to construct binding list --- packages/smithy-http/src/smithy_http/bindings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/smithy-http/src/smithy_http/bindings.py b/packages/smithy-http/src/smithy_http/bindings.py index de0eee090..39f88f30d 100644 --- a/packages/smithy-http/src/smithy_http/bindings.py +++ b/packages/smithy-http/src/smithy_http/bindings.py @@ -76,13 +76,13 @@ def __init__(self, struct: Schema, response_status: int) -> None: self.response_status = response_status found_body = False found_payload = False - self.bindings = [Binding.BODY] * len(struct.members) + self.bindings = [] self.payload_member = None self.event_stream_member = None for member in struct.members.values(): binding = self._do_match(member) - self.bindings[member.expect_member_index()] = binding + self.bindings.append(binding) found_body = ( found_body or binding is Binding.BODY or binding is Binding.HOST ) From 3eef7458cd9a57949e7be4d0d1a44356519773e4 Mon Sep 17 00:00:00 2001 From: JordonPhillips Date: Thu, 5 Jun 2025 13:08:46 +0200 Subject: [PATCH 3/3] Skip magic trailing comma in generated code This makes generated code a bit more dense by informing ruff to not use trailing commas as a signal skip condensing an expanded block. --- .../smithy/python/codegen/generators/SetupGenerator.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/SetupGenerator.java b/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/SetupGenerator.java index 021ba4019..64e2e7a2e 100644 --- a/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/SetupGenerator.java +++ b/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/SetupGenerator.java @@ -190,6 +190,9 @@ private static void writePyproject( [tool.ruff.lint] ignore = ["F841"] + [tool.ruff.format] + skip-magic-trailing-comma = true + [tool.pytest.ini_options] python_classes = ["!Test"] asyncio_mode = "auto"