Skip to content

Commit f65db3c

Browse files
Use shared settings for JSON codec
1 parent 059aefc commit f65db3c

File tree

5 files changed

+48
-80
lines changed

5 files changed

+48
-80
lines changed

packages/smithy-json/src/smithy_json/__init__.py

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from smithy_core.serializers import ShapeSerializer
1010
from smithy_core.types import TimestampFormat
1111

12+
from ._private import JSONSettings as _JSONSettings
1213
from ._private.deserializers import JSONShapeDeserializer as _JSONShapeDeserializer
1314
from ._private.serializers import JSONShapeSerializer as _JSONShapeSerializer
1415

@@ -18,15 +19,12 @@
1819
class JSONCodec(Codec):
1920
"""A codec for converting shapes to/from JSON."""
2021

21-
_use_json_name: bool
22-
_use_timestamp_format: bool
23-
_default_timestamp_format: TimestampFormat
24-
2522
def __init__(
2623
self,
2724
use_json_name: bool = True,
2825
use_timestamp_format: bool = True,
2926
default_timestamp_format: TimestampFormat = TimestampFormat.DATE_TIME,
27+
default_namespace: str | None = None,
3028
) -> None:
3129
"""Initializes a JSONCodec.
3230
@@ -37,28 +35,20 @@ def __init__(
3735
:param default_timestamp_format: The default timestamp format to use if the
3836
`smithy.api#timestampFormat` trait is not enabled or not present.
3937
"""
40-
self._use_json_name = use_json_name
41-
self._use_timestamp_format = use_timestamp_format
42-
self._default_timestamp_format = default_timestamp_format
38+
self._settings = _JSONSettings(
39+
use_json_name=use_json_name,
40+
use_timestamp_format=use_timestamp_format,
41+
default_timestamp_format=default_timestamp_format,
42+
)
4343

4444
@property
4545
def media_type(self) -> str:
4646
return "application/json"
4747

4848
def create_serializer(self, sink: BytesWriter) -> "ShapeSerializer":
49-
return _JSONShapeSerializer(
50-
sink,
51-
use_json_name=self._use_json_name,
52-
use_timestamp_format=self._use_timestamp_format,
53-
default_timestamp_format=self._default_timestamp_format,
54-
)
49+
return _JSONShapeSerializer(sink, settings=self._settings)
5550

5651
def create_deserializer(self, source: bytes | BytesReader) -> "ShapeDeserializer":
5752
if isinstance(source, bytes):
5853
source = BytesIO(source)
59-
return _JSONShapeDeserializer(
60-
source,
61-
use_json_name=self._use_json_name,
62-
use_timestamp_format=self._use_timestamp_format,
63-
default_timestamp_format=self._default_timestamp_format,
64-
)
54+
return _JSONShapeDeserializer(source, settings=self._settings)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: Apache-2.0
33

4+
from dataclasses import dataclass
45
from typing import Protocol, runtime_checkable
56

7+
from smithy_core.types import TimestampFormat
8+
69

710
@runtime_checkable
811
class Flushable(Protocol):
912
"""A protocol for objects that can be flushed."""
1013

1114
def flush(self) -> None: ...
15+
16+
17+
@dataclass
18+
class JSONSettings:
19+
use_json_name: bool = True
20+
use_timestamp_format: bool = True
21+
default_timestamp_format: TimestampFormat = TimestampFormat.DATE_TIME

packages/smithy-json/src/smithy_json/_private/deserializers.py

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from smithy_core.traits import JSONNameTrait, TimestampFormatTrait
1919
from smithy_core.types import TimestampFormat
2020

21+
from . import JSONSettings
2122
from .documents import JSONDocument
2223

2324
# TODO: put these type hints in a pyi somewhere. There here because ijson isn't
@@ -89,17 +90,10 @@ def peek(self) -> JSONParseEvent:
8990

