Skip to content

Commit 81a4c57

Browse files
Mike ProsserMike Prosser
authored andcommitted
Merge remote-tracking branch 'origin/main' into users/mprosser/add-more-samples
2 parents 6f6c683 + d2a957b commit 81a4c57

File tree

2 files changed

+122
-12
lines changed

2 files changed

+122
-12
lines changed

src/nipanel/_convert.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
IntCollectionConverter,
2222
StrCollectionConverter,
2323
)
24+
from nipanel.converters.protobuf_types import DoubleAnalogWaveformConverter, ScalarConverter
2425

2526
_logger = logging.getLogger(__name__)
2627

@@ -38,6 +39,9 @@
3839
FloatCollectionConverter(),
3940
IntCollectionConverter(),
4041
StrCollectionConverter(),
42+
# Protobuf Types
43+
DoubleAnalogWaveformConverter(),
44+
ScalarConverter(),
4145
]
4246

4347
_CONVERTIBLE_COLLECTION_TYPES = {
@@ -54,6 +58,12 @@
5458

5559
def to_any(python_value: object) -> any_pb2.Any:
5660
"""Convert a Python object to a protobuf Any."""
61+
best_matching_type = _get_best_matching_type(python_value)
62+
converter = _CONVERTER_FOR_PYTHON_TYPE[best_matching_type]
63+
return converter.to_protobuf_any(python_value)
64+
65+
66+
def _get_best_matching_type(python_value: object) -> str:
5767
underlying_parents = type(python_value).mro() # This covers enum.IntEnum and similar
5868

5969
container_type = None
@@ -86,9 +96,7 @@ def to_any(python_value: object) -> any_pb2.Any:
8696
f"Unsupported type: ({container_type}, {payload_type}) with parents {underlying_parents}. Supported types are: {_SUPPORTED_PYTHON_TYPES}"
8797
)
8898
_logger.debug(f"Best matching type for '{repr(python_value)}' resolved to {best_matching_type}")
89-
90-
converter = _CONVERTER_FOR_PYTHON_TYPE[best_matching_type]
91-
return converter.to_protobuf_any(python_value)
99+
return best_matching_type
92100

93101

94102
def from_any(protobuf_any: any_pb2.Any) -> object:

tests/unit/test_convert.py

Lines changed: 111 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
from typing import Any, Type, Union
1+
from typing import Any, Union
22

3+
import numpy as np
34
import pytest
45
from google.protobuf import any_pb2, wrappers_pb2
6+
from google.protobuf.message import Message
7+
from ni.protobuf.types.scalar_pb2 import ScalarData
58
from ni.pythonpanel.v1 import python_panel_types_pb2
9+
from ni_measurement_plugin_sdk_service._internal.stubs.ni.protobuf.types.waveform_pb2 import (
10+
DoubleAnalogWaveform,
11+
)
12+
from nitypes.scalar import Scalar
13+
from nitypes.waveform import AnalogWaveform
614
from typing_extensions import TypeAlias
715

816
import nipanel._convert
@@ -26,7 +34,52 @@
2634

2735

