A small, dependency-free Python ring buffer (a.k.a. circular buffer) with O(1) append, overwrite-oldest semantics when full, indexable, slice-able, and reversible.
ringbuf exists to give you the classical fixed-capacity buffer
without dragging in collections.deque(maxlen=...)'s linked-list
overhead, and without the "throws when full" surprise of
queue.Queue(maxsize=...). When the buffer is full and you append
again, the oldest item is silently overwritten � exactly what you
want for rolling logs, sliding windows, latest-N caches, and
event-loop telemetry.
deque(maxlen=n) is the standard answer and works well, but:
dequeis a linked list of blocks; random access by index is O(n).- A ring buffer backed by a list is O(1) for indexing and for forward / reverse iteration without rotation.
dequedoes not exposeis_full,peek_oldest,peek_newest, or a typed empty-error.
If you need a thread-safe queue, reach for queue.Queue. This
library targets the single-threaded case.
pip install ringbufringbuf requires Python 3.10 or newer. Zero runtime dependencies.
from ringbuf import RingBuffer
rb: RingBuffer[int] = RingBuffer(capacity=3)
rb.append(1)
rb.append(2)
rb.append(3)
rb.append(4) # overwrites 1
list(rb) # [2, 3, 4]
rb[0], rb[-1] # (2, 4)
rb.peek_oldest() # 2
rb.peek_newest() # 4
rb.popleft() # 2 -> rb is now [3, 4]
rb.pop() # 4 -> rb is now [3]You can construct a buffer pre-filled from any iterable. If the
iterable is longer than the capacity, only the last capacity
items survive � same semantics as append repeated:
rb = RingBuffer(3, range(10))
list(rb) # [7, 8, 9]Construct a buffer holding at most capacity items.
capacitymust be a positiveint. Booleans are rejected soRingBuffer(True)does not silently meanRingBuffer(1).iterable, if given, populates the buffer in order. Items beyondcapacityoverwrite earlier ones.
Raises CapacityError (subclass of ValueError) on invalid input.
| Method | Description |
|---|---|
append(item) |
O(1). Appends to the newest end. Overwrites the oldest item if the buffer is full. |
extend(iter) |
Appends every item from iter. Same overwrite behaviour as append repeated. |
pop() |
O(1). Removes and returns the newest item. Raises RingBufferEmpty if empty. |
popleft() |
O(1). Removes and returns the oldest item. Raises RingBufferEmpty if empty. |
clear() |
Removes all items. Capacity is unchanged. |
| Method / property | Description |
|---|---|
peek_oldest() |
Returns the oldest item without removing it. |
peek_newest() |
Returns the newest item without removing it. |
to_list() |
Returns a list snapshot in oldest-to-newest order. |
capacity |
The configured capacity (read-only). |
is_full |
True when the buffer holds capacity items. |
is_empty |
True when the buffer holds zero items. |
len(rb)� number of items currently stored.iter(rb)� yields oldest � newest.reversed(rb)� yields newest � oldest.rb[i]� positional access. Negative indices count from the newest end.IndexErroris raised on out-of-range.rb[a:b:s]� slice access returns alist.item in rb� membership test using equality.rb == other� true iffotheris aRingBufferwith the same capacity and the same items in the same order.repr(rb)�RingBuffer(capacity=N, items=[�]).- The buffer is unhashable, matching
listanddeque.
RingBufferError� base class for every error this library raises.RingBufferEmpty� raised bypop,popleft,peek_oldest,peek_newestwhen the buffer is empty. Subclass ofIndexError, so existingexcept IndexErrorblocks still catch it.CapacityError� raised by the constructor on invalid capacity. Subclass ofValueError.
RingBuffer(0)raisesCapacityError. So does any negative or non-intcapacity. Booleans are rejected becauseTrueandFalseare technicallyintin Python and this library refuses to guess.RingBuffer(1)is allowed. Every append replaces the only item.- After enough appends to wrap, indexing and iteration still report oldest-to-newest order; the wrap is invisible to callers.
__contains__only finds items currently in the buffer. Items that were overwritten are gone.RingBufferis not thread-safe.
git clone https://github.com/nripankadas07/ringbuf
cd ringbuf
pip install -e ".[dev]"
pytestTests are organised by topic under tests/:
tests/test_basic.py � happy path
tests/test_iteration.py � forward / reverse iteration, post-wrap order
tests/test_indexing.py � int / negative / slice indexing, contains
tests/test_errors.py � CapacityError, RingBufferEmpty surfaces
tests/test_protocols.py � equality, repr, hashability, truthiness
tests/test_lifecycle.py � long-running append/pop sequences
Coverage is 100% line-and-statement on the source tree.
MIT � see LICENSE.