9091
class JSONShapeDeserializer(ShapeDeserializer):
9192
def __init__(
92-
self,
93-
source: BytesReader,
94-
*,
95-
use_json_name: bool = True,
96-
use_timestamp_format: bool = True,
97-
default_timestamp_format: TimestampFormat = TimestampFormat.DATE_TIME,
93+
self, source: BytesReader, *, settings: JSONSettings | None = None
9894
) -> None:
9995
self._stream = BufferedParser(ijson.parse(source))
100-
self._use_json_name = use_json_name
101-
self._use_timestamp_format = use_timestamp_format
102-
self._default_timestamp_format = default_timestamp_format
96+
self._settings = settings or JSONSettings()
10397

10498
# A mapping of json name to member name for each shape. Since the deserializer
10599
# is shared and we don't know which shapes will be deserialized, this is
@@ -164,13 +158,7 @@ def read_string(self, schema: Schema) -> str:
164158
def read_document(self, schema: Schema) -> Document:
165159
start = next(self._stream)
166160
if start.type not in ("start_map", "start_array"):
167-
return JSONDocument(
168-
start.value,
169-
schema=schema,
170-
use_json_name=self._use_json_name,
171-
default_timestamp_format=self._default_timestamp_format,
172-
use_timestamp_format=self._use_timestamp_format,
173-
)
161+
return JSONDocument(start.value, schema=schema, settings=self._settings)
174162

175163
end_type = "end_map" if start.type == "start_map" else "end_array"
176164
builder = cast(TypedObjectBuilder, ObjectBuilder())
@@ -180,17 +168,11 @@ def read_document(self, schema: Schema) -> Document:
180168
).path != start.path or event.type != end_type:
181169
builder.event(event.type, event.value)
182170

183-
return JSONDocument(
184-
builder.value,
185-
schema=schema,
186-
use_json_name=self._use_json_name,
187-
default_timestamp_format=self._default_timestamp_format,
188-
use_timestamp_format=self._use_timestamp_format,
189-
)
171+
return JSONDocument(builder.value, schema=schema, settings=self._settings)
190172

191173
def read_timestamp(self, schema: Schema) -> datetime.datetime:
192-
format = self._default_timestamp_format
193-
if self._use_timestamp_format:
174+
format = self._settings.default_timestamp_format
175+
if self._settings.use_timestamp_format:
194176
if format_trait := schema.get_trait(TimestampFormatTrait):
195177
format = format_trait.format
196178

