Package: rxbp
RxPy back-pressure extensionrxbpis an extension to theRxPYpython
library, that integrates back-pressure into theObservablepattern
in form ofFlowables.Therxbplibrary is inspired byMonix,
andhas still an experimental status.Installationrxbp v3.x runs on Python 3.7 or above. To install rxbp alpha version:pip3 install --pre rxbpExamplerxbackpressurehas a similar syntax as RxPY.# example taken from RxPYimportrxbpsource=rxbp.from_(["Alpha","Beta","Gamma","Delta","Epsilon"])composed=source.pipe(rxbp.op.map(lambdas:len(s)),rxbp.op.filter(lambdai:i>=5))composed.subscribe(lambdavalue:print(f"Received{value}"))Integrate RxPYA RxPY Observable can be converted to aFlowableby using therxbp.from_rxfunction.
Equivalently, aFlowablecan be converted to an RxPY Observable
by using theto_rxfunction.importrximportrxbprx_source=rx.of("Alpha","Beta","Gamma","Delta","Epsilon")# convert Observable to Flowablesource=rxbp.from_rx(rx_source)composed=source.pipe(rxbp.op.map(lambdas:len(s)),rxbp.op.filter(lambdai:i>=5))# convert Flowable to Observablecomposed.to_rx().subscribe(lambdavalue:print(f"Received{value}"))Differences from RxPYFlowableSimilar to an RxPY Observable, aFlowableimplements asubscribemethod,
which is a mechanism that allows to describe a data flow from its source to
a sink. The description is done withrxbpoperators exposed byrxbp.op.Like in functional programming, usingsrxbpoperators
does not create any mutable states, but rather concatenates functions
without calling them yet. We first describe what we intend to
do in form of a plan and then execute the plan. AFlowableis
executed by calling itssubscribemethod. This will start a chain
reaction, where each downsreamFlowablescalls thesubscribemethod of its upstreamFlowableuntil
the sources start emitting the data. Once aFlowableis subscribed, we
allow it to have internal mutable states.Compared to RxPY Observables, however, aFlowableusesObserversthat are
able to back-pressure on anon_nextmethod call. This has the effect that
certain operators behave differently from the ones in RxPY.MultiCast (experimental)AMultiCastis used when aFlowableemits elements to more than oneObserver,
and can be though of a nestedFlowableof typeObservable[T[Flowable]].The syntax tomulti-casta Flowable is quite different from RxPY and there are good
reasons for that. In RxPY, there is an operator calledshare, that turns anObservableinto a so-called hotObservableallowing multiple downstream subscribers to receive the
same elements. The firstsubscribecall has the side-effect that subsequentsubscribecalls will not propagate upstream, but register themselves to the hotObservable.
The following example illustrates the side-effect that happens when a sharedObservableis subscribed for the first time.importrxfromrximportoperatorsasopo=rx.range(4).pipe(op.share(),)o.subscribe(print)o.subscribe(print)# the second time no elements are sentThe previous code outputs:0
1
2
3Inrxbp, however, the elements of aFlowablesequence can only be multi-casted,
if theFlowableis nested inside aMultiCast. This can be done with therxbp.multicast.return_flowablefunction.return_flowabletakes aFlowable, a
list ofFlowablesor a dictionary ofFlowablesand creates aMultiCastthat
emits the nestedFlowables. Similarly to aFlowable, aMultiCastimplements apipemethod that takes a sequence ofMultiCastoperators, which are exposed byrxbp.multicast.op.importrxbpf=rxbp.multicast.return_flowable(rxbp.range(10)).pipe(rxbp.multicast.op.map(lambdabase:base.pipe(rxbp.op.zip(base.pipe(rxbp.op.map(lambdav:v+1),rxbp.op.filter(lambdav:v%2==0)),),)),).to_flowable()f.subscribe(print)The previous code outputs:(0, 2)
(1, 4)
(2, 6)
(3, 8)
(4, 10)match operator (experimental)Thematchoperator tries to match twoFlowables, and raises an exception otherwise.
TwoFlowablesmatch if they have the same base or if there exists a mapping that maps
one base to the base of the otherFlowable. These mappings propagated internally when
subscribing to aFlowable.If twoFlowablesmatch, the elements of eachFlowablesequence are filtered and
dublicated (if necessary) first and then zipped together. The following example creates
twoFlowableswhere one is having base10and the other contains a mapping from
base10to it's own baseNone(baseNonerefers to a unknownFlowablesequence).
Thematchoperator applies the mapping to the Flowable of base10such that every
second element is selected due tov % 2.importrxbprxbp.from_range(10).pipe(rxbp.op.match(rxbp.from_range(10).pipe(rxbp.op.filter(lambdav:v%2==0)),)).subscribe(print)The previous code outputs:(1, 1)
(3, 3)
(5, 5)
(7, 7)
(9, 9)When to use a Flowable, when RxPY Observable?AFlowableis used when some asynchronous stage cannot process the data fast enough,
or needs to synchronize the data with some other event. Let's take thezipoperator
as an example. It receives elements from two or more sources and emits a tuple once it
received one element from each source. But what happens if one source emits the
elements before the other does? Without back-pressure, thezipoperator has to buffer
the elements from the eager source until it receives the elements from the other source.
This might be ok depending on how many elements need to be buffered. But often it is too
risky to buffer elements somewhere in our stream as it potentially leads to an
out of memory exception. The back-pressure capability prevents buffers to grow by holding
the data back until it is actually needed.The advantage of a RxPY Observable is that it is generally faster and more lightweight.FlowableCreate a Flowableempty- create aFlowableemitting no elementsfrom_- create aFlowablethat emits each element of an iterablefrom_iterable- seefrom_from_list- create aFlowablethat emits each element of a listfrom_range- create aFlowablethat emits elements defined by the rangefrom_rx- wrap a rx.Observable and exposes it as aFlowable, relaying signals in a backpressure-aware manner.return_flowable- create aFlowablethat emits a single elementTransforming operatorsfilter- emit only those elements for which the given predicate holdsfirst- emit the first element onlyflat_map- apply a function to each item emitted by the source and
flattens the resultmap- map each element emitted by the source by applying the given
functionmap_to_iterator- create aFlowablethat maps each element emitted
by the source to an iterator and emits each element of these iterators.pairwise- create aFlowablethat emits a pair for each consecutive
pairs of elements in theFlowablesequencereduce- Apply an accumulator function over a Flowable sequence and
emits a single elementrepeat_first- Return aFlowablethat repeats the first element it
receives from the source forever (until disposed).scan- apply an accumulator function over aFlowablesequence and
returns each intermediate result.to_list- Create a newFlowablethat collects the elements from
the source sequence, and emits a single element of type List.zip_with_index- zip each item emitted by the source with the
enumerated indexCombining operatorsconcat- ConcatentatesFlowablesequences together by back-pressuring
the tailFlowablesuntil the currentFlowablehas completedcontrolled_zip- create a newFlowablefrom twoFlowablesby combining
their elements in pairs. Which element gets paired with an element from
the otherFlowableis determined by two functions calledrequest_leftandrequest_rightmatch- create a newFlowablefrom twoFlowablesby first filtering and
duplicating (if necessary) the elements of eachFlowableand zip the resultingFlowablesequences togethermerge- merge the elements of theFlowablesequences into a singleFlowablezip- Create a newFlowablefrom twoFlowablesby combining their
item in pairs in a strict sequenceOther operatorsbuffer- buffer the element emitted by the source without back-pressure until
the buffer is fulldebug- print debug messages to the consoleexecute_on- inject new scheduler that is used to subscribe theFlowableobserve_on- schedule elements emitted by the source on a dedicated schedulerset_base- overwrite the base of the current Flowable sequenceshare- multi-cast the elements of theFlowableto possibly
multiple subscribersCreate a rx Observableto_rx- create a rx Observable from a ObservableMultiCast (experimental)Create a MultiCastempty- create aMultiCastemitting no elementsreturn_flowable- turn zero or moreFlowablesinto multi-castFlowablesemitted as a single element inside aMultiCastreturn_- create aMultiCastemitting a single elementfrom_iterable- create aMultiCastfrom an iterablefrom_rx_observable- create aMultiCastfrom anrx.Observablefrom_flowable- (similar tofrom_rx_observable) create aMultiCastthat emits each element received by the FlowableTransforming operatorsdefault_if_empty- either emits the elements of the source or a default elementfilter- emit only thoseMultiCastfor which the given predicate holdflat_map- apply a function to each item emitted by the source and
flattens the resultlift- lift the currentMultiCast[T1]to aMultiCast[T2[MultiCast[T1]]].map- map each element emitted by the source by applying the given
functionmerge- merge the elements of theMultiCastsequences into a singleMultiCastTransforming operators (Flowables)join_flowables- zip one or moreMulticasts(each emitting a singleFlowable)
to aMulticastemitting a single element (tuple ofFlowables)loop_flowables- create a loop insideFlowablescollect_flowables- create aMulticastthat emits a single element containing
the reducedFlowablesof the first element sent by the sourceOther operatorsdebug- print debug messages to the consoleobserve_on- schedule elements emitted by the source on a dedicated schedulershare- multi-cast the elements of the source to possibly
multiple subscribers