Package: pyring
PyRingA pure python implementation of a ring buffer with optional factory for alternate memory allocation. Variants included are Blocking (with a read cursor) and Locked (all manipulations are secured with a lock) ring buffers.You may not call it a ring buffer, they also go by other names like circular buffer, circular queue or cyclic buffer.Installationpython3 -m pip install pyringUsageIncluded are several variations of a ring buffer:RingBuffer- The most basic, non-blocking ring, supports optional RLocks as constructor argument.LockedRingBuffer- The same asRingBufferbut secured by a lock (handy for multithread / multiproc)BlockingRingBuffer- Extension ofRingBufferwith a read methodnext()which increments a read cursor and the writer cannot advance past the read cursorBlockingLockedRingBuffer- The same asBlockingRingBufferbut secured by a lock (handy for multithread / multiproc)WaitingBlockingRingBuffer- The same asBlockingRingBufferbut calls to next and put block and wait (with optional timeout arg)SingleProducerDisruptor- The same asWaitingBlockingRingBufferbut allows multiple subscribers to a single ring buffer by callingsubscribe()which returns aDisruptorSubscriberobject. No next method on disruptor, instead it is on theDisruptorSubscriberobjects.Basic usage with default size and factoryimportpyring# create ring bufferring_buffer=pyring.RingBuffer()# add to ringring_buffer.put("Something new!")# get latest from ringsequence,value=ring_buffer.get_latest()print(sequence,value)# 0 Something new!Custom ring sizeimportpyringring_buffer=pyring.RingBuffer(size=128)# size must be a power of 2Custom factoryimportpyring# custom factory that takes lists of ints and returns the sum# must declare the get and set methodsclassCustomSumFactory(pyring.RingFactory):value:typing.List[int]=[]defget(self):returnsum(self.value)defset(self,values):self.value=valuesring_buffer=pyring.RingBuffer(factory=CustomSumFactory)ring_buffer.put([1,2,3,4,5])sequence,value=ring_buffer.get_latest()print(sequence,value)# 0 15Multiprocess C-Types ExampleThe below is a demonstration of using a RingBuffer across multiple processes, this requires firstly a LockedRingBuffer (with a multiproc lock), in addition to a custom cursor_position_value to increment sequence counts across processes.This is more a demonstration on how flexible this ring buffer implementation can be rather than where you should use it, the below approach would most likely (with caveats on datasize) be better handled with threads reading off the ring buffer and passing messages via queues to worker processes.importmultiprocessingasmpimporttimefrompyringimportLockedRingBuffer,RingFactory# using r lock due to the reuse for the cursor_position_valuemp_lock=mp.RLock()# note if using the same lock it must be recursive lock otherwise you will get deadlockscursor_position_value=mp.Value("i",0,lock=mp_lock)# using multiproc compatible c-types.classMultiprocFactory(RingFactory):def__init__(self):self.value=mp.Value("i")defset(self,v:int):self.value.value=vdefget(self):returnself.value.valuering_buffer=LockedRingBuffer(factory=MultiprocFactory,lock=mp_lock,cursor_position_value=cursor_position_value)defworker_routine(worker_ring_buffer:LockedRingBuffer):foriinrange(10):worker_ring_buffer.put(i)proc=mp.Process(target=worker_routine,args=(ring_buffer,))proc.start()time.sleep(0.01)foriinrange(10):sequence,value=ring_buffer.get(i)print(sequence,value)proc.join()Single Producer Disruptor (Multiple Subscribers)frompyringimportSingleProducerDisruptordisruptor=SingleProducerDisruptor()subscriber_one=disruptor.subscribe()subscriber_two=disruptor.subscribe()foriinrange(100):disruptor.put(i**2)sequence_one,res_one=subscriber_one.next()sequence_two,res_two=subscriber_two.next()# releases the subscribers barriers and allows disruptor to continuesubscriber_one.unregister()subscriber_two.unregister()Examples of UsageCOMING SOONContributingPull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.LicenseMIT