55
66from __future__ import annotations
77
8+ from collections .abc import Iterable
89from copy import deepcopy
910from dataclasses import dataclass
1011from datetime import datetime , timedelta
11- from typing import Any , Generic , List , Sequence , TypeVar
12+ from typing import Any , Generic , List , TypeVar , overload
1213
1314import numpy as np
14- from numpy .lib import math
15+ import numpy .typing as npt
1516
16- from frequenz . sdk . timeseries import Sample
17+ from . import Sample
1718
18- Container = TypeVar ("Container " , list , np .ndarray )
19+ FloatArray = TypeVar ("FloatArray " , List [ float ], npt . NDArray [ np .float64 ] )
1920
2021
2122@dataclass
@@ -42,12 +43,12 @@ def contains(self, timestamp: datetime):
4243 return False
4344
4445
45- class OrderedRingBuffer (Generic [Container ]):
46+ class OrderedRingBuffer (Generic [FloatArray ]):
4647 """Time aware ringbuffer that keeps its entries sorted by time."""
4748
4849 def __init__ (
4950 self ,
50- buffer : Container ,
51+ buffer : FloatArray ,
5152 sampling_period : timedelta ,
5253 time_index_alignment : datetime = datetime (1 , 1 , 1 ),
5354 ) -> None :
@@ -129,7 +130,7 @@ def update(self, sample: Sample) -> None:
129130 # Don't add outdated entries
130131 if timestamp < self ._datetime_oldest and self ._datetime_oldest != datetime .max :
131132 raise IndexError (
132- f"Timestamp too old (cut-off is at { self ._datetime_oldest } )."
133+ f"Timestamp { timestamp } too old (cut-off is at { self ._datetime_oldest } )."
133134 )
134135
135136 # Update timestamps
@@ -138,7 +139,7 @@ def update(self, sample: Sample) -> None:
138139 self ._datetime_oldest = self ._datetime_newest - self ._time_range
139140
140141 # Update data
141- value : float = math .nan if sample .value is None else sample .value
142+ value : float = np .nan if sample .value is None else sample .value
142143 self ._buffer [self .datetime_to_index (timestamp )] = value
143144
144145 self ._update_gaps (timestamp , prev_newest , sample .value is None )
@@ -179,7 +180,7 @@ def datetime_to_index(
179180
180181 def window (
181182 self , start : datetime , end : datetime , force_copy : bool = False
182- ) -> Container :
183+ ) -> FloatArray :
183184 """Request a view on the data between start timestamp and end timestamp.
184185
185186 If the data is not used immediately it could be overwritten.
@@ -204,7 +205,7 @@ def window(
204205 copy of the data.
205206
206207 Raises:
207- IndexError: when requesting a window with invalid timestamps.
208+ IndexError: When requesting a window with invalid timestamps.
208209
209210 Returns:
210211 The requested window
@@ -228,14 +229,13 @@ def window(
228229 window = np .concatenate ((window , self ._buffer [0 :end_index ]))
229230 return window
230231
231- def in_gap (gap ) -> bool :
232- return gap .contains (start ) or gap .contains (end )
233-
234232 # Return a copy if there are none-values in the data
235- if force_copy or any (map (in_gap , self ._gaps )):
236- return deepcopy (self ._buffer [start_index :end_index ])
233+ if force_copy or any (
234+ map (lambda gap : gap .contains (start ) or gap .contains (end ), self ._gaps )
235+ ):
236+ return deepcopy (self [start_index :end_index ])
237237
238- return self . _buffer [start_index :end_index ]
238+ return self [start_index :end_index ]
239239
240240 def is_missing (self , timestamp : datetime ) -> bool :
241241 """Check if the given timestamp falls within a gap.
@@ -386,7 +386,33 @@ def _wrap(self, index: int) -> int:
386386 """
387387 return index % self .maxlen
388388
389- def __setitem__ (self , index_or_slice : int | slice , value : float ) -> None :
389+ @overload
390+ def __setitem__ (self , index_or_slice : slice , value : Iterable [float ]) -> None :
391+ """Set values at the request slice positions.
392+
393+ No wrapping of the index will be done.
394+ Create a feature request if you require this function.
395+
396+ Args:
397+ index_or_slice: Slice specification of the requested data.
398+ value: Sequence of value to set at the given range.
399+ """
400+
401+ @overload
402+ def __setitem__ (self , index_or_slice : int , value : float ) -> None :
403+ """Set value at requested index.
404+
405+ No wrapping of the index will be done.
406+ Create a feature request if you require this function.
407+
408+ Args:
409+ index_or_slice: Index of the data.
410+ value: Value to set at the given position.
411+ """
412+
413+ def __setitem__ (
414+ self , index_or_slice : int | slice , value : float | Iterable [float ]
415+ ) -> None :
390416 """Set item or slice at requested position.
391417
392418 No wrapping of the index will be done.
@@ -398,7 +424,29 @@ def __setitem__(self, index_or_slice: int | slice, value: float) -> None:
398424 """
399425 self ._buffer .__setitem__ (index_or_slice , value )
400426
401- def __getitem__ (self , index_or_slice : int | slice ) -> float | Sequence [float ]:
427+ @overload
428+ def __getitem__ (self , index_or_slice : int ) -> float :
429+ """Get item at requested position.
430+
431+ No wrapping of the index will be done.
432+ Create a feature request if you require this function.
433+
434+ Args:
435+ index_or_slice: Index of the requested data.
436+ """
437+
438+ @overload
439+ def __getitem__ (self , index_or_slice : slice ) -> FloatArray :
440+ """Get the data described by the given slice.
441+
442+ No wrapping of the index will be done.
443+ Create a feature request if you require this function.
444+
445+ Args:
446+ index_or_slice: Slice specification of where the requested data is.
447+ """
448+
449+ def __getitem__ (self , index_or_slice : int | slice ) -> float | FloatArray :
402450 """Get item or slice at requested position.
403451
404452 No wrapping of the index will be done.
0 commit comments