Skip to content

Commit 172c98a

Browse files
th0114ndIlya Konstantinov
authored andcommitted
Allow TimestampAttribute to be nullable (#14)
1 parent 446b922 commit 172c98a

File tree

3 files changed

+30
-6
lines changed

3 files changed

+30
-6
lines changed

pynamodb_attributes/timestamp.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ def serialize(self, value: datetime) -> str:
2525
return str(int(value.timestamp() * self._multiplier))
2626

2727
def __set__(self, instance: Any, value: Optional[Any]) -> None:
28-
if not isinstance(value, datetime):
29-
raise TypeError(f"value has invalid type '{type(value)}'; datetime expected")
30-
if value.tzinfo is None or value.tzinfo.utcoffset(value) is None:
31-
raise TypeError("aware datetime expected")
28+
if value is not None:
29+
if not isinstance(value, datetime):
30+
raise TypeError(f"value has invalid type '{type(value)}'; datetime expected")
31+
if value.tzinfo is None or value.tzinfo.utcoffset(value) is None:
32+
raise TypeError("aware datetime expected")
3233
return super().__set__(instance, value)
3334

3435

pynamodb_attributes/unicode_delimited_tuple.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def deserialize(self, value: str) -> T:
5151
def serialize(self, value: T) -> str:
5252
if not isinstance(value, self.tuple_type):
5353
raise TypeError(f"value has invalid type '{type(value)}'; expected '{self.tuple_type}'")
54-
values = [e for e in value]
54+
values = list(value)
5555
while values and values[-1] is None:
5656
del values[-1]
5757
strings = [str(e) for e in values]

tests/timestamp_attribute_test.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class MyModel(Model):
1919
value_ms = TimestampMsAttribute()
2020
value_us = TimestampUsAttribute()
2121

22+
null_value = TimestampAttribute(null=True)
23+
2224

2325
@pytest.fixture(scope='module', autouse=True)
2426
def create_table():
@@ -39,6 +41,7 @@ def test_serialization(uuid_key):
3941
assert model.value == now.replace(microsecond=0)
4042
assert model.value_ms == now.replace(microsecond=now.microsecond // 1_000 * 1_000)
4143
assert model.value_us == now
44+
assert model.null_value is None
4245

4346

4447
def test_set_invalid_type():
@@ -53,8 +56,28 @@ def test_set_naive_datetime():
5356
model.value = datetime.utcnow()
5457

5558

59+
def test_set_none_succeeds_on_nullable():
60+
model = MyModel()
61+
model.null_value = None
62+
assert model.null_value is None
63+
64+
65+
def test_set_timestame_succeeds_on_nullable():
66+
model = MyModel()
67+
now = datetime.now(tz=timezone.utc)
68+
model.null_value = now
69+
assert model.null_value == now
70+
71+
5672
def test_set_get():
5773
model = MyModel()
5874
now = datetime.now(tz=timezone.utc)
5975
model.value = now
60-
assert model.value == now # assert no data loss (before serialization)
76+
assert model.value == now, "data lost before serialization"
77+
78+
79+
def test_set_get_nullable():
80+
model = MyModel()
81+
now = datetime.now(tz=timezone.utc)
82+
model.null_value = now
83+
assert model.null_value == now, "data lost before serialization"

0 commit comments

Comments
 (0)