ni.datastore 2.0.0.dev0__tar.gz → 2.0.0.dev1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/PKG-INFO +4 -4
  2. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/README.md +1 -1
  3. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/pyproject.toml +7 -3
  4. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/data/_data_store_client.py +25 -27
  5. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/data/_grpc_conversion.py +174 -49
  6. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/data/_types/_published_measurement.py +7 -4
  7. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/data/_types/_step.py +6 -5
  8. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/data/_types/_test_result.py +6 -5
  9. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_metadata_store_client.py +13 -12
  10. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/_alias.py +1 -1
  11. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/_hardware_item.py +4 -3
  12. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/_metadata_items.py +4 -3
  13. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/_operator.py +4 -3
  14. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/_software_item.py +4 -3
  15. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/_test.py +4 -3
  16. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/_test_adapter.py +4 -3
  17. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/_test_description.py +4 -3
  18. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/_test_station.py +4 -3
  19. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/_uut.py +4 -3
  20. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/_uut_instance.py +4 -3
  21. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/LICENSE +0 -0
  22. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/__init__.py +0 -0
  23. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/data/__init__.py +0 -0
  24. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/data/_types/__init__.py +0 -0
  25. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/data/_types/_error_information.py +0 -0
  26. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/data/_types/_outcome.py +0 -0
  27. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/data/_types/_published_condition.py +0 -0
  28. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/data/_types/py.typed +0 -0
  29. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/data/py.typed +0 -0
  30. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/__init__.py +0 -0
  31. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_grpc_conversion.py +0 -0
  32. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/__init__.py +0 -0
  33. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/_alias_target_type.py +0 -0
  34. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/_extension_schema.py +0 -0
  35. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/_types/py.typed +0 -0
  36. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/metadata/py.typed +0 -0
  37. {ni_datastore-2.0.0.dev0 → ni_datastore-2.0.0.dev1}/src/ni/datastore/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ni.datastore
3
- Version: 2.0.0.dev0
3
+ Version: 2.0.0.dev1
4
4
  Summary: APIs for publishing and retrieving data from NI Measurement Data Services
5
5
  License: MIT
6
6
  Keywords: datastore
@@ -24,9 +24,9 @@ Classifier: Programming Language :: Python :: 3.13
24
24
  Classifier: Programming Language :: Python :: 3.14
25
25
  Classifier: Programming Language :: Python :: Implementation :: CPython
26
26
  Requires-Dist: hightime (>=1.0.0)
27
- Requires-Dist: ni-measurements-data-v1-client (>=1.1.0dev0)
27
+ Requires-Dist: ni-measurements-data-v1-client (>=1.1.0)
28
28
  Requires-Dist: ni-measurements-metadata-v1-client (>=1.0.0)
29
- Requires-Dist: ni-protobuf-types (>=1.1.0)
29
+ Requires-Dist: ni-protobuf-types (>=1.2.0)
30
30
  Requires-Dist: protobuf (>=4.21)
31
31
  Project-URL: Documentation, https://datastorepython.readthedocs.io/en/latest/
32
32
  Project-URL: Repository, https://github.com/ni/datastore-python
@@ -65,7 +65,7 @@ NI created and supports this package.
65
65
  ## Installation
66
66
 
67
67
  As a prerequisite to using the `ni.datastore` module, you must install Measurement Data Services
