Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/all_types/define_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class MyMixedEnum(enum.Enum):
"int": 42,
"str": "sample string",
"dt_datetime": dt.datetime.now(),
"dt_timedelta": dt.timedelta(weeks=2, days=5, minutes=12, milliseconds=75),
# supported enum and flag types
"intflags": MyIntFlags.VALUE1 | MyIntFlags.VALUE4,
"intenum": MyIntEnum.VALUE20,
Expand Down
2 changes: 2 additions & 0 deletions src/nipanel/_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
BoolConverter,
BytesConverter,
DTDateTimeConverter,
DTTimeDeltaConverter,
FloatConverter,
IntConverter,
StrConverter,
Expand All @@ -39,6 +40,7 @@
IntConverter(),
StrConverter(),
DTDateTimeConverter(),
DTTimeDeltaConverter(),
# Containers next
BoolCollectionConverter(),
BytesCollectionConverter(),
Expand Down
26 changes: 25 additions & 1 deletion src/nipanel/converters/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from collections.abc import Collection
from typing import Type

from google.protobuf import timestamp_pb2, wrappers_pb2
from google.protobuf import duration_pb2, timestamp_pb2, wrappers_pb2
from ni.panels.v1 import panel_types_pb2

from nipanel.converters import Converter
Expand Down Expand Up @@ -144,6 +144,30 @@ def to_python_value(self, protobuf_message: timestamp_pb2.Timestamp) -> dt.datet
return protobuf_message.ToDatetime()


class DTTimeDeltaConverter(Converter[dt.timedelta, duration_pb2.Duration]):
"""A converter for datetime.timedelta types."""

@property
def python_typename(self) -> str:
"""The Python type that this converter handles."""
return dt.timedelta.__name__

@property
def protobuf_message(self) -> Type[duration_pb2.Duration]:
"""The type-specific protobuf message for the Python type."""
return duration_pb2.Duration

def to_protobuf_message(self, python_value: dt.timedelta) -> duration_pb2.Duration:
"""Convert the Python dt.timedelta to a protobuf duration_pb2.Duration."""
dur = self.protobuf_message()
dur.FromTimedelta(python_value)
return dur

def to_python_value(self, protobuf_message: duration_pb2.Duration) -> dt.timedelta:
"""Convert the protobuf timestamp_pb2.Timestamp to a Python dt.timedelta."""
return protobuf_message.ToTimedelta()


class BoolCollectionConverter(Converter[Collection[bool], panel_types_pb2.BoolCollection]):
"""A converter for a Collection of bools."""

Expand Down
24 changes: 23 additions & 1 deletion tests/unit/test_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import numpy as np
import pytest
from google.protobuf import any_pb2, timestamp_pb2, wrappers_pb2
from google.protobuf import any_pb2, duration_pb2, timestamp_pb2, wrappers_pb2
from google.protobuf.message import Message
from ni.panels.v1 import panel_types_pb2
from ni.protobuf.types.scalar_pb2 import ScalarData
Expand Down Expand Up @@ -135,6 +135,16 @@ def test___python_datetime_datetime___to_any___valid_timestamppb2_value() -> Non
assert unpack_dest.ToDatetime() == expected_value


def test___python_datetime_timedelta___to_any___valid_durationpb2_value() -> None:
expected_value = dt.timedelta(days=1, seconds=1, microseconds=1)
result = nipanel._convert.to_any(expected_value)
unpack_dest = duration_pb2.Duration()
_assert_any_and_unpack(result, unpack_dest)

assert isinstance(unpack_dest, duration_pb2.Duration)
assert unpack_dest.ToTimedelta() == expected_value


@pytest.mark.parametrize(
"proto_type, default_value, expected_value",
[
Expand Down Expand Up @@ -199,6 +209,18 @@ def test___timestamppb2_timestamp___from_any___valid_python_value() -> None:
assert result == expected_value


def test___durationpb2_timestamp___from_any___valid_python_value() -> None:
expected_value = dt.timedelta(weeks=1, hours=1, minutes=1)
pb_value = duration_pb2.Duration()
pb_value.FromTimedelta(expected_value)
packed_any = _pack_into_any(pb_value)

result = nipanel._convert.from_any(packed_any)

assert isinstance(result, dt.timedelta)
assert result == expected_value


@pytest.mark.parametrize(
"proto_type, expected_value",
[
Expand Down
Loading