Skip to content

Commit 207c898

Browse files
author
Michael Johansen
committed
Add a converter for datetime.datetime <--> Timestamp.
Signed-off-by: Michael Johansen <[email protected]>
1 parent ce17c45 commit 207c898

File tree

4 files changed

+53
-4
lines changed

4 files changed

+53
-4
lines changed

src/nipanel/_convert.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from nipanel.converters.builtin import (
1313
BoolConverter,
1414
BytesConverter,
15+
DateTimeConverter,
1516
FloatConverter,
1617
IntConverter,
1718
StrConverter,
@@ -37,6 +38,7 @@
3738
FloatConverter(),
3839
IntConverter(),
3940
StrConverter(),
41+
DateTimeConverter(),
4042
# Containers next
4143
BoolCollectionConverter(),
4244
BytesCollectionConverter(),

src/nipanel/converters/builtin.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"""Classes to convert between builtin Python scalars and containers."""
22

3+
import datetime as dt
34
from collections.abc import Collection
45
from typing import Type
56

6-
from google.protobuf import wrappers_pb2
7+
from google.protobuf import timestamp_pb2, wrappers_pb2
78
from ni.panels.v1 import panel_types_pb2
89

910
from nipanel.converters import Converter
@@ -119,6 +120,30 @@ def to_python_value(self, protobuf_message: wrappers_pb2.StringValue) -> str:
119120
return protobuf_message.value
120121

121122

123+
class DateTimeConverter(Converter[dt.datetime, timestamp_pb2.Timestamp]):
124+
"""A converter for datetime.datetime types."""
125+
126+
@property
127+
def python_typename(self) -> str:
128+
"""The Python type that this converter handles."""
129+
return dt.datetime.__name__
130+
131+
@property
132+
def protobuf_message(self) -> Type[timestamp_pb2.Timestamp]:
133+
"""The type-specific protobuf message for the Python type."""
134+
return timestamp_pb2.Timestamp
135+
136+
def to_protobuf_message(self, python_value: dt.datetime) -> timestamp_pb2.Timestamp:
137+
"""Convert the Python dt.datetime to a protobuf timestamp_pb2.Timestamp."""
138+
ts = self.protobuf_message()
139+
ts.FromDatetime(python_value)
140+
return ts
141+
142+
def to_python_value(self, protobuf_message: timestamp_pb2.Timestamp) -> dt.datetime:
143+
"""Convert the protobuf timestamp_pb2.Timestamp to a Python dt.datetime."""
144+
return protobuf_message.ToDatetime()
145+
146+
122147
class BoolCollectionConverter(Converter[Collection[bool], panel_types_pb2.BoolCollection]):
123148
"""A converter for a Collection of bools."""
124149

tests/unit/test_convert.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import datetime as dt
12
from typing import Any, Collection, Union
23

34
import numpy as np
45
import pytest
5-
from google.protobuf import any_pb2, wrappers_pb2
6+
from google.protobuf import any_pb2, timestamp_pb2, wrappers_pb2
67
from google.protobuf.message import Message
78
from ni.panels.v1 import panel_types_pb2
89
from ni.protobuf.types.scalar_pb2 import ScalarData
@@ -53,6 +54,7 @@
5354
(tests.types.MixinIntEnum.VALUE11, "int"),
5455
(tests.types.MyStrEnum.VALUE1, "str"),
5556
(tests.types.MixinStrEnum.VALUE11, "str"),
57+
(dt.datetime.now(), "datetime"),
5658
([False, False], "Collection.bool"),
5759
([b"mystr", b"mystr"], "Collection.bytes"),
5860
([456.2, 1.0], "Collection.float"),
@@ -123,6 +125,16 @@ def test___python_builtin_scalar___to_any___valid_wrapperpb2_value(
123125
assert unpack_dest.value == expected_value
124126

125127

128+
def test___python_datetime_datetime___to_any___valid_timestamppb2_value() -> None:
129+
expected_value = dt.datetime.now()
130+
result = nipanel._convert.to_any(expected_value)
131+
unpack_dest = timestamp_pb2.Timestamp()
132+
_assert_any_and_unpack(result, unpack_dest)
133+
134+
assert isinstance(unpack_dest, timestamp_pb2.Timestamp)
135+
assert unpack_dest.ToDatetime() == expected_value
136+
137+
126138
@pytest.mark.parametrize(
127139
"proto_type, default_value, expected_value",
128140
[
@@ -175,6 +187,18 @@ def test___wrapperpb2_value___from_any___valid_python_value(
175187
assert result == expected_value
176188

177189

190+
def test___timestamppb2_timestamp___from_any___valid_python_value() -> None:
191+
expected_value = dt.datetime.now()
192+
pb_value = timestamp_pb2.Timestamp()
193+
pb_value.FromDatetime(expected_value)
194+
packed_any = _pack_into_any(pb_value)
195+
196+
result = nipanel._convert.from_any(packed_any)
197+
198+
assert isinstance(result, dt.datetime)
199+
assert result == expected_value
200+
201+
178202
@pytest.mark.parametrize(
179203
"proto_type, expected_value",
180204
[

tests/unit/test_streamlit_panel.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import enum
2-
from datetime import datetime
32

43
import grpc
54
import pytest
@@ -312,7 +311,6 @@ def test___enum_type___set_value___gets_same_value(
312311
@pytest.mark.parametrize(
313312
"value_payload",
314313
[
315-
datetime.now(),
316314
lambda x: x + 1,
317315
[1, "string"],
318316
["string", []],

0 commit comments

Comments
 (0)