Skip to content

Commit 6569aa6

Browse files
committed
waveform: Implement repr and corresponding constructor arguments
1 parent 70b9f87 commit 6569aa6

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
Any,
88
Generic,
@@ -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 (
@@ -94,6 +95,9 @@ def from_array_1d(
9495
copy: bool = ...,
9596
start_index: SupportsIndex | None = ...,
9697
sample_count: SupportsIndex | None = ...,
98+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
99+
timing: Timing | PrecisionTiming | None = ...,
100+
scale_mode: ScaleMode | None = ...,
97101
) -> AnalogWaveform[_ScalarType]: ...
98102

99103
@overload
@@ -105,6 +109,9 @@ def from_array_1d(
105109
copy: bool = ...,
106110
start_index: SupportsIndex | None = ...,
107111
sample_count: SupportsIndex | None = ...,
112+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
113+
timing: Timing | PrecisionTiming | None = ...,
114+
scale_mode: ScaleMode | None = ...,
108115
) -> AnalogWaveform[_ScalarType]: ...
109116

110117
@overload
@@ -116,6 +123,9 @@ def from_array_1d(
116123
copy: bool = ...,
117124
start_index: SupportsIndex | None = ...,
118125
sample_count: SupportsIndex | None = ...,
126+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
127+
timing: Timing | PrecisionTiming | None = ...,
128+
scale_mode: ScaleMode | None = ...,
119129
) -> AnalogWaveform[Any]: ...
120130

121131
@staticmethod
@@ -126,6 +136,9 @@ def from_array_1d(
126136
copy: bool = True,
127137
start_index: SupportsIndex | None = 0,
128138
sample_count: SupportsIndex | None = None,
139+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = None,
140+
timing: Timing | PrecisionTiming | None = None,
141+
scale_mode: ScaleMode | None = None,
129142
) -> AnalogWaveform[_ScalarType]:
130143
"""Construct an analog waveform from a one-dimensional array or sequence.
131144
@@ -136,6 +149,9 @@ def from_array_1d(
136149
copy: Specifies whether to copy the array or save a reference to it.
137150
start_index: The sample index at which the analog waveform data begins.
138151
sample_count: The number of samples in the analog waveform.
152+
extended_properties: The extended properties of the analog waveform.
153+
timing: The timing information of the analog waveform.
154+
scale_mode: The scale mode of the analog waveform.
139155
140156
Returns:
141157
An analog waveform containing the specified data.
@@ -154,9 +170,12 @@ def from_array_1d(
154170
raise invalid_arg_type("input array", "one-dimensional array or sequence", array)
155171

156172
return AnalogWaveform(
157-
_data=np.asarray(array, dtype, copy=copy),
173+
raw_data=np.asarray(array, dtype, copy=copy),
158174
start_index=start_index,
159175
sample_count=sample_count,
176+
extended_properties=extended_properties,
177+
timing=timing,
178+
scale_mode=scale_mode,
160179
)
161180

162181
@overload
@@ -168,6 +187,9 @@ def from_array_2d(
168187
copy: bool = ...,
169188
start_index: SupportsIndex | None = ...,
170189
sample_count: SupportsIndex | None = ...,
190+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
191+
timing: Timing | PrecisionTiming | None = ...,
192+
scale_mode: ScaleMode | None = ...,
171193
) -> list[AnalogWaveform[_ScalarType]]: ...
172194

173195
@overload
@@ -179,6 +201,9 @@ def from_array_2d(
179201
copy: bool = ...,
180202
start_index: SupportsIndex | None = ...,
181203
sample_count: SupportsIndex | None = ...,
204+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
205+
timing: Timing | PrecisionTiming | None = ...,
206+
scale_mode: ScaleMode | None = ...,
182207
) -> list[AnalogWaveform[_ScalarType]]: ...
183208

184209
@overload
@@ -190,6 +215,9 @@ def from_array_2d(
190215
copy: bool = ...,
191216
start_index: SupportsIndex | None = ...,
192217
sample_count: SupportsIndex | None = ...,
218+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
219+
timing: Timing | PrecisionTiming | None = ...,
220+
scale_mode: ScaleMode | None = ...,
193221
) -> list[AnalogWaveform[Any]]: ...
194222

195223
@staticmethod
@@ -200,6 +228,9 @@ def from_array_2d(
200228
copy: bool = True,
201229
start_index: SupportsIndex | None = 0,
202230
sample_count: SupportsIndex | None = None,
231+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = None,
232+
timing: Timing | PrecisionTiming | None = None,
233+
scale_mode: ScaleMode | None = None,
203234
) -> list[AnalogWaveform[_ScalarType]]:
204235
"""Construct a list of analog waveforms from a two-dimensional array or nested sequence.
205236
@@ -210,9 +241,16 @@ def from_array_2d(
210241
copy: Specifies whether to copy the array or save a reference to it.
211242
start_index: The sample index at which the analog waveform data begins.
212243
sample_count: The number of samples in the analog waveform.
244+
extended_properties: The extended properties of the analog waveform.
245+
timing: The timing information of the analog waveform.
246+
scale_mode: The scale mode of the analog waveform.
213247
214248
Returns:
215249
A list containing an analog waveform for each row of the specified data.
250+
251+
When constructing multiple analog waveforms, the same extended properties, timing
252+
information, and scale mode are applied to all analog waveforms. Consider assigning
253+
these properties after construction.
216254
"""
217255
if isinstance(array, np.ndarray):
218256
if array.ndim != 2:
@@ -229,9 +267,12 @@ def from_array_2d(
229267

230268
return [
231269
AnalogWaveform(
232-
_data=np.asarray(array[i], dtype, copy=copy),
270+
raw_data=np.asarray(array[i], dtype, copy=copy),
233271
start_index=start_index,
234272
sample_count=sample_count,
273+
extended_properties=extended_properties,
274+
timing=timing,
275+
scale_mode=scale_mode,
235276
)
236277
for i in range(len(array))
237278
]
@@ -262,9 +303,12 @@ def __init__( # noqa: D107 - Missing docstring in __init__ (auto-generated noqa
262303
sample_count: SupportsIndex | None = ...,
263304
dtype: None = ...,
264305
*,
306+
raw_data: None = ...,
265307
start_index: SupportsIndex | None = ...,
266308
capacity: SupportsIndex | None = ...,
267-
_data: None = ...,
309+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
310+
timing: Timing | PrecisionTiming | None = ...,
311+
scale_mode: ScaleMode | None = ...,
268312
) -> None: ...
269313

270314
@overload
@@ -273,9 +317,12 @@ def __init__( # noqa: D107 - Missing docstring in __init__ (auto-generated noqa
273317
sample_count: SupportsIndex | None = ...,
274318
dtype: type[_ScalarType_co] | np.dtype[_ScalarType_co] = ...,
275319
*,
320+
raw_data: None = ...,
276321
start_index: SupportsIndex | None = ...,
277322
capacity: SupportsIndex | None = ...,
278-
_data: None = ...,
323+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
324+
timing: Timing | PrecisionTiming | None = ...,
325+
scale_mode: ScaleMode | None = ...,
279326
) -> None: ...
280327

281328
@overload
@@ -284,9 +331,12 @@ def __init__( # noqa: D107 - Missing docstring in __init__ (auto-generated noqa
284331
sample_count: SupportsIndex | None = ...,
285332
dtype: None = ...,
286333
*,
334+
raw_data: npt.NDArray[_ScalarType_co] | None = ...,
287335
start_index: SupportsIndex | None = ...,
288336
capacity: SupportsIndex | None = ...,
289-
_data: npt.NDArray[_ScalarType_co] | None = ...,
337+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
338+
timing: Timing | PrecisionTiming | None = ...,
339+
scale_mode: ScaleMode | None = ...,
290340
) -> None: ...
291341

292342
@overload
@@ -295,45 +345,71 @@ def __init__( # noqa: D107 - Missing docstring in __init__ (auto-generated noqa
295345
sample_count: SupportsIndex | None = ...,
296346
dtype: npt.DTypeLike = ...,
297347
*,
348+
raw_data: npt.NDArray[Any] | None = ...,
298349
start_index: SupportsIndex | None = ...,
299350
capacity: SupportsIndex | None = ...,
300-
_data: npt.NDArray[Any] | None = ...,
351+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = ...,
352+
timing: Timing | PrecisionTiming | None = ...,
353+
scale_mode: ScaleMode | None = ...,
301354
) -> None: ...
302355

303356
def __init__(
304357
self,
305358
sample_count: SupportsIndex | None = None,
306359
dtype: npt.DTypeLike = None,
307360
*,
361+
raw_data: npt.NDArray[_ScalarType_co] | None = None,
308362
start_index: SupportsIndex | None = None,
309363
capacity: SupportsIndex | None = None,
310-
_data: npt.NDArray[_ScalarType_co] | None = None,
364+
extended_properties: Mapping[str, ExtendedPropertyValue] | None = None,
365+
timing: Timing | PrecisionTiming | None = None,
366+
scale_mode: ScaleMode | None = None,
311367
) -> None:
312368
"""Construct an analog waveform.
313369
314370
Args:
315371
sample_count: The number of samples in the analog waveform.
316372
dtype: The NumPy data type for the analog waveform data. If not specified, the data
317373
type defaults to np.float64.
374+
raw_data: A NumPy ndarray to use for sample storage. The analog waveform takes ownership
375+
of this array. If not specified, an ndarray is created based on the specified dtype,
376+
start index, sample count, and capacity.
318377
start_index: The sample index at which the analog waveform data begins.
319378
sample_count: The number of samples in the analog waveform.
320379
capacity: The number of samples to allocate. Pre-allocating a larger buffer optimizes
321380
appending samples to the waveform.
381+
extended_properties: The extended properties of the analog waveform.
382+
timing: The timing information of the analog waveform.
383+
scale_mode: The scale mode of the analog waveform.
322384
323385
Returns:
324386
An analog waveform.
325-
326-
Arguments that are prefixed with an underscore are internal implementation details and are
327-
subject to change.
328387
"""
329-
if _data is None:
388+
if raw_data is None:
330389
self._init_with_new_array(
331390
sample_count, dtype, start_index=start_index, capacity=capacity
332391
)
333-
else:
392+
elif isinstance(raw_data, np.ndarray):
334393
self._init_with_provided_array(
335-
_data, dtype, start_index=start_index, sample_count=sample_count, capacity=capacity
394+
raw_data,
395+
dtype,
396+
start_index=start_index,
397+
sample_count=sample_count,
398+
capacity=capacity,
336399
)
400+
else:
401+
raise invalid_arg_type("raw data", "NumPy ndarray", raw_data)
402+
403+
self._extended_properties = ExtendedPropertyDictionary(extended_properties)
404+
405+
if timing is None:
406+
timing = Timing.empty
407+
self._timing = timing
408+
self._converted_timing_cache = {}
409+
410+
if scale_mode is None:
411+
scale_mode = NO_SCALING
412+
self._scale_mode = scale_mode
337413

338414
def _init_with_new_array(
339415
self,
@@ -368,10 +444,6 @@ def _init_with_new_array(
368444
self._data = np.zeros(capacity, dtype)
369445
self._start_index = start_index
370446
self._sample_count = sample_count
371-
self._extended_properties = ExtendedPropertyDictionary()
372-
self._timing = Timing.empty
373-
self._converted_timing_cache = {}
374-
self._scale_mode = NO_SCALING
375447

376448
def _init_with_provided_array(
377449
self,
@@ -425,10 +497,6 @@ def _init_with_provided_array(
425497
self._data = data
426498
self._start_index = start_index
427499
self._sample_count = sample_count
428-
self._extended_properties = ExtendedPropertyDictionary()
429-
self._timing = Timing.empty
430-
self._converted_timing_cache = {}
431-
self._scale_mode = NO_SCALING
432500

433501
@property
434502
def raw_data(self) -> npt.NDArray[_ScalarType_co]:
@@ -759,13 +827,31 @@ def _increase_capacity(self, amount: int) -> None:
759827
if new_capacity > self.capacity:
760828
self.capacity = new_capacity
761829

762-
def __eq__(self, other: object) -> bool: # noqa: D105 - Missing docstring in magic method
763-
if not isinstance(other, self.__class__):
830+
def __eq__(self, value: object, /) -> bool:
831+
"""Return self==value."""
832+
if not isinstance(value, self.__class__):
764833
return NotImplemented
765834
return (
766-
self.dtype == other.dtype
767-
and np.array_equal(self.raw_data, other.raw_data)
768-
and self._extended_properties == other._extended_properties
769-
and self._base_timing == other._base_timing
770-
and self._scale_mode == other._scale_mode
835+
self.dtype == value.dtype
836+
and np.array_equal(self.raw_data, value.raw_data)
837+
and self._extended_properties == value._extended_properties
838+
and self._timing == value._timing
839+
and self._scale_mode == value._scale_mode
771840
)
841+
842+
def __repr__(self) -> str:
843+
"""Return repr(self)."""
844+
args = [f"{self._sample_count}"]
845+
if self.dtype != np.float64:
846+
args.append(f"{self.dtype.name}")
847+
# start_index and capacity are not shown because they are allocation details. raw_data hides
848+
# the unused data before start_index and after start_index+sample_count.
849+
if self._sample_count > 0:
850+
args.append(f"raw_data={self.raw_data!r}")
851+
if self._extended_properties:
852+
args.append(f"extended_properties={self._extended_properties._properties!r}")
853+
if self._timing is not Timing.empty and self._timing is not PrecisionTiming.empty:
854+
args.append(f"timing={self._timing!r}")
855+
if self._scale_mode is not NO_SCALING:
856+
args.append(f"scale_mode={self._scale_mode}")
857+
return f"{self.__class__.__module__}.{self.__class__.__name__}({', '.join(args)})"
Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import operator
4+
from collections.abc import Mapping
45
from typing import Iterator, MutableMapping, Union
56

67
from nitypes._typing import TypeAlias
@@ -18,36 +19,36 @@
1819
class ExtendedPropertyDictionary(MutableMapping[str, ExtendedPropertyValue]):
1920
"""A dictionary of extended properties."""
2021

21-
def __init__(self) -> None:
22+
def __init__(self, properties: Mapping[str, ExtendedPropertyValue] | None = None, /) -> None:
2223
"""Construct an ExtendedPropertyDictionary."""
2324
self._properties: dict[str, ExtendedPropertyValue] = {}
25+
if properties is not None:
26+
self._properties.update(properties)
2427

25-
def __len__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa)
26-
self,
27-
) -> int:
28+
def __len__(self) -> int:
29+
"""Return len(self)."""
2830
return len(self._properties)
2931

30-
def __iter__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa)
31-
self,
32-
) -> Iterator[str]:
32+
def __iter__(self) -> Iterator[str]:
33+
"""Implement iter(self)."""
3334
return iter(self._properties)
3435

35-
def __contains__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa)
36-
self, x: object, /
37-
) -> bool:
38-
return operator.contains(self._properties, x)
36+
def __contains__(self, value: object, /) -> bool:
37+
"""Implement value in self."""
38+
return operator.contains(self._properties, value)
3939

40-
def __getitem__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa)
41-
self, key: str, /
42-
) -> ExtendedPropertyValue:
40+
def __getitem__(self, key: str, /) -> ExtendedPropertyValue:
41+
"""Get self[key]."""
4342
return operator.getitem(self._properties, key)
4443

45-
def __setitem__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa)
46-
self, key: str, value: ExtendedPropertyValue, /
47-
) -> None:
44+
def __setitem__(self, key: str, value: ExtendedPropertyValue, /) -> None:
45+
"""Set self[key] to value."""
4846
operator.setitem(self._properties, key, value)
4947

50-
def __delitem__( # noqa: D105 - Missing docstring in magic method (auto-generated noqa)
51-
self, key: str, /
52-
) -> None:
48+
def __delitem__(self, key: str, /) -> None:
49+
"""Delete self[key]."""
5350
operator.delitem(self._properties, key)
51+
52+
def __repr__(self) -> str:
53+
"""Return repr(self)."""
54+
return f"{self.__class__.__module__}.{self.__class__.__name__}({self._properties!r})"

0 commit comments

Comments
 (0)