2836
# ========================================================
29-
# Python to Protobuf
37+
# _get_best_matching_type() tests
38+
# ========================================================
39+
@pytest.mark.parametrize(
40+
"python_object, expected_type_string",
41+
[
42+
(False, "bool"),
43+
(b"mystr", "bytes"),
44+
(456.2, "float"),
45+
(123, "int"),
46+
("mystr", "str"),
47+
([False, False], "Collection.bool"),
48+
([b"mystr", b"mystr"], "Collection.bytes"),
49+
([456.2, 1.0], "Collection.float"),
50+
([123, 456], "Collection.int"),
51+
(["mystr", "mystr"], "Collection.str"),
52+
((False, False), "Collection.bool"),
53+
((b"mystr", b"mystr"), "Collection.bytes"),
54+
((456.2, 1.0), "Collection.float"),
55+
((123, 456), "Collection.int"),
56+
(("mystr", "mystr"), "Collection.str"),
57+
((False, False), "Collection.bool"),
58+
((b"mystr", b"mystr"), "Collection.bytes"),
59+
((456.2, 1.0), "Collection.float"),
60+
((123, 456), "Collection.int"),
61+
(("mystr", "mystr"), "Collection.str"),
62+
(set([False, True]), "Collection.bool"),
63+
(set([b"mystr", b"mystr2"]), "Collection.bytes"),
64+
(set([456.2, 1.0]), "Collection.float"),
65+
(set([123, 456]), "Collection.int"),
66+
(set(["mystr", "mystr2"]), "Collection.str"),
67+
(frozenset([False, True]), "Collection.bool"),
68+
(frozenset([b"mystr", b"mystr2"]), "Collection.bytes"),
69+
(frozenset([456.2, 1.0]), "Collection.float"),
70+
(frozenset([123, 456]), "Collection.int"),
71+
(frozenset(["mystr", "mystr2"]), "Collection.str"),
72+
],
73+
)
74+
def test___various_python_objects___get_best_matching_type___returns_correct_type_string(
75+
python_object: object, expected_type_string: str
76+
) -> None:
77+
type_string = nipanel._convert._get_best_matching_type(python_object)
78+
assert type_string == expected_type_string
79+
80+
81+
# ========================================================
82+
# Built-in Types: Python to Protobuf
3083
# ========================================================
3184
@pytest.mark.parametrize(
3285
"proto_type, default_value, expected_value",
@@ -39,7 +92,7 @@
3992
],
4093
)
4194
def test___python_builtin_scalar___to_any___valid_wrapperpb2_value(
42-
proto_type: Type[_AnyWrappersPb2], default_value: Any, expected_value: Any
95+
proto_type: type[_AnyWrappersPb2], default_value: Any, expected_value: Any
4396
) -> None:
4497
result = nipanel._convert.to_any(expected_value)
4598
unpack_dest = proto_type(value=default_value)
@@ -60,7 +113,7 @@ def test___python_builtin_scalar___to_any___valid_wrapperpb2_value(
60113
],
61114
)
62115
def test___python_panel_collection___to_any___valid_paneltype_value(
63-
proto_type: Type[_AnyPanelPbTypes], default_value: Any, expected_value: Any
116+
proto_type: type[_AnyPanelPbTypes], default_value: Any, expected_value: Any
64117
) -> None:
65118
result = nipanel._convert.to_any(expected_value)
66119
unpack_dest = proto_type(values=default_value)
@@ -71,7 +124,7 @@ def test___python_panel_collection___to_any___valid_paneltype_value(
71124

72125

73126
# ========================================================
74-
# Protobuf to Python
127+
# Built-in Types: Protobuf to Python
75128
# ========================================================
76129
@pytest.mark.parametrize(
77130
"proto_type, expected_value",
@@ -84,7 +137,7 @@ def test___python_panel_collection___to_any___valid_paneltype_value(
84137
],
85138
)
86139
def test___wrapperpb2_value___from_any___valid_python_value(
87-
proto_type: Type[_AnyWrappersPb2], expected_value: Any
140+
proto_type: type[_AnyWrappersPb2], expected_value: Any
88141
) -> None:
89142
pb_value = proto_type(value=expected_value)
90143
packed_any = _pack_into_any(pb_value)
@@ -106,7 +159,7 @@ def test___wrapperpb2_value___from_any___valid_python_value(
106159
],
107160
)
108161
def test___paneltype_value___from_any___valid_python_value(
109-
proto_type: Type[_AnyPanelPbTypes], expected_value: Any
162+
proto_type: type[_AnyPanelPbTypes], expected_value: Any
110163
) -> None:
111164
pb_value = proto_type(values=expected_value)
112165
packed_any = _pack_into_any(pb_value)
@@ -117,15 +170,64 @@ def test___paneltype_value___from_any___valid_python_value(
117170
assert result == expected_value
118171

119172

173+
# ========================================================
174+
# Protobuf Types: Python to Protobuf
175+
# ========================================================
176+
def test___python_scalar_object___to_any___valid_scalar_data_value() -> None:
177+
scalar_obj = Scalar(1.0, "amps")
178+
result = nipanel._convert.to_any(scalar_obj)
179+
unpack_dest = ScalarData()
180+
_assert_any_and_unpack(result, unpack_dest)
181+
182+
assert isinstance(unpack_dest, ScalarData)
183+
assert unpack_dest.double_value == 1.0
184+
assert unpack_dest.units == "amps"
185+
186+
187+
def test___python_analog_waveform___to_any___valid_double_analog_waveform() -> None:
188+
wfm_obj = AnalogWaveform(3)
189+
result = nipanel._convert.to_any(wfm_obj)
190+
unpack_dest = DoubleAnalogWaveform()
191+
_assert_any_and_unpack(result, unpack_dest)
192+
193+
assert isinstance(unpack_dest, DoubleAnalogWaveform)
194+
assert list(unpack_dest.y_data) == [0.0, 0.0, 0.0]
195+
196+
197+
# ========================================================
198+
# Protobuf Types: Protobuf to Python
199+
# ========================================================
200+
def test___scalar_data___from_any___valid_python_scalar_object() -> None:
201+
pb_value = ScalarData(units="amps", double_value=1.0)
202+
packed_any = _pack_into_any(pb_value)
203+
204+
result = nipanel._convert.from_any(packed_any)
205+
206+
assert isinstance(result, Scalar)
207+
assert result.value == 1.0
208+
assert result.units == "amps"
209+
210+
211+
def test___double_analog_waveform___from_any___valid_python_analog_waveform() -> None:
212+
pb_value = DoubleAnalogWaveform(y_data=[0.0, 0.0, 0.0])
213+
packed_any = _pack_into_any(pb_value)
214+
215+
result = nipanel._convert.from_any(packed_any)
216+
217+
assert isinstance(result, AnalogWaveform)
218+
assert result.sample_count == result.capacity == len(result.raw_data) == 3
219+
assert result.dtype == np.float64
220+
221+
120222
# ========================================================
121223
# Pack/Unpack Helpers
122224
# ========================================================
123-
def _assert_any_and_unpack(packed_message: any_pb2.Any, unpack_destination: Any) -> None:
225+
def _assert_any_and_unpack(packed_message: any_pb2.Any, unpack_destination: Message) -> None:
124226
assert isinstance(packed_message, any_pb2.Any)
125227
assert packed_message.Unpack(unpack_destination)
126228

127229

128-
def _pack_into_any(proto_value: Union[_AnyWrappersPb2, _AnyPanelPbTypes]) -> any_pb2.Any:
230+
def _pack_into_any(proto_value: Message) -> any_pb2.Any:
129231
as_any = any_pb2.Any()
130232
as_any.Pack(proto_value)
131233
return as_any

0 commit comments

Comments
 (0)