Skip to content

Commit c9c3d74

Browse files
author
Michael Johansen
committed
Add Vector converter
Signed-off-by: Michael Johansen <[email protected]>
1 parent b6eafd7 commit c9c3d74

File tree

7 files changed

+100
-111
lines changed

7 files changed

+100
-111
lines changed

examples/all_types/define_types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import numpy as np
77
from nitypes.complex import ComplexInt32DType
88
from nitypes.scalar import Scalar
9+
from nitypes.vector import Vector
910
from nitypes.waveform import AnalogWaveform, ComplexWaveform, DigitalWaveform, Spectrum
1011

1112

@@ -84,6 +85,7 @@ class MyMixedEnum(enum.Enum):
8485
"mixedenum": MyMixedEnum.VALUE2,
8586
# NI types
8687
"nitypes_Scalar": Scalar(42, "m"),
88+
"nitypes_Vector": Vector([1, 2, 3], "volts"),
8789
"nitypes_DoubleAnalogWaveform": AnalogWaveform.from_array_1d(np.array([1.0, 2.0, 3.0])),
8890
"nitypes_I16AnalogWaveform": AnalogWaveform.from_array_1d(np.array([1, 2, 3]), dtype=np.int16),
8991
"nitypes_DoubleComplexWaveform": ComplexWaveform(2, np.complex128),

poetry.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ streamlit = ">=1.24"
1616
nitypes = {version=">=0.1.0dev8", allow-prereleases=true}
1717
numpy = ">=1.22"
1818
debugpy = ">=1.8.1"
19-
ni-protobuf-types = { version = ">=0.1.0dev2", allow-prereleases = true }
19+
ni-protobuf-types = { version = ">=0.1.0dev3", allow-prereleases = true }
2020
ni-panels-v1-proto = { version = ">=0.1.0dev1", allow-prereleases = true }
2121

2222
[tool.poetry.group.dev.dependencies]

src/nipanel/_convert.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
IntCollectionConverter,
3434
ScalarConverter,
3535
StrCollectionConverter,
36+
VectorConverter,
3637
)
3738

3839
_logger = logging.getLogger(__name__)
@@ -61,6 +62,7 @@
6162
IntCollectionConverter(),
6263
StrCollectionConverter(),
6364
ScalarConverter(),
65+
VectorConverter(),
6466
]
6567

6668
_CONVERTIBLE_COLLECTION_TYPES = {

src/nipanel/converters/protobuf_types.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@
1313
precision_timestamp_conversion,
1414
scalar_conversion,
1515
scalar_pb2,
16+
vector_pb2,
17+
vector_conversion,
1618
waveform_conversion,
1719
waveform_pb2,
1820
)
1921
from nitypes.complex import ComplexInt32Base
2022
from nitypes.scalar import Scalar
23+
from nitypes.vector import Vector
2124
from nitypes.waveform import AnalogWaveform, ComplexWaveform, DigitalWaveform, Spectrum
2225
from typing_extensions import TypeAlias
2326

@@ -422,3 +425,25 @@ def to_protobuf_message(self, python_value: Scalar[_AnyScalarType]) -> scalar_pb
422425
def to_python_value(self, protobuf_message: scalar_pb2.Scalar) -> Scalar[_AnyScalarType]:
423426
"""Convert the protobuf message to a Python Scalar."""
424427
return scalar_conversion.scalar_from_protobuf(protobuf_message)
428+
429+
430+
class VectorConverter(Converter[Vector[_AnyScalarType], vector_pb2.Vector]):
431+
"""A converter for Vector objects."""
432+
433+
@property
434+
def python_type(self) -> type:
435+
"""The Python type that this converter handles."""
436+
return Vector
437+
438+
@property
439+
def protobuf_message(self) -> Type[vector_pb2.Vector]:
440+
"""The type-specific protobuf message for the Python type."""
441+
return vector_pb2.Vector
442+
443+
def to_protobuf_message(self, python_value: Vector[Any]) -> vector_pb2.Vector:
444+
"""Convert the Python Scalar to a protobuf scalar_pb2.Scalar."""
445+
return vector_conversion.vector_to_protobuf(python_value)
446+
447+
def to_python_value(self, protobuf_message: vector_pb2.Vector) -> Vector[_AnyScalarType]:
448+
"""Convert the protobuf message to a Python Scalar."""
449+
return vector_conversion.vector_from_protobuf(protobuf_message)

