rxbp


Namerxbp JSON
Version 3.0.0a11 PyPI version JSON
download
home_pagehttps://github.com/MichaelSchneeberger/rxbackpressure
SummaryAn RxPY extension with back-pressure
upload_time2020-09-21 08:04:08
maintainer
docs_urlNone
authorMichael Schneeberger
requires_python>=3.7
license
keywords rx reactive extension back-pressure backpressure flowable multicast
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            
RxPy back-pressure extension
============================

![Build Status](https://github.com/MichaelSchneeberger/rxbackpressure/workflows/build/badge.svg)
[![Coverage Status](https://coveralls.io/repos/github/MichaelSchneeberger/rxbackpressure/badge.svg?branch=master)](https://coveralls.io/github/MichaelSchneeberger/rxbackpressure?branch=master)
![Package Publish Status](https://github.com/MichaelSchneeberger/rxbackpressure/workflows/pypi/badge.svg)

*rxbp* is an extension to the [RxPY](https://github.com/ReactiveX/RxPY) python 
library, that integrates back-pressure into the *Observable* pattern
in form of *Flowables*.

The *rxbp* library is inspired by [Monix](https://github.com/monix/monix), 
and **has still an experimental status**. 

Installation
------------

rxbp v3.x runs on Python 3.7 or above. To install rxbp alpha version:

```
pip3 install --pre rxbp
```

Example
-------

*rxbackpressure* has a similar syntax as RxPY.

```python
# example taken from RxPY
import rxbp

source = rxbp.from_(["Alpha", "Beta", "Gamma", "Delta", "Epsilon"])

composed = source.pipe(
    rxbp.op.map(lambda s: len(s)),
    rxbp.op.filter(lambda i: i >= 5)
)
composed.subscribe(lambda value: print(f"Received {value}"))
```

Integrate RxPY
--------------

A RxPY Observable can be converted to a *Flowable* by using the `rxbp.from_rx` function.
Equivalently, a *Flowable* can be converted to an RxPY Observable 
by using the `to_rx` function.

```python
import rx
import rxbp

rx_source = rx.of("Alpha", "Beta", "Gamma", "Delta", "Epsilon")

# convert Observable to Flowable
source = rxbp.from_rx(rx_source)

composed = source.pipe(
    rxbp.op.map(lambda s: len(s)),
    rxbp.op.filter(lambda i: i >= 5)
)

# convert Flowable to Observable
composed.to_rx().subscribe(lambda value: print(f"Received {value}"))
```

Differences from RxPY
---------------------

### Flowable

Similar to an RxPY Observable, a *Flowable* implements a `subscribe` method,
which is a mechanism that allows to describe a data flow from its source to 
a sink. The description is done with *rxbp* operators exposed by `rxbp.op`.

Like in functional programming, usings *rxbp* operators 
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. A *Flowable* is 
executed by calling its `subscribe` method. This will start a chain 
reaction, where each downsream *Flowables* calls the `subscribe` 
method of its upstream *Flowable* until
the sources start emitting the data. Once a *Flowable* is subscribed, we
allow it to have internal mutable states.

Compared to RxPY Observables, however, a *Flowable* uses `Observers` that are
able to back-pressure on an `on_next` method call. This has the effect that
certain operators behave differently from the ones in RxPY.

### MultiCast (experimental)

A *MultiCast* is used when a *Flowable* emits elements to more than one `Observer`, 
and can be though of a nested *Flowable* of type `Observable[T[Flowable]]`.

The syntax to *multi-cast* a Flowable is quite different from RxPY and there are good
reasons for that. In RxPY, there is an operator called `share`, that turns an *Observable* 
into a so-called hot *Observable* allowing multiple downstream subscribers to receive the 
same elements. The first `subscribe` call has the side-effect that subsequent `subscribe` 
calls will not propagate upstream, but register themselves to the hot *Observable*.
The following example illustrates the side-effect that happens when a shared *Observable*
is subscribed for the first time.

``` python
import rx
from rx import operators as op

o = rx.range(4).pipe(
    op.share(),
)

o.subscribe(print)
o.subscribe(print)      # the second time no elements are sent
```

The previous code outputs:

```
0
1
2
3
```

In *rxbp*, however, the elements of a *Flowable* sequence can only be multi-casted,
if the *Flowable* is nested inside a *MultiCast*. This can be done with the 
`rxbp.multicast.return_flowable` function. `return_flowable` takes a *Flowable*, a
list of *Flowables* or a dictionary of *Flowables* and creates a *MultiCast* that
emits the nested *Flowables*. Similarly to a *Flowable*, a *MultiCast* implements a `pipe`
method that takes a sequence of *MultiCast* operators, which are exposed by 
`rxbp.multicast.op`.

```python
import rxbp

f = rxbp.multicast.return_flowable(rxbp.range(10)).pipe(
    rxbp.multicast.op.map(lambda base: base.pipe(
        rxbp.op.zip(base.pipe(
            rxbp.op.map(lambda v: v + 1),
            rxbp.op.filter(lambda v: 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)

The `match` operator tries to match two *Flowables*, and raises an exception otherwise.
Two *Flowables* match if they have the same base or if there exists a mapping that maps 
one base to the base of the other *Flowable*. These mappings propagated internally when 
subscribing to a *Flowable*.

If two *Flowables* match, the elements of each *Flowable* sequence are filtered and
dublicated (if necessary) first and then zipped together. The following example creates
two *Flowables* where one is having base *10* and the other contains a mapping from
base *10* to it's own base *None* (base *None* refers to a unknown *Flowable* sequence). 
The `match` operator applies the mapping to the Flowable of base *10* such that every
second element is selected due to `v % 2`.


```python
import rxbp

rxbp.from_range(10).pipe(
    rxbp.op.match(rxbp.from_range(10).pipe(
        rxbp.op.filter(lambda v: 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?
-----------------------------------------

A *Flowable* is used when some asynchronous stage cannot process the data fast enough, 
or needs to synchronize the data with some other event. Let's take the `zip` operator 
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, the `zip` operator 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.


Flowable
--------

### Create a Flowable

- `empty` - create a *Flowable* emitting no elements
- `from_` - create a *Flowable* that emits each element of an iterable
- `from_iterable` - see `from_`
- `from_list` - create a *Flowable* that emits each element of a list
- `from_range` - create a *Flowable* that emits elements defined by the range
- `from_rx` - wrap a rx.Observable and exposes it as a *Flowable*, relaying signals in a backpressure-aware manner.
- `return_flowable` - create a *Flowable* that emits a single element

### Transforming operators

- `filter` - emit only those elements for which the given predicate holds
- `first` - emit the first element only
- `flat_map` - apply a function to each item emitted by the source and 
flattens the result
- `map` - map each element emitted by the source by applying the given 
function
- `map_to_iterator` - create a *Flowable* that maps each element emitted 
by the source to an iterator and emits each element of these iterators.
- `pairwise` - create a *Flowable* that emits a pair for each consecutive 
pairs of elements in the *Flowable* sequence
- `reduce` - Apply an accumulator function over a Flowable sequence and 
emits a single element
- `repeat_first` - Return a *Flowable* that repeats the first element it 
receives from the source forever (until disposed).
- `scan` - apply an accumulator function over a *Flowable* sequence and 
returns each intermediate result.
- `to_list` - Create a new *Flowable* that 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 index

### Combining operators

- `concat` - Concatentates *Flowable* sequences together by back-pressuring 
the tail *Flowables* until the current *Flowable* has completed
- `controlled_zip` - create a new *Flowable* from two *Flowables* by combining 
their elements in pairs. Which element gets paired with an element from 
the other *Flowable* is determined by two functions called `request_left` and 
`request_right`
- `match` - create a new *Flowable* from two *Flowables* by first filtering and 
duplicating (if necessary) the elements of each *Flowable* and zip the resulting 
*Flowable* sequences together
- `merge` - merge the elements of the *Flowable* sequences into a single *Flowable*
- `zip` - Create a new *Flowable* from two *Flowables* by combining their 
item in pairs in a strict sequence

### Other operators

- `buffer` - buffer the element emitted by the source without back-pressure until 
the buffer is full
- `debug` - print debug messages to the console
- `execute_on` - inject new scheduler that is used to subscribe the *Flowable*
- `observe_on` - schedule elements emitted by the source on a dedicated scheduler
- `set_base` - overwrite the base of the current Flowable sequence
- `share` - multi-cast the elements of the *Flowable* to possibly 
multiple subscribers

### Create a rx Observable

- `to_rx` - create a rx Observable from a Observable

MultiCast (experimental)
------------------------

### Create a MultiCast

- `empty` - create a *MultiCast* emitting no elements
- `return_flowable` - turn zero or more *Flowables* into multi-cast *Flowables* 
emitted as a single element inside a  *MultiCast*
- `return_` - create a *MultiCast* emitting a single element
- `from_iterable` - create a *MultiCast* from an iterable
- `from_rx_observable` - create a *MultiCast* from an *rx.Observable*
- `from_flowable` - (similar to `from_rx_observable`) create a *MultiCast* 
that emits each element received by the Flowable

### Transforming operators

- `default_if_empty` - either emits the elements of the source or a default element
- `filter` - emit only those *MultiCast* for which the given predicate hold
- `flat_map` - apply a function to each item emitted by the source and 
flattens the result
- `lift` - lift the current `MultiCast[T1]` to a `MultiCast[T2[MultiCast[T1]]]`.
- `map` - map each element emitted by the source by applying the given 
function
- `merge` - merge the elements of the *MultiCast* sequences into a single *MultiCast*

### Transforming operators (Flowables)

- `join_flowables` - zip one or more *Multicasts* (each emitting a single *Flowable*)
 to a *Multicast* emitting a single element (tuple of *Flowables*)
- `loop_flowables` - create a loop inside *Flowables*
- `collect_flowables` - create a *Multicast* that emits a single element containing 
the reduced *Flowables* of the first element sent by the source

### Other operators 

- `debug` - print debug messages to the console
- `observe_on` - schedule elements emitted by the source on a dedicated scheduler
- `share` - multi-cast the elements of the source to possibly 
multiple subscribers


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/MichaelSchneeberger/rxbackpressure",
    "name": "rxbp",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "rx reactive extension back-pressure backpressure flowable multicast",
    "author": "Michael Schneeberger",
    "author_email": "michael.schneeb@outlook.com",
    "download_url": "https://files.pythonhosted.org/packages/a7/bf/c91edbdf5550f99f57ef2c85cd8e92ec0039d076f74108acfa2d79257ee6/rxbp-3.0.0a11.tar.gz",
    "platform": "",
    "description": "\nRxPy back-pressure extension\n============================\n\n![Build Status](https://github.com/MichaelSchneeberger/rxbackpressure/workflows/build/badge.svg)\n[![Coverage Status](https://coveralls.io/repos/github/MichaelSchneeberger/rxbackpressure/badge.svg?branch=master)](https://coveralls.io/github/MichaelSchneeberger/rxbackpressure?branch=master)\n![Package Publish Status](https://github.com/MichaelSchneeberger/rxbackpressure/workflows/pypi/badge.svg)\n\n*rxbp* is an extension to the [RxPY](https://github.com/ReactiveX/RxPY) python \nlibrary, that integrates back-pressure into the *Observable* pattern\nin form of *Flowables*.\n\nThe *rxbp* library is inspired by [Monix](https://github.com/monix/monix), \nand **has still an experimental status**. \n\nInstallation\n------------\n\nrxbp v3.x runs on Python 3.7 or above. To install rxbp alpha version:\n\n```\npip3 install --pre rxbp\n```\n\nExample\n-------\n\n*rxbackpressure* has a similar syntax as RxPY.\n\n```python\n# example taken from RxPY\nimport rxbp\n\nsource = rxbp.from_([\"Alpha\", \"Beta\", \"Gamma\", \"Delta\", \"Epsilon\"])\n\ncomposed = source.pipe(\n    rxbp.op.map(lambda s: len(s)),\n    rxbp.op.filter(lambda i: i >= 5)\n)\ncomposed.subscribe(lambda value: print(f\"Received {value}\"))\n```\n\nIntegrate RxPY\n--------------\n\nA RxPY Observable can be converted to a *Flowable* by using the `rxbp.from_rx` function.\nEquivalently, a *Flowable* can be converted to an RxPY Observable \nby using the `to_rx` function.\n\n```python\nimport rx\nimport rxbp\n\nrx_source = rx.of(\"Alpha\", \"Beta\", \"Gamma\", \"Delta\", \"Epsilon\")\n\n# convert Observable to Flowable\nsource = rxbp.from_rx(rx_source)\n\ncomposed = source.pipe(\n    rxbp.op.map(lambda s: len(s)),\n    rxbp.op.filter(lambda i: i >= 5)\n)\n\n# convert Flowable to Observable\ncomposed.to_rx().subscribe(lambda value: print(f\"Received {value}\"))\n```\n\nDifferences from RxPY\n---------------------\n\n### Flowable\n\nSimilar to an RxPY Observable, a *Flowable* implements a `subscribe` method,\nwhich is a mechanism that allows to describe a data flow from its source to \na sink. The description is done with *rxbp* operators exposed by `rxbp.op`.\n\nLike in functional programming, usings *rxbp* operators \ndoes not create any mutable states, but rather concatenates functions \nwithout calling them yet. We first describe what we intend to \ndo in form of a plan and then execute the plan. A *Flowable* is \nexecuted by calling its `subscribe` method. This will start a chain \nreaction, where each downsream *Flowables* calls the `subscribe` \nmethod of its upstream *Flowable* until\nthe sources start emitting the data. Once a *Flowable* is subscribed, we\nallow it to have internal mutable states.\n\nCompared to RxPY Observables, however, a *Flowable* uses `Observers` that are\nable to back-pressure on an `on_next` method call. This has the effect that\ncertain operators behave differently from the ones in RxPY.\n\n### MultiCast (experimental)\n\nA *MultiCast* is used when a *Flowable* emits elements to more than one `Observer`, \nand can be though of a nested *Flowable* of type `Observable[T[Flowable]]`.\n\nThe syntax to *multi-cast* a Flowable is quite different from RxPY and there are good\nreasons for that. In RxPY, there is an operator called `share`, that turns an *Observable* \ninto a so-called hot *Observable* allowing multiple downstream subscribers to receive the \nsame elements. The first `subscribe` call has the side-effect that subsequent `subscribe` \ncalls will not propagate upstream, but register themselves to the hot *Observable*.\nThe following example illustrates the side-effect that happens when a shared *Observable*\nis subscribed for the first time.\n\n``` python\nimport rx\nfrom rx import operators as op\n\no = rx.range(4).pipe(\n    op.share(),\n)\n\no.subscribe(print)\no.subscribe(print)      # the second time no elements are sent\n```\n\nThe previous code outputs:\n\n```\n0\n1\n2\n3\n```\n\nIn *rxbp*, however, the elements of a *Flowable* sequence can only be multi-casted,\nif the *Flowable* is nested inside a *MultiCast*. This can be done with the \n`rxbp.multicast.return_flowable` function. `return_flowable` takes a *Flowable*, a\nlist of *Flowables* or a dictionary of *Flowables* and creates a *MultiCast* that\nemits the nested *Flowables*. Similarly to a *Flowable*, a *MultiCast* implements a `pipe`\nmethod that takes a sequence of *MultiCast* operators, which are exposed by \n`rxbp.multicast.op`.\n\n```python\nimport rxbp\n\nf = rxbp.multicast.return_flowable(rxbp.range(10)).pipe(\n    rxbp.multicast.op.map(lambda base: base.pipe(\n        rxbp.op.zip(base.pipe(\n            rxbp.op.map(lambda v: v + 1),\n            rxbp.op.filter(lambda v: v % 2 == 0)),\n        ),\n    )),\n).to_flowable()\nf.subscribe(print)\n```\n\nThe previous code outputs:\n\n```\n(0, 2)\n(1, 4)\n(2, 6)\n(3, 8)\n(4, 10)\n```\n\n\n### match operator (experimental)\n\nThe `match` operator tries to match two *Flowables*, and raises an exception otherwise.\nTwo *Flowables* match if they have the same base or if there exists a mapping that maps \none base to the base of the other *Flowable*. These mappings propagated internally when \nsubscribing to a *Flowable*.\n\nIf two *Flowables* match, the elements of each *Flowable* sequence are filtered and\ndublicated (if necessary) first and then zipped together. The following example creates\ntwo *Flowables* where one is having base *10* and the other contains a mapping from\nbase *10* to it's own base *None* (base *None* refers to a unknown *Flowable* sequence). \nThe `match` operator applies the mapping to the Flowable of base *10* such that every\nsecond element is selected due to `v % 2`.\n\n\n```python\nimport rxbp\n\nrxbp.from_range(10).pipe(\n    rxbp.op.match(rxbp.from_range(10).pipe(\n        rxbp.op.filter(lambda v: v % 2 == 0)),\n    )\n).subscribe(print)\n```\n\nThe previous code outputs:\n\n```\n(1, 1)\n(3, 3)\n(5, 5)\n(7, 7)\n(9, 9)\n```\n\nWhen to use a Flowable, when RxPY Observable?\n-----------------------------------------\n\nA *Flowable* is used when some asynchronous stage cannot process the data fast enough, \nor needs to synchronize the data with some other event. Let's take the `zip` operator \nas an example. It receives elements from two or more sources and emits a tuple once it \nreceived one element from each source. But what happens if one source emits the \nelements before the other does? Without back-pressure, the `zip` operator has to buffer \nthe elements from the eager source until it receives the elements from the other source.\nThis might be ok depending on how many elements need to be buffered. But often it is too\nrisky to buffer elements somewhere in our stream as it potentially leads to an \nout of memory exception. The back-pressure capability prevents buffers to grow by holding \nthe data back until it is actually needed.\n\nThe advantage of a RxPY Observable is that it is generally faster and more lightweight.\n\n\nFlowable\n--------\n\n### Create a Flowable\n\n- `empty` - create a *Flowable* emitting no elements\n- `from_` - create a *Flowable* that emits each element of an iterable\n- `from_iterable` - see `from_`\n- `from_list` - create a *Flowable* that emits each element of a list\n- `from_range` - create a *Flowable* that emits elements defined by the range\n- `from_rx` - wrap a rx.Observable and exposes it as a *Flowable*, relaying signals in a backpressure-aware manner.\n- `return_flowable` - create a *Flowable* that emits a single element\n\n### Transforming operators\n\n- `filter` - emit only those elements for which the given predicate holds\n- `first` - emit the first element only\n- `flat_map` - apply a function to each item emitted by the source and \nflattens the result\n- `map` - map each element emitted by the source by applying the given \nfunction\n- `map_to_iterator` - create a *Flowable* that maps each element emitted \nby the source to an iterator and emits each element of these iterators.\n- `pairwise` - create a *Flowable* that emits a pair for each consecutive \npairs of elements in the *Flowable* sequence\n- `reduce` - Apply an accumulator function over a Flowable sequence and \nemits a single element\n- `repeat_first` - Return a *Flowable* that repeats the first element it \nreceives from the source forever (until disposed).\n- `scan` - apply an accumulator function over a *Flowable* sequence and \nreturns each intermediate result.\n- `to_list` - Create a new *Flowable* that collects the elements from \nthe source sequence, and emits a single element of type List.\n- `zip_with_index` - zip each item emitted by the source with the \nenumerated index\n\n### Combining operators\n\n- `concat` - Concatentates *Flowable* sequences together by back-pressuring \nthe tail *Flowables* until the current *Flowable* has completed\n- `controlled_zip` - create a new *Flowable* from two *Flowables* by combining \ntheir elements in pairs. Which element gets paired with an element from \nthe other *Flowable* is determined by two functions called `request_left` and \n`request_right`\n- `match` - create a new *Flowable* from two *Flowables* by first filtering and \nduplicating (if necessary) the elements of each *Flowable* and zip the resulting \n*Flowable* sequences together\n- `merge` - merge the elements of the *Flowable* sequences into a single *Flowable*\n- `zip` - Create a new *Flowable* from two *Flowables* by combining their \nitem in pairs in a strict sequence\n\n### Other operators\n\n- `buffer` - buffer the element emitted by the source without back-pressure until \nthe buffer is full\n- `debug` - print debug messages to the console\n- `execute_on` - inject new scheduler that is used to subscribe the *Flowable*\n- `observe_on` - schedule elements emitted by the source on a dedicated scheduler\n- `set_base` - overwrite the base of the current Flowable sequence\n- `share` - multi-cast the elements of the *Flowable* to possibly \nmultiple subscribers\n\n### Create a rx Observable\n\n- `to_rx` - create a rx Observable from a Observable\n\nMultiCast (experimental)\n------------------------\n\n### Create a MultiCast\n\n- `empty` - create a *MultiCast* emitting no elements\n- `return_flowable` - turn zero or more *Flowables* into multi-cast *Flowables* \nemitted as a single element inside a  *MultiCast*\n- `return_` - create a *MultiCast* emitting a single element\n- `from_iterable` - create a *MultiCast* from an iterable\n- `from_rx_observable` - create a *MultiCast* from an *rx.Observable*\n- `from_flowable` - (similar to `from_rx_observable`) create a *MultiCast* \nthat emits each element received by the Flowable\n\n### Transforming operators\n\n- `default_if_empty` - either emits the elements of the source or a default element\n- `filter` - emit only those *MultiCast* for which the given predicate hold\n- `flat_map` - apply a function to each item emitted by the source and \nflattens the result\n- `lift` - lift the current `MultiCast[T1]` to a `MultiCast[T2[MultiCast[T1]]]`.\n- `map` - map each element emitted by the source by applying the given \nfunction\n- `merge` - merge the elements of the *MultiCast* sequences into a single *MultiCast*\n\n### Transforming operators (Flowables)\n\n- `join_flowables` - zip one or more *Multicasts* (each emitting a single *Flowable*)\n to a *Multicast* emitting a single element (tuple of *Flowables*)\n- `loop_flowables` - create a loop inside *Flowables*\n- `collect_flowables` - create a *Multicast* that emits a single element containing \nthe reduced *Flowables* of the first element sent by the source\n\n### Other operators \n\n- `debug` - print debug messages to the console\n- `observe_on` - schedule elements emitted by the source on a dedicated scheduler\n- `share` - multi-cast the elements of the source to possibly \nmultiple subscribers\n\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "An RxPY extension with back-pressure",
    "version": "3.0.0a11",
    "split_keywords": [
        "rx",
        "reactive",
        "extension",
        "back-pressure",
        "backpressure",
        "flowable",
        "multicast"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "md5": "752e9e6c3cd8c19a8472d830c8027f0c",
                "sha256": "f64b573c70c4e4ad9ed66fae95b905f6a57fff5733ec74a70456c54ada7bcff8"
            },
            "downloads": -1,
            "filename": "rxbp-3.0.0a11-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "752e9e6c3cd8c19a8472d830c8027f0c",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 236066,
            "upload_time": "2020-09-21T08:04:07",
            "upload_time_iso_8601": "2020-09-21T08:04:07.002778Z",
            "url": "https://files.pythonhosted.org/packages/91/af/e00760391ed41531e0e723ad4271392d6fc595c80008d42b4a9f9503d0e4/rxbp-3.0.0a11-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "md5": "37a93b54eef83621d14ada8353b7ce16",
                "sha256": "f8082fc00e28a46e5db87cb0338dff30ae72c3c904523501a8b1aa2c0ccba441"
            },
            "downloads": -1,
            "filename": "rxbp-3.0.0a11.tar.gz",
            "has_sig": false,
            "md5_digest": "37a93b54eef83621d14ada8353b7ce16",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 116971,
            "upload_time": "2020-09-21T08:04:08",
            "upload_time_iso_8601": "2020-09-21T08:04:08.370213Z",
            "url": "https://files.pythonhosted.org/packages/a7/bf/c91edbdf5550f99f57ef2c85cd8e92ec0039d076f74108acfa2d79257ee6/rxbp-3.0.0a11.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2020-09-21 08:04:08",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": null,
    "github_project": "MichaelSchneeberger",
    "error": "Could not fetch GitHub repository",
    "lcname": "rxbp"
}
        
Elapsed time: 0.14582s