Skip to content

Commit 755472f

Browse files
committed
String-convert "string" types in case they're not actually strings (UUIDs). Split out formats from types to allow for slightly more granular serializing.
1 parent f214373 commit 755472f

File tree

2 files changed

+35
-18
lines changed

2 files changed

+35
-18
lines changed

pydanticrud/backends/dynamodb.py

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
from typing import Optional, Set
1+
from typing import Optional, Set, Union
22
import logging
33
import json
44
from datetime import datetime
5-
from base64 import b64encode, b64decode
65

76
import boto3
87
from boto3.dynamodb.conditions import Key, Attr
@@ -80,20 +79,16 @@ def _to_epoch_float(dt):
8079

8180
SERIALIZE_MAP = {
8281
"number": str, # float or decimal
83-
"string": lambda d: d.isoformat() if isinstance(d, datetime) else d, # string, datetime
82+
"string": str,
83+
"string:date-time": lambda d: d.isoformat(),
8484
"boolean": lambda d: 1 if d else 0,
8585
"object": json.dumps,
8686
"array": json.dumps,
8787
}
8888

8989

90-
def do_nothing(x):
91-
return x
92-
93-
9490
DESERIALIZE_MAP = {
9591
"number": float,
96-
"string": do_nothing,
9792
"boolean": bool,
9893
"object": json.loads,
9994
"array": json.loads,
@@ -121,27 +116,44 @@ def __init__(self, schema):
121116
self.properties = schema["properties"]
122117
self.definitions = schema.get("definitions")
123118

124-
def _get_type_possibilities(self, field_name) -> Set[str]:
119+
def _get_type_possibilities(self, field_name) -> Set[tuple]:
125120
field_properties = self.properties[field_name]
126121

127122
possible_types = []
128123
if "anyOf" in field_properties:
129-
possible_types.extend([r.get("$ref", r.get("type")) for r in field_properties["anyOf"]])
124+
possible_types.extend([r.get("$ref", r) for r in field_properties["anyOf"]])
130125
else:
131-
possible_types.append(field_properties.get("$ref", field_properties.get("type")))
132-
return set([
133-
(self.definitions[t.split('/')[-1]].get("type") if t.startswith('#/') else t)
126+
possible_types.append(field_properties.get("$ref", field_properties))
127+
128+
def type_from_definition(definition_signature: Union[str, dict]) -> dict:
129+
if isinstance(definition_signature, str):
130+
t = definition_signature.split('/')[-1]
131+
return self.definitions[t]
132+
return definition_signature
133+
134+
type_dicts = [
135+
type_from_definition(t)
134136
for t in possible_types
137+
]
138+
139+
return set([
140+
(t['type'], t.get('format', ''))
141+
for t in type_dicts
135142
])
136143

137144
def _serialize_field(self, field_name, value):
138145
field_types = self._get_type_possibilities(field_name)
139146
if value is not None:
140147
for t in field_types:
141148
try:
142-
return SERIALIZE_MAP[t](value)
149+
type_signature = ":".join(t).rstrip(':')
150+
try:
151+
return SERIALIZE_MAP[type_signature](value)
152+
except KeyError:
153+
return SERIALIZE_MAP[t[0]](value)
143154
except (ValueError, TypeError, KeyError):
144155
pass
156+
145157
return value
146158

147159
def serialize_record(self, data_dict) -> dict:
@@ -158,9 +170,14 @@ def _deserialize_field(self, field_name, value):
158170
if value is not None:
159171
for t in field_types:
160172
try:
161-
return DESERIALIZE_MAP[t](value)
173+
type_signature = ":".join(t).rstrip(':')
174+
try:
175+
return DESERIALIZE_MAP[type_signature](value)
176+
except KeyError:
177+
return DESERIALIZE_MAP[t[0]](value)
162178
except (ValueError, TypeError, KeyError):
163179
pass
180+
164181
return value
165182

166183
def deserialize_record(self, data_dict) -> dict:

tests/test_dynamodb.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
from typing import Dict, List, Optional, Union
2-
from base64 import b64decode, b64encode
32
from decimal import Decimal
43
from datetime import datetime
5-
import json
6-
from uuid import uuid4
4+
from uuid import uuid4, UUID
75
import random
86

97
import docker
@@ -32,6 +30,7 @@ class SimpleKeyModel(BaseModel):
3230
enabled: bool
3331
data: Dict[int, int] = None
3432
items: List[int]
33+
hash: UUID
3534

3635
class Config:
3736
title = "ModelTitle123"
@@ -122,6 +121,7 @@ def simple_model_data_generator(**kwargs):
122121
enabled=random.choice((True, False)),
123122
data={random.randint(0, 1000): random.randint(0, 1000)},
124123
items=[random.randint(0, 100000), random.randint(0, 100000), random.randint(0, 100000)],
124+
hash=uuid4()
125125
)
126126
data.update(kwargs)
127127
return data

0 commit comments

Comments
 (0)