68
- Software 2026 Q1 or later on your system. You can download and install this software using
68
+ Software 2026 Q3 or later on your system. You can download and install this software using
69
69
  [NI Package Manager](https://www.ni.com/en/support/downloads/software-products/download.package-manager.html).
70
70
 
71
71
  You can directly install the `ni.datastore` package using `pip` or by listing it as a
@@ -31,7 +31,7 @@ NI created and supports this package.
31
31
  ## Installation
32
32
 
33
33
  As a prerequisite to using the `ni.datastore` module, you must install Measurement Data Services
34
- Software 2026 Q1 or later on your system. You can download and install this software using
34
+ Software 2026 Q3 or later on your system. You can download and install this software using
35
35
  [NI Package Manager](https://www.ni.com/en/support/downloads/software-products/download.package-manager.html).
36
36
 
37
37
  You can directly install the `ni.datastore` package using `pip` or by listing it as a
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ni.datastore"
3
- version = "2.0.0.dev0"
3
+ version = "2.0.0.dev1"
4
4
  license = "MIT"
5
5
  description = "APIs for publishing and retrieving data from NI Measurement Data Services"
6
6
  authors = [{name = "NI", email = "opensource@ni.com"}]
@@ -40,9 +40,9 @@ requires-poetry = '>=2.1,<3.0'
40
40
  [tool.poetry.dependencies]
41
41
  python = "^3.10"
42
42
  protobuf = {version=">=4.21"}
43
- ni-measurements-data-v1-client = { version = ">=1.1.0dev0", allow-prereleases = true }
43
+ ni-measurements-data-v1-client = { version = ">=1.1.0", allow-prereleases = true }
44
44
  ni-measurements-metadata-v1-client = { version = ">=1.0.0" }
45
- ni-protobuf-types = { version = ">=1.1.0" }
45
+ ni-protobuf-types = { version = ">=1.2.0", allow-prereleases = true }
46
46
  hightime = { version = ">=1.0.0" }
47
47
 
48
48
  [tool.poetry.group.dev.dependencies]
@@ -113,3 +113,7 @@ exclude = ["**/*_pb2_grpc.py", "**/*_pb2_grpc.pyi", "**/*_pb2.py", "**/*_pb2.pyi
113
113
  [tool.pytest.ini_options]
114
114
  addopts = "--doctest-modules --doctest-plus --strict-markers"
115
115
  testpaths = ["tests"]
116
+ norecursedirs = [".venv"]
117
+ filterwarnings = [
118
+ "ignore:cannot collect test class.*because it has a __init__ constructor:pytest.PytestCollectionWarning",
119
+ ]
@@ -7,25 +7,10 @@ import sys
7
7
  from collections.abc import Iterable, Sequence
8
8
  from threading import Lock
9
9
  from types import TracebackType
10
- from typing import TYPE_CHECKING, Type, TypeVar, overload
10
+ from typing import overload, Type, TYPE_CHECKING, TypeVar
11
11
 
12
12
  import hightime as ht
13
13
  from grpc import Channel
14
- from ni.datastore.data._grpc_conversion import (
15
- convert_read_condition_response_from_protobuf,
16
- convert_read_measurement_response_from_protobuf,
17
- get_publish_measurement_timestamp,
18
- populate_publish_condition_batch_request_values,
19
- populate_publish_condition_request_value,
20
- populate_publish_measurement_batch_request_values,
21
- populate_publish_measurement_request_value,
22
- )
23
- from ni.datastore.data._types._error_information import ErrorInformation
24
- from ni.datastore.data._types._outcome import Outcome
25
- from ni.datastore.data._types._published_condition import PublishedCondition
26
- from ni.datastore.data._types._published_measurement import PublishedMeasurement
27
- from ni.datastore.data._types._step import Step
28
- from ni.datastore.data._types._test_result import TestResult
29
14
  from ni.measurementlink.discovery.v1.client import DiscoveryClient
30
15
  from ni.measurements.data.v1.client import DataStoreClient as DataStoreServiceClient
31
16
  from ni.measurements.data.v1.data_store_service_pb2 import (
@@ -51,6 +36,22 @@ from ni.protobuf.types.precision_timestamp_conversion import (
51
36
  )
52
37
  from ni_grpc_extensions.channelpool import GrpcChannelPool
53
38
 
39
+ from ni.datastore.data._grpc_conversion import (
40
+ convert_measurement_timestamp_to_protobuf,
41
+ convert_read_condition_response_from_protobuf,
42
+ convert_read_measurement_response_from_protobuf,
43
+ populate_publish_condition_batch_request_values,
44
+ populate_publish_condition_request_value,
45
+ populate_publish_measurement_batch_request_values,
46
+ populate_publish_measurement_request_value,
47
+ )
48
+ from ni.datastore.data._types._error_information import ErrorInformation
49
+ from ni.datastore.data._types._outcome import Outcome
50
+ from ni.datastore.data._types._published_condition import PublishedCondition
51
+ from ni.datastore.data._types._published_measurement import PublishedMeasurement
52
+ from ni.datastore.data._types._step import Step
53
+ from ni.datastore.data._types._test_result import TestResult
54
+
54
55
  if TYPE_CHECKING:
55
56
  if sys.version_info >= (3, 11):
56
57
  from typing import Self
@@ -237,8 +238,8 @@ class DataStoreClient:
237
238
  step_id: The ID of the step associated with this measurement. This
238
239
  value is expected to be a parsable GUID.
239
240
 
240
- timestamp: The timestamp of the measurement. If None, the current
241
- time will be used.
241
+ timestamp: The timestamp of the measurement. If None, no timestamp
242
+ will be specified.
242
243
 
243
244
  outcome: The outcome of the measurement (PASSED, FAILED,
244
245
  INDETERMINATE, or UNSPECIFIED).
@@ -274,11 +275,9 @@ class DataStoreClient:
274
275
  test_adapter_ids=test_adapter_ids,
275
276
  software_item_ids=software_item_ids,
276
277
  notes=notes,
278
+ timestamp=convert_measurement_timestamp_to_protobuf(timestamp),
277
279
  )
278
280
  populate_publish_measurement_request_value(publish_request, value)
279
- publish_request.timestamp.CopyFrom(
280
- get_publish_measurement_timestamp(publish_request, timestamp)
281
- )
282
281
  publish_response = self._get_data_store_client().publish_measurement(publish_request)
283
282
  return publish_response.measurement_id
284
283
 
@@ -295,7 +294,7 @@ class DataStoreClient:
295
294
  software_item_ids: Iterable[str] = tuple(),
296
295
  notes: str = "",
297
296
  ) -> Sequence[str]:
298
- """Publish multiple scalar measurements at once for parametric sweeps.
297
+ """Publish multiple measurements at once for parametric sweeps.
299
298
 
300
299
  Args:
301
300
  name: The name used for associating/grouping
@@ -303,7 +302,7 @@ class DataStoreClient:
303
302
  iterations. For example, "Temperature" can be used for
304
303
  associating temperature readings across multiple iterations.
305
304
 
306
- values: The values of the (scalar) measurement being published
305
+ values: The values of the measurement being published
307
306
  across N iterations.
308
307
 
309
308
  step_id: The ID of the step associated with this measurement. This
@@ -339,10 +338,9 @@ class DataStoreClient:
339
338
  notes: Any notes to be associated with the published measurements.
340
339
 
341
340
  Returns:
342
- Sequence[str]: The ids of the published measurement ids.
343
- NOTE: Using a Sequence is for future flexibility.
344
- This sequence will currently always have a single measurement id
345
- returned.
341
+ Sequence[str]: The IDs of the corresponding PublishedMeasurements. A single
342
+ ID will be returned when publishing scalar measurement values.
343
+ N IDs will be returned when publishing (N) non-scalar measurement values.
346
344
  """
347
345
  publish_request = PublishMeasurementBatchRequest(
348
346
  name=name,
@@ -2,9 +2,9 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import datetime as std_datetime
6
5
  import logging
7
- from typing import Iterable, cast
6
+ from itertools import chain
7
+ from typing import Any, Callable, cast, Iterable
8
8
 
9
9
  import hightime as ht
10
10
  import numpy as np
@@ -49,6 +49,129 @@ from nitypes.xy_data import XYData
49
49
  _logger = logging.getLogger(__name__)
50
50
 
51
51
 
52
+ def _copy_batch_values(
53
+ repeated_field: Any,
54
+ batch_values: Iterable[object],
55
+ is_supported: Callable[[object], bool],
56
+ convert_value: Callable[[Any], Any],
57
+ error_message: str,
58
+ ) -> None:
59
+ for value in batch_values:
60
+ if not is_supported(value):
61
+ raise TypeError(error_message)
62
+ repeated_field.add().CopyFrom(convert_value(value))
63
+
64
+
65
+ def _populate_vector_batch_values(
66
+ publish_request: PublishMeasurementBatchRequest, values: Iterable[object]
67
+ ) -> None:
68
+ _copy_batch_values(
69
+ publish_request.vector_values.vectors,
70
+ values,
71
+ lambda value: isinstance(value, Vector),
72
+ vector_to_protobuf,
73
+ "Unsupported iterable: all values must be Vector.",
74
+ )
75
+
76
+
77
+ def _populate_analog_waveform_batch_values(
78
+ publish_request: PublishMeasurementBatchRequest,
79
+ first_value: AnalogWaveform[Any],
80
+ values: Iterable[object],
81
+ ) -> None:
82
+ if first_value.dtype == np.float64:
83
+ _copy_batch_values(
84
+ publish_request.double_analog_waveform_values.waveforms,
85
+ values,
86
+ lambda value: isinstance(value, AnalogWaveform) and value.dtype == np.float64,
87
+ float64_analog_waveform_to_protobuf,
88
+ "Unsupported iterable: all values must be float64 AnalogWaveform.",
89
+ )
90
+ return
91
+ elif first_value.dtype == np.int16:
92
+ _copy_batch_values(
93
+ publish_request.i16_analog_waveform_values.waveforms,
94
+ values,
95
+ lambda value: isinstance(value, AnalogWaveform) and value.dtype == np.int16,
96
+ int16_analog_waveform_to_protobuf,
97
+ "Unsupported iterable: all values must be int16 AnalogWaveform.",
98
+ )
99
+ return
100
+ raise TypeError(f"Unsupported AnalogWaveform dtype: {first_value.dtype}")
101
+
102
+
103
+ def _populate_complex_waveform_batch_values(
104
+ publish_request: PublishMeasurementBatchRequest,
105
+ first_value: ComplexWaveform[Any],
106
+ values: Iterable[object],
107
+ ) -> None:
108
+ if first_value.dtype == np.complex128:
109
+ _copy_batch_values(
110
+ publish_request.double_complex_waveform_values.waveforms,
111
+ values,
112
+ lambda value: isinstance(value, ComplexWaveform) and value.dtype == np.complex128,
113
+ float64_complex_waveform_to_protobuf,
114
+ "Unsupported iterable: all values must be complex128 ComplexWaveform.",
115
+ )
116
+ return
117
+ if first_value.dtype == ComplexInt32DType:
118
+ _copy_batch_values(
119
+ publish_request.i16_complex_waveform_values.waveforms,
120
+ values,
121
+ lambda value: isinstance(value, ComplexWaveform) and value.dtype == ComplexInt32DType,
122
+ int16_complex_waveform_to_protobuf,
123
+ "Unsupported iterable: all values must be ComplexWaveform with ComplexInt32DType.",
124
+ )
125
+ return
126
+ raise TypeError(f"Unsupported ComplexWaveform dtype: {first_value.dtype}")
127
+
128
+
129
+ def _populate_spectrum_batch_values(
130
+ publish_request: PublishMeasurementBatchRequest,
131
+ first_value: Spectrum[Any],
132
+ values: Iterable[object],
133
+ ) -> None:
134
+ if first_value.dtype != np.float64:
135
+ raise TypeError(f"Unsupported Spectrum dtype: {first_value.dtype}")
136
+
137
+ _copy_batch_values(
138
+ publish_request.double_spectrum_values.waveforms,
139
+ values,
140
+ lambda value: isinstance(value, Spectrum) and value.dtype == np.float64,
141
+ float64_spectrum_to_protobuf,
142
+ "Unsupported iterable: all values must be float64 Spectrum.",
143
+ )
144
+
145
+
146
+ def _populate_digital_waveform_batch_values(
147
+ publish_request: PublishMeasurementBatchRequest, values: Iterable[object]
148
+ ) -> None:
149
+ _copy_batch_values(
150
+ publish_request.digital_waveform_values.waveforms,
151
+ values,
152
+ lambda value: isinstance(value, DigitalWaveform),
153
+ digital_waveform_to_protobuf,
154
+ "Unsupported iterable: all values must be DigitalWaveform.",
155
+ )
156
+
157
+
158
+ def _populate_xydata_batch_values(
159
+ publish_request: PublishMeasurementBatchRequest,
160
+ first_value: XYData[Any],
161
+ values: Iterable[object],
162
+ ) -> None:
163
+ if first_value.dtype != np.float64:
164
+ raise TypeError(f"Unsupported XYData dtype: {first_value.dtype}")
165
+
166
+ _copy_batch_values(
167
+ publish_request.x_y_data_values.x_y_data,
168
+ values,
169
+ lambda value: isinstance(value, XYData) and value.dtype == np.float64,
170
+ float64_xydata_to_protobuf,
171
+ "Unsupported iterable: all values must be float64 XYData.",
172
+ )
173
+
174
+
52
175
  def populate_publish_condition_request_value(
53
176
  publish_request: PublishConditionRequest, value: object
54
177
  ) -> None:
@@ -76,10 +199,15 @@ def populate_publish_condition_batch_request_values(
76
199
  if isinstance(values, Vector):
77
200
  publish_request.scalar_values.CopyFrom(vector_to_protobuf(values))
78
201
  elif isinstance(values, Iterable):
79
- if not values:
80
- raise ValueError("Cannot publish an empty Iterable.")
202
+ values_iterator = iter(values)
203
+ try:
204
+ first_value = next(values_iterator)
205
+ except StopIteration as exc:
206
+ raise ValueError("Cannot publish an empty Iterable.") from exc
207
+
208
+ all_values = chain([first_value], values_iterator)
81
209
  try:
82
- vector = Vector(values)
210
+ vector = Vector(cast(Iterable[bool | int | float | str], all_values))
83
211
  except (TypeError, ValueError):
84
212
  raise TypeError(
85
213
  f"Unsupported iterable: {values}. Subtype must be bool, float, int, or string."
@@ -139,10 +267,15 @@ def populate_publish_measurement_request_value(
139
267
  else:
140
268
  raise TypeError(f"Unsupported XYData dtype: {value.dtype}")
141
269
  elif isinstance(value, Iterable):
142
- if not value:
143
- raise ValueError("Cannot publish an empty Iterable.")
270
+ value_iterator = iter(value)
144
271
  try:
145
- vector = Vector(value)
272
+ first_item = next(value_iterator)
273
+ except StopIteration as exc:
274
+ raise ValueError("Cannot publish an empty Iterable.") from exc
275
+
276
+ all_items = chain([first_item], value_iterator)
277
+ try:
278
+ vector = Vector(cast(Iterable[bool | int | float | str], all_items))
146
279
  except (TypeError, ValueError):
147
280
  raise TypeError(
148
281
  f"Unsupported iterable: {value}. Subtype must be bool, float, int, or string."
@@ -162,16 +295,34 @@ def populate_publish_measurement_batch_request_values(
162
295
  if isinstance(values, Vector):
163
296
  publish_request.scalar_values.CopyFrom(vector_to_protobuf(values))
164
297
  elif isinstance(values, Iterable):
165
- if not values:
166
- raise ValueError("Cannot publish an empty Iterable.")
298
+ values_iterator = iter(values)
167
299
  try:
168
- vector = Vector(values)
169
- except (TypeError, ValueError):
170
- raise TypeError(
171
- f"Unsupported iterable: {values}. Subtype must be bool, float, int, or string."
172
- )
300
+ first_value = next(values_iterator)
301
+ except StopIteration as exc:
302
+ raise ValueError("Cannot publish an empty Iterable.") from exc
173
303
 
174
- publish_request.scalar_values.CopyFrom(vector_to_protobuf(vector))
304
+ all_values = chain([first_value], values_iterator)
305
+ if isinstance(first_value, Vector):
306
+ _populate_vector_batch_values(publish_request, all_values)
307
+ elif isinstance(first_value, AnalogWaveform):
308
+ _populate_analog_waveform_batch_values(publish_request, first_value, all_values)
309
+ elif isinstance(first_value, ComplexWaveform):
310
+ _populate_complex_waveform_batch_values(publish_request, first_value, all_values)
311
+ elif isinstance(first_value, Spectrum):
312
+ _populate_spectrum_batch_values(publish_request, first_value, all_values)
313
+ elif isinstance(first_value, DigitalWaveform):
314
+ _populate_digital_waveform_batch_values(publish_request, all_values)
315
+ elif isinstance(first_value, XYData):
316
+ _populate_xydata_batch_values(publish_request, first_value, all_values)
317
+ else:
318
+ try:
319
+ vector = Vector(cast(Iterable[bool | int | float | str], all_values))
320
+ except (TypeError, ValueError):
321
+ raise TypeError(
322
+ f"Unsupported iterable. Subtype must be bool, float, int, string, Vector, "
323
+ "AnalogWaveform, ComplexWaveform, Spectrum, DigitalWaveform, or XYData."
324
+ )
325
+ publish_request.scalar_values.CopyFrom(vector_to_protobuf(vector))
175
326
  else:
176
327
  raise TypeError(
177
328
  f"Unsupported measurement values type: {type(values)}. Please consult the documentation."
@@ -212,37 +363,11 @@ def convert_read_condition_response_from_protobuf(response: ReadConditionValueRe
212
363
  raise TypeError(f"Invalid read type: {read_data_type}")
213
364
 
214
365
 
215
- def get_publish_measurement_timestamp(
216
- publish_request: PublishMeasurementRequest, client_provided_timestamp: ht.datetime | None
217
- ) -> PrecisionTimestamp:
218
- """Determine the correct timestamp to use for publishing a measurement."""
219
- no_client_timestamp_provided = client_provided_timestamp is None
220
- if no_client_timestamp_provided:
221
- publish_time = hightime_datetime_to_protobuf(ht.datetime.now(std_datetime.timezone.utc))
366
+ def convert_measurement_timestamp_to_protobuf(
367
+ client_provided_timestamp: ht.datetime | None,
368
+ ) -> PrecisionTimestamp | None:
369
+ """Convert the provided timestamp to PrecisionTimestamp if it's not None."""
370
+ if client_provided_timestamp is not None:
371
+ return hightime_datetime_to_protobuf(client_provided_timestamp)
222
372
  else:
223
- publish_time = hightime_datetime_to_protobuf(cast(ht.datetime, client_provided_timestamp))
224
-
225
- waveform_t0: PrecisionTimestamp | None = None
226
- value_case = publish_request.WhichOneof("value")
227
- if value_case == "double_analog_waveform":
228
- waveform_t0 = publish_request.double_analog_waveform.t0
229
- elif value_case == "i16_analog_waveform":
230
- waveform_t0 = publish_request.i16_analog_waveform.t0
231
- elif value_case == "double_complex_waveform":
232
- waveform_t0 = publish_request.double_complex_waveform.t0
233
- elif value_case == "i16_complex_waveform":
234
- waveform_t0 = publish_request.i16_complex_waveform.t0
235
- elif value_case == "digital_waveform":
236
- waveform_t0 = publish_request.digital_waveform.t0
237
-
238
- # If an initialized waveform t0 value is present
239
- if waveform_t0 is not None and waveform_t0 != PrecisionTimestamp():
240
- if no_client_timestamp_provided:
241
- # If the client did not provide a timestamp, use the waveform t0 value
242
- publish_time = waveform_t0
243
- elif publish_time != waveform_t0:
244
- raise ValueError(
245
- "The provided timestamp does not match the waveform t0. Please provide a matching timestamp or "
246
- "omit the timestamp to use the waveform t0."
247
- )
248
- return publish_time
373
+ return None
@@ -5,15 +5,18 @@ from __future__ import annotations
5
5
  from typing import Iterable, MutableSequence
6
6
 
7
7
  import hightime as ht
8
- from ni.datastore.data._types._error_information import ErrorInformation
9
- from ni.datastore.data._types._outcome import Outcome
10
- from ni.datastore.data._types._published_condition import PublishedCondition
11
- from ni.measurements.data.v1.data_store_pb2 import PublishedMeasurement as PublishedMeasurementProto
8
+ from ni.measurements.data.v1.data_store_pb2 import (
9
+ PublishedMeasurement as PublishedMeasurementProto,
10
+ )
12
11
  from ni.protobuf.types.precision_timestamp_conversion import (
13
12
  hightime_datetime_from_protobuf,
14
13
  hightime_datetime_to_protobuf,
15
14
  )
16
15
 
16
+ from ni.datastore.data._types._error_information import ErrorInformation
17
+ from ni.datastore.data._types._outcome import Outcome
18
+ from ni.datastore.data._types._published_condition import PublishedCondition
19
+
17
20
 
18
21
  class PublishedMeasurement:
19
22
  """Represents a measurement that has been published to the data store.
@@ -5,17 +5,18 @@ from __future__ import annotations
5
5
  from typing import Mapping, MutableMapping
6
6
 
7
7
  import hightime as ht
8
+ from ni.measurements.data.v1.data_store_pb2 import Step as StepProto
9
+ from ni.protobuf.types.precision_timestamp_conversion import (
10
+ hightime_datetime_from_protobuf,
11
+ hightime_datetime_to_protobuf,
12
+ )
13
+
8
14
  from ni.datastore.data._types._error_information import ErrorInformation
9
15
  from ni.datastore.data._types._outcome import Outcome
10
16
  from ni.datastore.metadata._grpc_conversion import (
11
17
  populate_extension_value_message_map,
12
18
  populate_from_extension_value_message_map,
13
19
  )
14
- from ni.measurements.data.v1.data_store_pb2 import Step as StepProto
15
- from ni.protobuf.types.precision_timestamp_conversion import (
16
- hightime_datetime_from_protobuf,
17
- hightime_datetime_to_protobuf,
18
- )
19
20
 
20
21
 
21
22
  class Step:
@@ -5,17 +5,18 @@ from __future__ import annotations
5
5
  from typing import Iterable, Mapping, MutableMapping, MutableSequence
6
6
 
7
7
  import hightime as ht
8
+ from ni.measurements.data.v1.data_store_pb2 import TestResult as TestResultProto
9
+ from ni.protobuf.types.precision_timestamp_conversion import (
10
+ hightime_datetime_from_protobuf,
11
+ hightime_datetime_to_protobuf,
12
+ )
13
+
8
14
  from ni.datastore.data._types._error_information import ErrorInformation
9
15
  from ni.datastore.data._types._outcome import Outcome
10
16
  from ni.datastore.metadata._grpc_conversion import (
11
17
  populate_extension_value_message_map,
12
18
  populate_from_extension_value_message_map,
13
19
  )
14
- from ni.measurements.data.v1.data_store_pb2 import TestResult as TestResultProto
15
- from ni.protobuf.types.precision_timestamp_conversion import (
16
- hightime_datetime_from_protobuf,
17
- hightime_datetime_to_protobuf,
18
- )
19
20
 
20
21
 
21
22
  class TestResult:
@@ -11,18 +11,6 @@ from types import TracebackType
11
11
  from typing import TYPE_CHECKING
12
12
 
13
13
  from grpc import Channel
14
- from ni.datastore.metadata._types._alias import Alias
15
- from ni.datastore.metadata._types._extension_schema import ExtensionSchema
16
- from ni.datastore.metadata._types._hardware_item import HardwareItem
17
- from ni.datastore.metadata._types._metadata_items import MetadataItems
18
- from ni.datastore.metadata._types._operator import Operator
19
- from ni.datastore.metadata._types._software_item import SoftwareItem
20
- from ni.datastore.metadata._types._test import Test
21
- from ni.datastore.metadata._types._test_adapter import TestAdapter
22
- from ni.datastore.metadata._types._test_description import TestDescription
23
- from ni.datastore.metadata._types._test_station import TestStation
24
- from ni.datastore.metadata._types._uut import Uut
25
- from ni.datastore.metadata._types._uut_instance import UutInstance
26
14
  from ni.measurementlink.discovery.v1.client import DiscoveryClient
27
15
  from ni.measurements.metadata.v1.client import (
28
16
  MetadataStoreClient as MetadataStoreServiceClient,
@@ -65,6 +53,19 @@ from ni.measurements.metadata.v1.metadata_store_service_pb2 import (
65
53
  )
66
54
  from ni_grpc_extensions.channelpool import GrpcChannelPool
67
55
 
56
+ from ni.datastore.metadata._types._alias import Alias
57
+ from ni.datastore.metadata._types._extension_schema import ExtensionSchema
58
+ from ni.datastore.metadata._types._hardware_item import HardwareItem
59
+ from ni.datastore.metadata._types._metadata_items import MetadataItems
60
+ from ni.datastore.metadata._types._operator import Operator
61
+ from ni.datastore.metadata._types._software_item import SoftwareItem
62
+ from ni.datastore.metadata._types._test import Test
63
+ from ni.datastore.metadata._types._test_adapter import TestAdapter
64
+ from ni.datastore.metadata._types._test_description import TestDescription
65
+ from ni.datastore.metadata._types._test_station import TestStation
66
+ from ni.datastore.metadata._types._uut import Uut
67
+ from ni.datastore.metadata._types._uut_instance import UutInstance
68
+
68
69
  if TYPE_CHECKING:
69
70
  if sys.version_info >= (3, 11):
70
71
  from typing import Self
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from ni.measurements.metadata.v1.metadata_store_pb2 import Alias as AliasProto
6
6
 
7
- from ._alias_target_type import AliasTargetType
7
+ from ni.datastore.metadata._types._alias_target_type import AliasTargetType
8
8
 
9
9
 
10
10
  class Alias:
@@ -4,13 +4,14 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Mapping, MutableMapping
6
6
 
7
+ from ni.measurements.metadata.v1.metadata_store_pb2 import (
8
+ HardwareItem as HardwareItemProto,
9
+ )
10
+
7
11
  from ni.datastore.metadata._grpc_conversion import (
8
12
  populate_extension_value_message_map,
9
13
  populate_from_extension_value_message_map,
10
14
  )
11
- from ni.measurements.metadata.v1.metadata_store_pb2 import (
12
- HardwareItem as HardwareItemProto,
13
- )
14
15
 
15
16
 
16
17
  class HardwareItem:
@@ -4,6 +4,10 @@ from __future__ import annotations
4
4
 
5
5
  from collections.abc import Sequence
6
6
 
7
+ from ni.measurements.metadata.v1.metadata_store_service_pb2 import (
8
+ CreateFromJsonDocumentResponse,
9
+ )
10
+
7
11
  from ni.datastore.metadata._types._hardware_item import HardwareItem
8
12
  from ni.datastore.metadata._types._operator import Operator
9
13
  from ni.datastore.metadata._types._software_item import SoftwareItem
@@ -13,9 +17,6 @@ from ni.datastore.metadata._types._test_description import TestDescription
13
17
  from ni.datastore.metadata._types._test_station import TestStation
14
18
  from ni.datastore.metadata._types._uut import Uut
15
19
  from ni.datastore.metadata._types._uut_instance import UutInstance
16
- from ni.measurements.metadata.v1.metadata_store_service_pb2 import (
17
- CreateFromJsonDocumentResponse,
18
- )
19
20
 
20
21
 
21
22
  class MetadataItems:
@@ -4,13 +4,14 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Mapping, MutableMapping
6
6
 
7
+ from ni.measurements.metadata.v1.metadata_store_pb2 import (
8
+ Operator as OperatorProto,
9
+ )
10
+
7
11
  from ni.datastore.metadata._grpc_conversion import (
8
12
  populate_extension_value_message_map,
9
13
  populate_from_extension_value_message_map,
10
14
  )
11
- from ni.measurements.metadata.v1.metadata_store_pb2 import (
12
- Operator as OperatorProto,
13
- )
14
15
 
15
16
 
16
17
  class Operator:
@@ -4,13 +4,14 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Mapping, MutableMapping
6
6
 
7
+ from ni.measurements.metadata.v1.metadata_store_pb2 import (
8
+ SoftwareItem as SoftwareItemProto,
9
+ )
10
+
7
11
  from ni.datastore.metadata._grpc_conversion import (
8
12
  populate_extension_value_message_map,
9
13
  populate_from_extension_value_message_map,
10
14
  )
11
- from ni.measurements.metadata.v1.metadata_store_pb2 import (
12
- SoftwareItem as SoftwareItemProto,
13
- )
14
15
 
15
16
 
16
17
  class SoftwareItem:
@@ -4,13 +4,14 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Mapping, MutableMapping
6
6
 
7
+ from ni.measurements.metadata.v1.metadata_store_pb2 import (
8
+ Test as TestProto,
9
+ )
10
+
7
11
  from ni.datastore.metadata._grpc_conversion import (
8
12
  populate_extension_value_message_map,
9
13
  populate_from_extension_value_message_map,
10
14
  )
11
- from ni.measurements.metadata.v1.metadata_store_pb2 import (
12
- Test as TestProto,
13
- )
14
15
 
15
16
 
16
17
  class Test:
@@ -4,13 +4,14 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Mapping, MutableMapping
6
6
 
7
+ from ni.measurements.metadata.v1.metadata_store_pb2 import (
8
+ TestAdapter as TestAdapterProto,
9
+ )
10
+
7
11
  from ni.datastore.metadata._grpc_conversion import (
8
12
  populate_extension_value_message_map,
9
13
  populate_from_extension_value_message_map,
10
14
  )
11
- from ni.measurements.metadata.v1.metadata_store_pb2 import (
12
- TestAdapter as TestAdapterProto,
13
- )
14
15
 
15
16
 
16
17
  class TestAdapter:
@@ -4,13 +4,14 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Mapping, MutableMapping
6
6
 
7
+ from ni.measurements.metadata.v1.metadata_store_pb2 import (
8
+ TestDescription as TestDescriptionProto,
9
+ )
10
+
7
11
  from ni.datastore.metadata._grpc_conversion import (
8
12
  populate_extension_value_message_map,
9
13
  populate_from_extension_value_message_map,
10
14
  )
11
- from ni.measurements.metadata.v1.metadata_store_pb2 import (
12
- TestDescription as TestDescriptionProto,
13
- )
14
15
 
15
16
 
16
17
  class TestDescription:
@@ -4,13 +4,14 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Mapping, MutableMapping
6
6
 
7
+ from ni.measurements.metadata.v1.metadata_store_pb2 import (
8
+ TestStation as TestStationProto,
9
+ )
10
+
7
11
  from ni.datastore.metadata._grpc_conversion import (
8
12
  populate_extension_value_message_map,
9
13
  populate_from_extension_value_message_map,
10
14
  )
11
- from ni.measurements.metadata.v1.metadata_store_pb2 import (
12
- TestStation as TestStationProto,
13
- )
14
15
 
15
16
 
16
17
  class TestStation:
@@ -4,13 +4,14 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Iterable, Mapping, MutableMapping, MutableSequence
6
6
 
7
+ from ni.measurements.metadata.v1.metadata_store_pb2 import (
8
+ Uut as UutProto,
9
+ )
10
+
7
11
  from ni.datastore.metadata._grpc_conversion import (
8
12
  populate_extension_value_message_map,
9
13
  populate_from_extension_value_message_map,
10
14
  )
11
- from ni.measurements.metadata.v1.metadata_store_pb2 import (
12
- Uut as UutProto,
13
- )
14
15
 
15
16
 
16
17
  class Uut:
@@ -4,13 +4,14 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Mapping, MutableMapping
6
6
 
7
+ from ni.measurements.metadata.v1.metadata_store_pb2 import (
8
+ UutInstance as UutInstanceProto,
9
+ )
10
+
7
11
  from ni.datastore.metadata._grpc_conversion import (
8
12
  populate_extension_value_message_map,
9
13
  populate_from_extension_value_message_map,
10
14
  )
11
- from ni.measurements.metadata.v1.metadata_store_pb2 import (
12
- UutInstance as UutInstanceProto,
13
- )
14
15
 
15
16
 
16
17
  class UutInstance: