Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
9487985
Add sketch of basic TimeDelta array
jfriedri-ni Jul 24, 2025
b263c5f
Do not incorrectly swap the struct packing order
jfriedri-ni Jul 24, 2025
3772dc4
Specialize the numpy storage as much as supported
jfriedri-ni Jul 25, 2025
d73ae19
Stop using Union
jfriedri-ni Jul 25, 2025
9a15209
Use len() for a 1D numpy array
jfriedri-ni Jul 25, 2025
5dda9f0
Test with negative and fractional values as well
jfriedri-ni Jul 25, 2025
25dd201
Add CVI representation helpers to TimeValueTuple
jfriedri-ni Jul 25, 2025
6626785
Fix docstring typo
jfriedri-ni Jul 25, 2025
3645d6a
Use the CVI helper when getting an item
jfriedri-ni Jul 25, 2025
d25b88f
Try using a generator when constructing the TimeDeltaArray
jfriedri-ni Jul 25, 2025
0bdac3d
Address formatter to get to the type checker
jfriedri-ni Jul 25, 2025
7af2a9a
Iterate over the input sequence when constructing the np array
jfriedri-ni Jul 25, 2025
f35b7ff
Add a test for indexing by integer
jfriedri-ni Jul 25, 2025
268351f
Use np.fromiter() to initialize the backing ndarray
jfriedri-ni Jul 25, 2025
6a6861e
Add test for len()
jfriedri-ni Jul 25, 2025
363740a
Add TimeDeltaArray constructor benchmark tests
jfriedri-ni Jul 25, 2025
56f0d7c
Promote to MutableSequence
jfriedri-ni Jul 28, 2025
27a0c91
Add signatures for remaining MutableSequence methods
jfriedri-ni Jul 28, 2025
37470da
Add impl and test for slicing
jfriedri-ni Jul 28, 2025
30dda3a
Add test for indexing with invalid indices
jfriedri-ni Jul 28, 2025
27faef7
Add test cases for negative index and out of bounds index
jfriedri-ni Jul 29, 2025
c1a2265
Implement and test __setitem__()
jfriedri-ni Jul 29, 2025
f116a78
Implement and test __delitem__()
jfriedri-ni Jul 29, 2025
13fa2a1
Implement and test insert()
jfriedri-ni Jul 29, 2025
1600306
Validate and test constructor argument
jfriedri-ni Jul 30, 2025
4f421e6
Test constructing from another TimeDeltaArray
jfriedri-ni Jul 30, 2025
67db372
Test MutableSequence mixin methods
jfriedri-ni Jul 30, 2025
dceb88c
Implement and test builtin functions
jfriedri-ni Jul 30, 2025
cc7fd8a
Address formatter
jfriedri-ni Jul 30, 2025
afa6e19
Optimize when slicing
jfriedri-ni Jul 31, 2025
57d39af
Clarify error messages
jfriedri-ni Jul 31, 2025
0228bf0
Implement and test __imul__()
jfriedri-ni Jul 31, 2025
f1d20e2
Remove antipattern from benchmark test
jfriedri-ni Jul 31, 2025
0a86f24
Update README and .gitignore to support comparing benchmarks
jfriedri-ni Jul 31, 2025
356d82e
Group the constructor benchmarks into a single test with parametrize
jfriedri-ni Jul 31, 2025
69ae183
Add docstrings from Claude Sonnet 4
jfriedri-ni Aug 1, 2025
d613342
Remove implementation details and example usage from method docstrings
jfriedri-ni Aug 1, 2025
4865b2c
Add TimeDeltaArray to module docstring
jfriedri-ni Aug 1, 2025
192ea1c
Address the analyzers
jfriedri-ni Aug 1, 2025
9f2aac4
Fix docstring tests
jfriedri-ni Aug 1, 2025
b45507b
Switch to google-style docstrings
jfriedri-ni Aug 1, 2025
55c524e
Remove unnecessary detail from descriptions, rely on default docgen b…
jfriedri-ni Aug 1, 2025
2d011fd
Remove the how-to-use narrative section
jfriedri-ni Aug 1, 2025
f280f97
Guess and check -- was it this one?
jfriedri-ni Aug 1, 2025
6935936
Allow indexing with bools
jfriedri-ni Aug 5, 2025
547d051
Use object for __eq__'s type hint on other
jfriedri-ni Aug 5, 2025
4b6e25a
Use flattened strings for test parameter declarations
jfriedri-ni Aug 5, 2025
c5b6fde
Make test section comments match other files' style
jfriedri-ni Aug 5, 2025
b3169c2
Remove __imul__ and tests
jfriedri-ni Aug 5, 2025
e504a6f
Use error factories for user input validation
jfriedri-ni Aug 5, 2025
21f3b09
Fix test name typo
jfriedri-ni Aug 6, 2025
7a9f9a2
Validate that an unpickled TimeDeltaArray has its own backing array
jfriedri-ni Aug 6, 2025
01a2aa4
Remove test for sorted
jfriedri-ni Aug 6, 2025
03ffd4e
Add test for deepcopy()
jfriedri-ni Aug 6, 2025
17c090f
Use pytest parameters for validating equals
jfriedri-ni Aug 6, 2025
7b91f69
TDD: use full truth-table coverage for setting with a slice
jfriedri-ni Aug 6, 2025
4891ab4
Make TimeDeltaArray behave like list when setting by slice
jfriedri-ni Aug 6, 2025
fdb40bb
Unpack the slice into named variables
jfriedri-ni Aug 7, 2025
9e3c67a
Prefer builtin functions over magic methods
jfriedri-ni Aug 7, 2025
8543def
Group the slice assignment tests together
jfriedri-ni Aug 7, 2025
3b198e0
Add test cases for settings mismatched-length values on contiguous sl…
jfriedri-ni Aug 7, 2025
89b857a
Raise when trying to shrink the array using a stride
jfriedri-ni Aug 7, 2025
896abc8
Do not hokey-pokey when setting a single value with a slice
jfriedri-ni Aug 7, 2025
8c87415
Group selected==incoming test cases
jfriedri-ni Aug 7, 2025
83e6bcf
Remove 'expected to raise' test cases that should pass for plain lists
jfriedri-ni Aug 7, 2025
f9ff477
Group and add mismatched slice vs incoming test cases
jfriedri-ni Aug 7, 2025
c891490
Address the analyzers
jfriedri-ni Aug 7, 2025
26a18ba
Contiguous slices accept any number of incoming entries
jfriedri-ni Aug 7, 2025
176aa33
Implement plain old list slice assignment
jfriedri-ni Aug 7, 2025
157d3e4
Use np.insert to grow the array once
jfriedri-ni Aug 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/nitypes/bintime/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
)
from nitypes.bintime._time_value_tuple import TimeValueTuple
from nitypes.bintime._timedelta import TimeDelta
from nitypes.bintime._timedelta_array import TimeDeltaArray

