Skip to content

Commit 66b0af6

Browse files
committed
waveform: Implement repr and corresponding constructor arguments
1 parent a167c1b commit 66b0af6

File tree

6 files changed

+269
-133
lines changed

6 files changed

+269
-133
lines changed

src/nitypes/waveform/_analog_waveform.py

Lines changed: 115 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import datetime as dt
44
import sys
5-
from collections.abc import Sequence
5+
from collections.abc import Mapping, Sequence
66
from typing import (
77
TYPE_CHECKING,
88
Any,
@@ -24,6 +24,7 @@
2424
CHANNEL_NAME,
2525
UNIT_DESCRIPTION,
2626
ExtendedPropertyDictionary,
27+
ExtendedPropertyValue,
2728
)
2829
from nitypes.waveform._scaling import NO_SCALING, ScaleMode
2930
from nitypes.waveform._timing import (
@@ -99,6 +100,9 @@ def from_array_1d(
99100
copy: bool = ...,
100101
start_index: SupportsIndex | None = ...,
101102
sample_count: SupportsIndex | None = ...,
103+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
104+
timing: Timing | PrecisionTiming | None = ...,
105+
scale_mode: ScaleMode | None = ...,
102106
) -> AnalogWaveform[_ScalarType]: ...
103107

104108
@overload
@@ -110,6 +114,9 @@ def from_array_1d(
110114
copy: bool = ...,
111115
start_index: SupportsIndex | None = ...,
112116
sample_count: SupportsIndex | None = ...,
117+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
118+
timing: Timing | PrecisionTiming | None = ...,
119+
scale_mode: ScaleMode | None = ...,
113120
) -> AnalogWaveform[_ScalarType]: ...
114121

115122
@overload
@@ -121,6 +128,9 @@ def from_array_1d(
121128
copy: bool = ...,
122129
start_index: SupportsIndex | None = ...,
123130
sample_count: SupportsIndex | None = ...,
131+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
132+
timing: Timing | PrecisionTiming | None = ...,
133+
scale_mode: ScaleMode | None = ...,
124134
) -> AnalogWaveform[Any]: ...
125135

126136
@staticmethod
@@ -131,6 +141,9 @@ def from_array_1d(
131141
copy: bool = True,
132142
start_index: SupportsIndex | None = 0,
133143
sample_count: SupportsIndex | None = None,
144+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = None,
145+
timing: Timing | PrecisionTiming | None = None,
146+
scale_mode: ScaleMode | None = None,
134147
) -> AnalogWaveform[_ScalarType]:
135148
"""Construct an analog waveform from a one-dimensional array or sequence.
136149
@@ -141,6 +154,9 @@ def from_array_1d(
141154
copy: Specifies whether to copy the array or save a reference to it.
142155
start_index: The sample index at which the analog waveform data begins.
143156
sample_count: The number of samples in the analog waveform.
157+
extended_properties: The extended properties of the analog waveform.
158+
timing: The timing information of the analog waveform.
159+
scale_mode: The scale mode of the analog waveform.
144160
145161
Returns:
146162
An analog waveform containing the specified data.
@@ -159,9 +175,12 @@ def from_array_1d(
159175
raise invalid_arg_type("input array", "one-dimensional array or sequence", array)
160176

161177
return AnalogWaveform(
162-
_data=np.asarray(array, dtype, copy=copy),
178+
raw_data=np.asarray(array, dtype, copy=copy),
163179
start_index=start_index,
164180
sample_count=sample_count,
181+
extended_properties=extended_properties,
182+
timing=timing,
183+
scale_mode=scale_mode,
165184
)
166185

167186
@overload
@@ -173,6 +192,9 @@ def from_array_2d(
173192
copy: bool = ...,
174193
start_index: SupportsIndex | None = ...,
175194
sample_count: SupportsIndex | None = ...,
195+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
196+
timing: Timing | PrecisionTiming | None = ...,
197+
scale_mode: ScaleMode | None = ...,
176198
) -> list[AnalogWaveform[_ScalarType]]: ...
177199

178200
@overload
@@ -184,6 +206,9 @@ def from_array_2d(
184206
copy: bool = ...,
185207
start_index: SupportsIndex | None = ...,
186208
sample_count: SupportsIndex | None = ...,
209+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
210+
timing: Timing | PrecisionTiming | None = ...,
211+
scale_mode: ScaleMode | None = ...,
187212
) -> list[AnalogWaveform[_ScalarType]]: ...
188213

189214
@overload
@@ -195,6 +220,9 @@ def from_array_2d(
195220
copy: bool = ...,
196221
start_index: SupportsIndex | None = ...,
197222
sample_count: SupportsIndex | None = ...,
223+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
224+
timing: Timing | PrecisionTiming | None = ...,
225+
scale_mode: ScaleMode | None = ...,
198226
) -> list[AnalogWaveform[Any]]: ...
199227

200228
@staticmethod
@@ -205,6 +233,9 @@ def from_array_2d(
205233
copy: bool = True,
206234
start_index: SupportsIndex | None = 0,
207235
sample_count: SupportsIndex | None = None,
236+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = None,
237+
timing: Timing | PrecisionTiming | None = None,
238+
scale_mode: ScaleMode | None = None,
208239
) -> list[AnalogWaveform[_ScalarType]]:
209240
"""Construct a list of analog waveforms from a two-dimensional array or nested sequence.
210241
@@ -215,9 +246,16 @@ def from_array_2d(
215246
copy: Specifies whether to copy the array or save a reference to it.
216247
start_index: The sample index at which the analog waveform data begins.
217248
sample_count: The number of samples in the analog waveform.
249+
extended_properties: The extended properties of the analog waveform.
250+
timing: The timing information of the analog waveform.
251+
scale_mode: The scale mode of the analog waveform.
218252
219253
Returns:
220254
A list containing an analog waveform for each row of the specified data.
255+
256+
When constructing multiple analog waveforms, the same extended properties, timing
257+
information, and scale mode are applied to all analog waveforms. Consider assigning
258+
these properties after construction.
221259
"""
222260
if isinstance(array, np.ndarray):
223261
if array.ndim != 2:
@@ -234,9 +272,12 @@ def from_array_2d(
234272

235273
return [
236274
AnalogWaveform(
237-
_data=np.asarray(array[i], dtype, copy=copy),
275+
raw_data=np.asarray(array[i], dtype, copy=copy),
238276
start_index=start_index,
239277
sample_count=sample_count,
278+
extended_properties=extended_properties,
279+
timing=timing,
280+
scale_mode=scale_mode,
240281
)
241282
for i in range(len(array))
242283
]
@@ -267,9 +308,12 @@ def __init__( # noqa: D107 - Missing docstring in __init__ (auto-generated noqa
267308
sample_count: SupportsIndex | None = ...,
268309
dtype: None = ...,
269310
*,
311+
raw_data: None = ...,
270312
start_index: SupportsIndex | None = ...,
271313
capacity: SupportsIndex | None = ...,
272-
_data: None = ...,
314+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
315+
timing: Timing | PrecisionTiming | None = ...,
316+
scale_mode: ScaleMode | None = ...,
273317
) -> None: ...
274318

275319
@overload
@@ -278,9 +322,12 @@ def __init__( # noqa: D107 - Missing docstring in __init__ (auto-generated noqa
278322
sample_count: SupportsIndex | None = ...,
279323
dtype: type[_ScalarType_co] | np.dtype[_ScalarType_co] = ...,
280324
*,
325+
raw_data: None = ...,
281326
start_index: SupportsIndex | None = ...,
282327
capacity: SupportsIndex | None = ...,
283-
_data: None = ...,
328+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
329+
timing: Timing | PrecisionTiming | None = ...,
330+
scale_mode: ScaleMode | None = ...,
284331
) -> None: ...
285332

286333
@overload
@@ -289,9 +336,12 @@ def __init__( # noqa: D107 - Missing docstring in __init__ (auto-generated noqa
289336
sample_count: SupportsIndex | None = ...,
290337
dtype: None = ...,
291338
*,
339+
raw_data: npt.NDArray[_ScalarType_co] | None = ...,
292340
start_index: SupportsIndex | None = ...,
293341
capacity: SupportsIndex | None = ...,
294-
_data: npt.NDArray[_ScalarType_co] | None = ...,
342+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
343+
timing: Timing | PrecisionTiming | None = ...,
344+
scale_mode: ScaleMode | None = ...,
295345
) -> None: ...
296346

297347
@overload
@@ -300,45 +350,71 @@ def __init__( # noqa: D107 - Missing docstring in __init__ (auto-generated noqa
300350
sample_count: SupportsIndex | None = ...,
301351
dtype: npt.DTypeLike = ...,
302352
*,
353+
raw_data: npt.NDArray[Any] | None = ...,
303354
start_index: SupportsIndex | None = ...,
304355
capacity: SupportsIndex | None = ...,
305-
_data: npt.NDArray[Any] | None = ...,
356+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
357+
timing: Timing | PrecisionTiming | None = ...,
358+
scale_mode: ScaleMode | None = ...,
306359
) -> None: ...
307360

308361
def __init__(
309362
self,
310363
sample_count: SupportsIndex | None = None,
311364
dtype: npt.DTypeLike = None,
312365
*,
366+
raw_data: npt.NDArray[_ScalarType_co] | None = None,
313367
start_index: SupportsIndex | None = None,
314368
capacity: SupportsIndex | None = None,
315-
_data: npt.NDArray[_ScalarType_co] | None = None,
369+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = None,
370+
timing: Timing | PrecisionTiming | None = None,
371+
scale_mode: ScaleMode | None = None,
316372
) -> None:
317373
"""Construct an analog waveform.
318374
319375
Args:
320376
sample_count: The number of samples in the analog waveform.
321377
dtype: The NumPy data type for the analog waveform data. If not specified, the data
322378
type defaults to np.float64.
379+
raw_data: A NumPy ndarray to use for sample storage. The analog waveform takes ownership
380+
of this array. If not specified, an ndarray is created based on the specified dtype,
381+
start index, sample count, and capacity.
323382
start_index: The sample index at which the analog waveform data begins.
324383
sample_count: The number of samples in the analog waveform.
325384
capacity: The number of samples to allocate. Pre-allocating a larger buffer optimizes
326385
appending samples to the waveform.
386+
extended_properties: The extended properties of the analog waveform.
387+
timing: The timing information of the analog waveform.
388+
scale_mode: The scale mode of the analog waveform.
327389
328390
Returns:
329391
An analog waveform.
330-
331-
Arguments that are prefixed with an underscore are internal implementation details and are
332-
subject to change.
333392
"""
334-
if _data is None:
393+
if raw_data is None:
335394
self._init_with_new_array(
336395
sample_count, dtype, start_index=start_index, capacity=capacity
337396
)
338-
else:
397+
elif isinstance(raw_data, np.ndarray):
339398
self._init_with_provided_array(
340-
_data, dtype, start_index=start_index, sample_count=sample_count, capacity=capacity
399+
raw_data,
400+
dtype,
401+
start_index=start_index,
402+
sample_count=sample_count,
403+
capacity=capacity,
341404
)
405+
else:
406+
raise invalid_arg_type("raw data", "NumPy ndarray", raw_data)
407+
408+
self._extended_properties = ExtendedPropertyDictionary(extended_properties)
409+
410+
if timing is None:
411+
timing = Timing.empty
412+
self._timing = timing
413+
self._converted_timing_cache = {}
414+
415+
if scale_mode is None:
416+
scale_mode = NO_SCALING
417+
self._scale_mode = scale_mode
342418

343419
def _init_with_new_array(
344420
self,
@@ -373,10 +449,6 @@ def _init_with_new_array(
373449
self._data = np.zeros(capacity, dtype)
374450
self._start_index = start_index
375451
self._sample_count = sample_count
376-
self._extended_properties = ExtendedPropertyDictionary()
377-
self._timing = Timing.empty
378-
self._converted_timing_cache = {}
379-
self._scale_mode = NO_SCALING
380452

381453
def _init_with_provided_array(
382454
self,
@@ -430,10 +502,6 @@ def _init_with_provided_array(
430502
self._data = data
431503
self._start_index = start_index
432504
self._sample_count = sample_count
433-
self._extended_properties = ExtendedPropertyDictionary()
434-
self._timing = Timing.empty
435-
self._converted_timing_cache = {}
436-
self._scale_mode = NO_SCALING
437505

438506
@property
439507
def raw_data(self) -> npt.NDArray[_ScalarType_co]:
@@ -764,13 +832,31 @@ def _increase_capacity(self, amount: int) -> None:
764832
if new_capacity > self.capacity:
765833
self.capacity = new_capacity
766834

767-
def __eq__(self, other: object) -> bool: # noqa: D105 - Missing docstring in magic method
768-
if not isinstance(other, self.__class__):
835+
def __eq__(self, value: object, /) -> bool:
836+
"""Return self==value."""
837+
if not isinstance(value, self.__class__):
769838
return NotImplemented
770839
return (
771-
self.dtype == other.dtype
772-
and np.array_equal(self.raw_data, other.raw_data)
773-
and self._extended_properties == other._extended_properties
774-
and self._base_timing == other._base_timing
775-
and self._scale_mode == other._scale_mode
840+
self.dtype == value.dtype
841+
and np.array_equal(self.raw_data, value.raw_data)
842+
and self._extended_properties == value._extended_properties
843+
and self._timing == value._timing
844+
and self._scale_mode == value._scale_mode
776845
)
846+
847+
def __repr__(self) -> str:
848+
"""Return repr(self)."""
849+
args = [f"{self._sample_count}"]
850+
if self.dtype != np.float64:
851+
args.append(f"{self.dtype.name}")
852+
# start_index and capacity are not shown because they are allocation details. raw_data hides
853+
# the unused data before start_index and after start_index+sample_count.
854+
if self._sample_count > 0:
855+
args.append(f"raw_data={self.raw_data!r}")
856+
if self._extended_properties:
857+
args.append(f"extended_properties={self._extended_properties._properties!r}")
858+
if self._timing is not Timing.empty and self._timing is not PrecisionTiming.empty:
859+
args.append(f"timing={self._timing!r}")
860+
if self._scale_mode is not NO_SCALING:
861+
args.append(f"scale_mode={self._scale_mode}")
862+
return f"{self.__class__.__module__}.{self.__class__.__name__}({', '.join(args)})"

src/nitypes/waveform/_extended_properties.py

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import operator
44
import sys
5+
from collections.abc import Mapping
56
from typing import TYPE_CHECKING, Iterator, MutableMapping
67

78
if sys.version_info >= (3, 10):
@@ -29,36 +30,36 @@
2930
class ExtendedPropertyDictionary(MutableMapping[str, ExtendedPropertyValue]):
3031
"""A dictionary of extended properties."""
3132

32-
def __init__(self) -> None:
33+
def __init__(self, properties: Mapping[str, ExtendedPropertyValue] | None = None, /) -> None:
3334
"""Construct an ExtendedPropertyDictionary."""
3435
self._properties: dict[str, ExtendedPropertyValue] = {}
36+
if properties is not None:
37+
self._properties.update(properties)
3538

36-
def __len__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa)
37-
self,
38-
) -> int:
39+
def __len__(self) -> int:
40+
"""Return len(self)."""
3941
return len(self._properties)
4042

41-
def __iter__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa)
42-
self,
43-
) -> Iterator[str]:
43+
def __iter__(self) -> Iterator[str]:
44+
"""Implement iter(self)."""
4445
return iter(self._properties)
4546

46-
def __contains__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa)
47-
self, x: object, /
48-
) -> bool:
49-
return operator.contains(self._properties, x)
47+
def __contains__(self, value: object, /) -> bool:
48+
"""Implement value in self."""
49+
return operator.contains(self._properties, value)
5050

51-
def __getitem__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa)
52-
self, key: str, /
53-
) -> ExtendedPropertyValue:
51+
def __getitem__(self, key: str, /) -> ExtendedPropertyValue:
52+
"""Get self[key]."""
5453
return operator.getitem(self._properties, key)
5554

56-
def __setitem__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa)
57-
self, key: str, value: ExtendedPropertyValue, /
58-
) -> None:
55+
def __setitem__(self, key: str, value: ExtendedPropertyValue, /) -> None:
56+
"""Set self[key] to value."""
5957
operator.setitem(self._properties, key, value)
6058

61-
def __delitem__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa)
62-
self, key: str, /
63-
) -> None:
59+
def __delitem__(self, key: str, /) -> None:
60+
"""Delete self[key]."""
6461
operator.delitem(self._properties, key)
62+
63+
def __repr__(self) -> str:
64+
"""Return repr(self)."""
65+
return f"{self.__class__.__module__}.{self.__class__.__name__}({self._properties!r})"

0 commit comments

Comments
 (0)