@@ -221,7 +203,7 @@ def read_struct(
221203
next(self._stream)
222204

223205
def _resolve_member(self, schema: Schema, key: str) -> Schema | None:
224-
if self._use_json_name:
206+
if self._settings.use_json_name:
225207
if schema.id not in self._json_names:
226208
self._cache_json_names(schema=schema)
227209
if key in self._json_names[schema.id]:

packages/smithy-json/src/smithy_json/_private/documents.py

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99
from smithy_core.documents import Document, DocumentValue
1010
from smithy_core.prelude import DOCUMENT
1111
from smithy_core.schemas import Schema
12-
from smithy_core.shapes import ShapeType
12+
from smithy_core.shapes import ShapeID, ShapeType
1313
from smithy_core.traits import JSONNameTrait, TimestampFormatTrait
14-
from smithy_core.types import TimestampFormat
1514
from smithy_core.utils import expect_type
1615

16+
from . import JSONSettings
17+
1718

1819
class JSONDocument(Document):
1920
_schema: Schema
@@ -24,24 +25,26 @@ def __init__(
2425
value: DocumentValue | dict[str, "Document"] | list["Document"],
2526
*,
2627
schema: Schema = DOCUMENT,
27-
use_json_name: bool = True,
28-
use_timestamp_format: bool = True,
29-
default_timestamp_format: TimestampFormat = TimestampFormat.DATE_TIME,
28+
settings: JSONSettings | None = None,
3029
) -> None:
3130
super().__init__(value, schema=schema)
32-
self._use_json_name = use_json_name
33-
self._use_timestamp_format = use_timestamp_format
34-
self._default_timestamp_format = default_timestamp_format
31+
self._settings = settings or JSONSettings()
3532
self._json_names = {}
3633

37-
if use_json_name and schema.shape_type in (
34+
if self._settings.use_json_name and schema.shape_type in (
3835
ShapeType.STRUCTURE,
3936
ShapeType.UNION,
4037
):
4138
for member_name, member_schema in schema.members.items():
4239
if json_name := member_schema.get_trait(JSONNameTrait):
4340
self._json_names[json_name.value] = member_name
4441

42+
@property
43+
def discriminator(self) -> ShapeID:
44+
if self._type is ShapeType.MAP:
45+
return ShapeID(self.as_map()["__type"].as_string())
46+
return super().discriminator
47+
4548
def as_blob(self) -> bytes:
4649
return b64decode(expect_type(str, self._value))
4750

@@ -51,8 +54,8 @@ def as_float(self) -> float:
5154
return float(expect_type(Decimal, self._value))
5255

5356
def as_timestamp(self) -> datetime:
54-
format = self._default_timestamp_format
55-
if self._use_timestamp_format:
57+
format = self._settings.default_timestamp_format
58+
if self._settings.use_timestamp_format:
5659
if format_trait := self._schema.get_trait(TimestampFormatTrait):
5760
format = format_trait.format
5861

@@ -106,13 +109,7 @@ def _new_document(
106109
value: DocumentValue | dict[str, "Document"] | list["Document"],
107110
schema: Schema,
108111
) -> "Document":
109-
return JSONDocument(
110-
value,
111-
schema=schema,
112-
use_json_name=self._use_json_name,
113-
use_timestamp_format=self._use_timestamp_format,
114-
default_timestamp_format=self._default_timestamp_format,
115-
)
112+
return JSONDocument(value, schema=schema, settings=self._settings)
116113

117114
def _wrap_map(self, value: Mapping[str, DocumentValue]) -> dict[str, "Document"]:
118115
if self._schema.shape_type not in (ShapeType.STRUCTURE, ShapeType.UNION):

packages/smithy-json/src/smithy_json/_private/serializers.py

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,34 +21,23 @@
2121
from smithy_core.traits import JSONNameTrait, TimestampFormatTrait
2222
from smithy_core.types import TimestampFormat
2323

24-
from . import Flushable
24+
from . import Flushable, JSONSettings
2525

2626
_INF: float = float("inf")
2727
_NEG_INF: float = float("-inf")
2828

2929

3030
class JSONShapeSerializer(ShapeSerializer):
31-
_stream: "StreamingJSONEncoder"
32-
_use_json_name: bool
33-
_use_timestamp_format: bool
34-
_default_timestamp_format: TimestampFormat
35-
3631
def __init__(
37-
self,
38-
sink: BytesWriter,
39-
use_json_name: bool = True,
40-
use_timestamp_format: bool = True,
41-
default_timestamp_format: TimestampFormat = TimestampFormat.DATE_TIME,
32+
self, sink: BytesWriter, *, settings: JSONSettings | None = None
4233
) -> None:
4334
self._stream = StreamingJSONEncoder(sink)
44-
self._use_json_name = use_json_name
45-
self._use_timestamp_format = use_timestamp_format
46-
self._default_timestamp_format = default_timestamp_format
35+
self._settings = settings or JSONSettings()
4736

4837
def begin_struct(
4938
self, schema: "Schema"
5039
) -> AbstractContextManager["ShapeSerializer"]:
51-
return JSONStructSerializer(self._stream, self, self._use_json_name)
40+
return JSONStructSerializer(self._stream, self, self._settings.use_json_name)
5241

5342
def begin_list(
5443
self, schema: "Schema", size: int
@@ -82,8 +71,8 @@ def write_blob(self, schema: "Schema", value: bytes) -> None:
8271
self._stream.write_string(b64encode(value).decode("utf-8"))
8372

8473
def write_timestamp(self, schema: "Schema", value: datetime) -> None:
85-
format = self._default_timestamp_format
86-
if self._use_timestamp_format:
74+
format = self._settings.default_timestamp_format
75+
if self._settings.use_timestamp_format:
8776
if format_trait := schema.get_trait(TimestampFormatTrait):
8877
format = format_trait.format
8978

0 commit comments

Comments
 (0)