__all__ = [
"DateTime",
Expand All @@ -82,6 +83,7 @@
"CVITimeIntervalBase",
"CVITimeIntervalDType",
"TimeDelta",
"TimeDeltaArray",
"TimeValueTuple",
]

Expand Down
63 changes: 63 additions & 0 deletions src/nitypes/bintime/_timedelta_array.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from __future__ import annotations

from collections.abc import Sequence
from typing import (
Union,
final,
overload,
)

import numpy as np

from nitypes.bintime import CVITimeIntervalDType, TimeDelta, TimeValueTuple


@final
class TimeDeltaArray(Sequence[TimeDelta]):
"""An array of TimeDelta values in NI Binary Time Format (NI-BTF)."""

__slots__ = ["_array"]

_array: np.ndarray

def __init__(
self,
value: Union[Sequence[TimeDelta], None] = None,
) -> None:
"""Initialize a new TimeDeltaArray."""
if value is None:
value = []
self._array = np.zeros(len(value), dtype=CVITimeIntervalDType)
for index, entry in enumerate(value):
self._array[index] = entry.to_tuple()

@overload
def __getitem__( # noqa: D105 - missing docstring in magic method
self, index: int
) -> TimeDelta: ...

@overload
def __getitem__( # noqa: D105 - missing docstring in magic method
self, index: slice
) -> Sequence[TimeDelta]: ...

def __getitem__(self, index: Union[int, slice]) -> Union[TimeDelta, Sequence[TimeDelta]]:
"""Return the TimeDelta at the specified location."""
if isinstance(index, int):
entry = self._array[index]
# Without casting via int(), local variable as_tuple holds
# TimeValueTuple(whole_seconds=np.uint64(1), fractional_seconds=np.int64(0))
# and TimeDelta.from_tuple() raises
# TypeError: ufunc 'bitwise_or' not supported for the input types, and the inputs
# could not be safely coerced to any supported types according to the casting rule
# ''safe''
as_tuple = TimeValueTuple(int(entry[0]), int(entry[1]))
return TimeDelta.from_tuple(as_tuple)
elif isinstance(index, slice):
raise NotImplementedError("TODO AB#3137071")
else:
raise TypeError("Index must be an int or slice")

def __len__(self) -> int:
"""Return the length of the array."""
return np.size(self._array)
26 changes: 26 additions & 0 deletions tests/unit/bintime/test_timedelta_array.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from __future__ import annotations

from typing_extensions import assert_type

from nitypes.bintime import TimeDelta
from nitypes.bintime import TimeDeltaArray


def test___no_args___construct___returns_empty_array() -> None:
value = TimeDeltaArray()

assert_type(value, TimeDeltaArray)
assert isinstance(value, TimeDeltaArray)
assert len(value._array) == 0


def test___list_arg___construct___returns_matching_array() -> None:
arg = [TimeDelta(1), TimeDelta(20), TimeDelta(500)]
value = TimeDeltaArray(arg)

assert_type(value, TimeDeltaArray)
assert isinstance(value, TimeDeltaArray)
assert len(value._array) == len(arg)
assert tuple(value._array[0]) == TimeDelta(1).to_tuple()
assert tuple(value._array[1]) == TimeDelta(20).to_tuple()
assert tuple(value._array[2]) == TimeDelta(500).to_tuple()
Loading