tests/unit/test_convert.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
import pytest
66
from google.protobuf import any_pb2, duration_pb2, timestamp_pb2, wrappers_pb2
77
from google.protobuf.message import Message
8-
from ni.protobuf.types import array_pb2, attribute_value_pb2, scalar_pb2, waveform_pb2
8+
from ni.protobuf.types import array_pb2, attribute_value_pb2, scalar_pb2, vector_pb2, waveform_pb2
99
from nitypes.complex import ComplexInt32DType
1010
from nitypes.scalar import Scalar
11+
from nitypes.vector import Vector
1112
from nitypes.waveform import AnalogWaveform, ComplexWaveform, DigitalWaveform, Spectrum
1213
from typing_extensions import TypeAlias
1314

@@ -131,6 +132,8 @@
131132
),
132133
(DigitalWaveform(10, 2, np.bool, False), "nitypes.waveform.DigitalWaveform"),
133134
(Spectrum(10, np.float64), "nitypes.waveform.Spectrum"),
135+
(Scalar("one"), "nitypes.scalar.Scalar"),
136+
(Vector([1, 2, 3]), "nitypes.vector.Vector"),
134137
],
135138
)
136139
def test___various_python_objects___get_best_matching_type___returns_correct_type_string(
@@ -274,6 +277,17 @@ def test___python_scalar_object___to_any___valid_scalar_proto() -> None:
274277
assert unpack_dest.attributes["NI_UnitDescription"].string_value == "amps"
275278

276279

280+
def test___python_vector_object___to_any___valid_vector_proto() -> None:
281+
vector_obj = Vector([1.0, 2.0, 3.0], "amps")
282+
result = nipanel._convert.to_any(vector_obj)
283+
unpack_dest = vector_pb2.Vector()
284+
_assert_any_and_unpack(result, unpack_dest)
285+
286+
assert isinstance(unpack_dest, vector_pb2.Vector)
287+
assert list(unpack_dest.double_array.values) == [1.0, 2.0, 3.0]
288+
assert unpack_dest.attributes["NI_UnitDescription"].string_value == "amps"
289+
290+
277291
def test___python_float64_analog_waveform___to_any___valid_double_analog_waveform_proto() -> None:
278292
wfm_obj = AnalogWaveform(3, np.float64)
279293
result = nipanel._convert.to_any(wfm_obj)
@@ -449,6 +463,20 @@ def test___scalar_proto___from_any___valid_python_scalar() -> None:
449463
assert result.units == "amps"
450464

451465

466+
def test___vector_proto___from_any___valid_python_vector() -> None:
467+
attrs = {"NI_UnitDescription": attribute_value_pb2.AttributeValue(string_value="amps")}
468+
pb_value = vector_pb2.Vector(
469+
attributes=attrs, double_array=array_pb2.DoubleArray(values=[1.0, 2.0, 3.0]),
470+
)
471+
packed_any = _pack_into_any(pb_value)
472+
473+
result = nipanel._convert.from_any(packed_any)
474+
475+
assert isinstance(result, Vector)
476+
assert list(result) == [1.0, 2.0, 3.0]
477+
assert result.units == "amps"
478+
479+
452480
def test___double_analog_waveform_proto___from_any___valid_python_float64_analog_waveform() -> None:
453481
pb_value = waveform_pb2.DoubleAnalogWaveform(y_data=[0.0, 0.0, 0.0])
454482
packed_any = _pack_into_any(pb_value)
Lines changed: 37 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import pytest
2-
from ni.protobuf.types import array_pb2, attribute_value_pb2, scalar_pb2
2+
from ni.protobuf.types import array_pb2, attribute_value_pb2, scalar_pb2, vector_pb2
33
from nitypes.scalar import Scalar
4+
from nitypes.vector import Vector
45
from typing_extensions import Mapping
56

6-
from nipanel.converters.protobuf_types import Double2DArrayConverter, ScalarConverter
7+
from nipanel.converters.protobuf_types import (
8+
Double2DArrayConverter,
9+
ScalarConverter,
10+
VectorConverter,
11+
)
712

813

914
# ========================================================
@@ -84,47 +89,8 @@ def test___double2darray_empty_data___convert___returns_empty_list() -> None:
8489
# ========================================================
8590
# Scalar: Protobuf to Python
8691
# ========================================================
87-
def test___bool_scalar_protobuf___convert___valid_bool_scalar() -> None:
88-
attrs = _units_to_scalar_attribute_map("volts")
89-
protobuf_value = scalar_pb2.Scalar(attributes=attrs)
90-
protobuf_value.bool_value = True
91-
92-
converter = ScalarConverter()
93-
python_value = converter.to_python_value(protobuf_value)
94-
95-
assert isinstance(python_value.value, bool)
96-
assert python_value.value is True
97-
assert python_value.units == "volts"
98-
99-
100-
def test___int32_scalar_protobuf___convert___valid_int_scalar() -> None:
101-
attrs = _units_to_scalar_attribute_map("volts")
102-
protobuf_value = scalar_pb2.Scalar(attributes=attrs)
103-
protobuf_value.sint32_value = 10
104-
105-
converter = ScalarConverter()
106-
python_value = converter.to_python_value(protobuf_value)
107-
108-
assert isinstance(python_value.value, int)
109-
assert python_value.value == 10
110-
assert python_value.units == "volts"
111-
112-
113-
def test___double_scalar_protobuf___convert___valid_float_scalar() -> None:
114-
attrs = _units_to_scalar_attribute_map("volts")
115-
protobuf_value = scalar_pb2.Scalar(attributes=attrs)
116-
protobuf_value.double_value = 20.0
117-
118-
converter = ScalarConverter()
119-
python_value = converter.to_python_value(protobuf_value)
120-
121-
assert isinstance(python_value.value, float)
122-
assert python_value.value == 20.0
123-
assert python_value.units == "volts"
124-
125-
12692
def test___string_scalar_protobuf___convert___valid_str_scalar() -> None:
127-
attrs = _units_to_scalar_attribute_map("volts")
93+
attrs = _units_to_attribute_map("volts")
12894
protobuf_value = scalar_pb2.Scalar(attributes=attrs)
12995
protobuf_value.string_value = "value"
13096

@@ -136,65 +102,9 @@ def test___string_scalar_protobuf___convert___valid_str_scalar() -> None:
136102
assert python_value.units == "volts"
137103

138104

139-
def test___scalar_protobuf_value_unset___convert___throws_type_error() -> None:
140-
attrs = _units_to_scalar_attribute_map("volts")
141-
protobuf_value = scalar_pb2.Scalar(attributes=attrs)
142-
143-
converter = ScalarConverter()
144-
with pytest.raises(ValueError) as exc:
145-
_ = converter.to_python_value(protobuf_value)
146-
147-
assert exc.value.args[0].startswith("Could not determine the data type of 'value'.")
148-
149-
150-
def test___scalar_protobuf_units_unset___convert___python_units_blank() -> None:
151-
protobuf_value = scalar_pb2.Scalar()
152-
protobuf_value.bool_value = True
153-
154-
converter = ScalarConverter()
155-
python_value = converter.to_python_value(protobuf_value)
156-
157-
assert isinstance(python_value.value, bool)
158-
assert python_value.value is True
159-
assert python_value.units == ""
160-
161-
162105
# ========================================================
163106
# Scalar: Python to Protobuf
164107
# ========================================================
165-
def test___bool_scalar___convert___valid_bool_scalar_protobuf() -> None:
166-
python_value = Scalar(True, "volts")
167-
168-
converter = ScalarConverter()
169-
protobuf_value = converter.to_protobuf_message(python_value)
170-
171-
assert protobuf_value.WhichOneof("value") == "bool_value"
172-
assert protobuf_value.bool_value is True
173-
assert protobuf_value.attributes["NI_UnitDescription"].string_value == "volts"
174-
175-
176-
def test___int_scalar___convert___valid_int32_scalar_protobuf() -> None:
177-
python_value = Scalar(10, "volts")
178-
179-
converter = ScalarConverter()
180-
protobuf_value = converter.to_protobuf_message(python_value)
181-
182-
assert protobuf_value.WhichOneof("value") == "sint32_value"
183-
assert protobuf_value.sint32_value == 10
184-
assert protobuf_value.attributes["NI_UnitDescription"].string_value == "volts"
185-
186-
187-
def test___float_scalar___convert___valid_double_scalar_protobuf() -> None:
188-
python_value = Scalar(20.0, "volts")
189-
190-
converter = ScalarConverter()
191-
protobuf_value = converter.to_protobuf_message(python_value)
192-
193-
assert protobuf_value.WhichOneof("value") == "double_value"
194-
assert protobuf_value.double_value == 20.0
195-
assert protobuf_value.attributes["NI_UnitDescription"].string_value == "volts"
196-
197-
198108
def test___str_scalar___convert___valid_string_scalar_protobuf() -> None:
199109
python_value = Scalar("value", "volts")
200110

@@ -206,17 +116,39 @@ def test___str_scalar___convert___valid_string_scalar_protobuf() -> None:
206116
assert protobuf_value.attributes["NI_UnitDescription"].string_value == "volts"
207117

208118

209-
def test___scalar_units_unset___convert___protobuf_units_blank() -> None:
210-
python_value = Scalar(10)
119+
# ========================================================
120+
# Vector: Protobuf to Python
121+
# ========================================================
122+
def test___string_vector_protobuf___convert___valid_str_vector() -> None:
123+
attrs = _units_to_attribute_map("volts")
124+
protobuf_value = vector_pb2.Vector(
125+
attributes=attrs,
126+
string_array=array_pb2.StringArray(values=["one", "two", "three"]),
127+
)
128+
129+
converter = VectorConverter()
130+
python_value = converter.to_python_value(protobuf_value)
211131

212-
converter = ScalarConverter()
132+
assert isinstance(python_value, Vector)
133+
assert list(python_value) == ["one", "two", "three"]
134+
assert python_value.units == "volts"
135+
136+
137+
# ========================================================
138+
# Vector: Python to Protobuf
139+
# ========================================================
140+
def test___str_vector___convert___valid_string_vector_protobuf() -> None:
141+
python_value = Vector(["one", "two", "three"], "volts")
142+
143+
converter = VectorConverter()
213144
protobuf_value = converter.to_protobuf_message(python_value)
214145

215-
assert protobuf_value.WhichOneof("value") == "sint32_value"
216-
assert protobuf_value.sint32_value == 10
217-
assert protobuf_value.attributes["NI_UnitDescription"].string_value == ""
146+
assert isinstance(protobuf_value, vector_pb2.Vector)
147+
assert protobuf_value.WhichOneof("value") == "string_array"
148+
assert list(protobuf_value.string_array.values) == ["one", "two", "three"]
149+
assert protobuf_value.attributes["NI_UnitDescription"].string_value == "volts"
218150

219151

220-
def _units_to_scalar_attribute_map(units: str) -> Mapping[str, attribute_value_pb2.AttributeValue]:
152+
def _units_to_attribute_map(units: str) -> Mapping[str, attribute_value_pb2.AttributeValue]:
221153
value = attribute_value_pb2.AttributeValue(string_value=units)
222154
return {"NI_UnitDescription": value}

0 commit comments

Comments
 (0)