asyncio-gevent


Nameasyncio-gevent JSON
Version 0.2.3 PyPI version JSON
download
home_pagehttps://github.com/gfmio/asyncio-gevent
Summaryasyncio & gevent in harmony
upload_time2022-12-05 12:31:41
maintainer
docs_urlNone
authorFrédérique Mittelstaedt
requires_python>=3.7,<4.0
licenseMIT
keywords asyncio gevent
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # asyncio-gevent

asyncio-gevent makes asyncio and gevent compatible. It provides utilities for

- running asyncio on gevent (by using gevent as asyncio's event loop)
- running gevent on asyncio (by using asyncio as gevent's event loop, still work in progress)
- converting greenlets to asyncio futures
- converting futures to asyncio greenlets
- wrapping blocking or spawning functions in coroutines which spawn a greenlet and wait for its completion
- wrapping coroutines in spawning functions which block until the future is resolved

asyncio-gevent is a fork and rewrite of `aiogevent` and `tulipcore` for modern python 3.

## Install

Install `asyncio-gevent` from pypi using your favourite package manager.

```sh
# If you use poetry
poetry add asyncio-gevent

# If you use pip
pip install asyncio-gevent
```

## Usage

### Running asyncio on gevent

In order to run `asyncio` on `gevent`, we need to set the (default) `EventLoopPolicy` to use `asyncio_gevent.EventLoopPolicy`.

```py3
import gevent.monkey
gevent.monkey.patch_all()

import asyncio

import asyncio_gevent

asyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())

async def main():
    await asyncio.sleep(1)
    print("done")

asyncio.run(main())
```

After setting the event loop policy, asyncio will use an event loop that uses greenlets for scheduling.

Under the hood, it uses the default selector-based event loop in asyncio with the the gevent selector implementation.

Alternatively, you can also manually set and use the event loop.

```py3
import gevent.monkey
gevent.monkey.patch_all()

import asyncio

import asyncio_gevent

loop = asyncio_gevent.EventLoop()
asyncio.set_event_loop(loop)

async def main():
    await asyncio.sleep(1)
    print("done")

loop.run_until_complete(main())
```

### Running gevent on asyncio

> This implementation is still work-in-progress. It may work for simple examples, but otherwise fail in unexpected ways.

In order to run `gevent` on `asyncio`, `gevent` needs to be initialised to use the asyncio event loop. This is done by setting the environment variable `GEVENT_LOOP` to `asyncio_gevent.gevent_loop.GeventLoop` and then starting python.

```sh
GEVENT_LOOP=asyncio_gevent.gevent_loop.GeventLoop python3 myscript.py
```

gevent will now run on asyncio.

Alternatively, you can also set the loop configuration setting, preferably right after importing `gevent` and before monkey patching.

```py3
import gevent
gevent.config.loop = "asyncio_gevent.gevent_loop.GeventLoop"
```

### Converting greenlets to asyncio futures

Use `asyncio_gevent.greenlet_to_future` to convert a greenlet to an asyncio future. The future yields once the greenlet has finished execution.

```py3
# Preamble: Apply the gevent monkey patch and initialise the asyncio event loop policy

import gevent.monkey
gevent.monkey.patch_all()

import asyncio

import asyncio_gevent

asyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())

# Main example

import gevent

def blocking_function() -> int:
    gevent.sleep(10)
    return 42


async def main() -> None:
    greenlet = gevent.spawn(blocking_function)
    future = asyncio_gevent.greenlet_to_future(greenlet)
    result = await future

asyncio.run(main())
```

If the greenlet is already dead when the future is awaited/scheduled, then the future will resolve with the result or raise the exception thrown immediately.

If the greenlet is not yet running, the greenlet will by default be started when the future is awaited/scheduled. This is to ensure a sensible default behaviour and prevent odd concurrency issues. To prevent this auto-starting, you can pass `autostart_greenlet=False` as an argument to `greenlet_to_future`.

When a greenlet is killed without a custom exception type, it will return a `GreenletExit` exception. In this instance, the future get cancelled. If a custom exception type is used, the future will raise the exception.

If the future gets cancelled, then by default the greenlet is killed. To prevent the greenlet from getting killed, you can pass `autokill_greenlet=False` as an argument to `greenlet_to_future`.

### Converting asyncio futures to greenlets

Use `asyncio_gevent.future_to_greenlet` to convert a future to a greenlet.

```py3
# Preamble: Apply the gevent monkey patch and initialise the asyncio event loop policy

import gevent.monkey
gevent.monkey.patch_all()

import asyncio

import asyncio_gevent

asyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())

# Main example

import gevent

async def async_function() -> int:
    await asyncio.sleep(1)
    return 42


def main() -> None:
    future = async_function()
    greenlet = asyncio_gevent.future_to_greenlet(future)
    greenlet.start()
    greenlet.join()
    assert greenlet.get() == 42

main()
```

The greenlet returned by this function will not start automatically, so you need to call `Greenlet.start()` manually.

If `future` is a coroutine object, it will be scheduled as a task on the `loop` when the greenlet starts. If no `loop` argument has been passed, the running event loop will be used. If there is no running event loop, a new event loop will be started using the current event loop policy.

If the future is not already scheduled, then it won't be scheduled for execution until the greenlet starts running. To prevent the future from being scheduled automatically, you can pass `autostart_future=False` as an argument to `future_to_greenlet`.

If the greenlet gets killed, the by default the future gets cancelled. To prevent this from happening and having the future return the `GreenletExit` objct instead, you can pass `autocancel_future=False` as an argument to `future_to_greenlet`.

If the future gets cancelled, the greenlet gets killed and will return a `GreenletExit`. This default behaviour can be circumvented by passing `autokill_greenlet=False` and the greenlet will raise the `CancelledError` instead.

### Wrapping blocking or spawning functions in coroutines

Use `asyncio_gevent.sync_to_async` to wrap a blocking function or a function that may spawn greenlets and wait for their completion in a coroutine.

```py3
# Preamble: Apply the gevent monkey patch and initialise the asyncio event loop policy

import gevent.monkey
gevent.monkey.patch_all()

import asyncio

import asyncio_gevent

asyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())

# Main example

import gevent

def blocking_function(duration: float):
    gevent.sleep(duration)
    return 42

async_function = asyncio_gevent.sync_to_async(blocking_function)

asyncio.run(async_function(1.0))
```

The returned corountine function will execute the original function in a spawned greenlet and await it's completion.

Under the hood, this is just a thin convenience wrapper around `asyncio_gevent.greenlet_to_future`.

As a result, `asyncio_gevent.sync_to_async` accepts the same arguments as `asyncio_gevent.greenlet_to_future` to achieve the same behaviour.

`asyncio_gevent.sync_to_async` can also be used as a decorator

```py3
# Preamble: Apply the gevent monkey patch and initialise the asyncio event loop policy

import gevent.monkey
gevent.monkey.patch_all()

import asyncio

import asyncio_gevent

asyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())

# Main example

import gevent

@asyncio_gevent.sync_to_async()
def fn(duration: float):
    gevent.sleep(duration)
    return 42

asyncio.run(fn(1.0))
```


### Wrapping coroutines in spawning functions

Use `asyncio_gevent.async_to_sync` to wrap a coroutine function or in a blocking function that spawns a greenlet and waits until the coroutine has returned.

```py3
# Preamble: Apply the gevent monkey patch and initialise the asyncio event loop policy

import gevent.monkey
gevent.monkey.patch_all()

import asyncio

import asyncio_gevent

asyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())

# Main example

import gevent

async def async_function(duration: float):
    await asyncio.sleep(duration)
    return 42

blocking_function = asyncio_gevent.async_to_sync(async_function)

blocking_function(1)
```

The returned function will execute the coroutine on an existing running loop or a new event loop and await it's completion.

Under the hood, this is just a thin convenience wrapper around `asyncio_gevent.future_to_greenlet`.

As a result, `asyncio_gevent.async_to_sync` accepts the same arguments as `asyncio_gevent.future_to_greenlet` to achieve the same behaviour.

`asyncio_gevent.async_to_sync` can also be used as a decorator.


```py3
# Preamble: Apply the gevent monkey patch and initialise the asyncio event loop policy

import gevent.monkey
gevent.monkey.patch_all()

import asyncio

import asyncio_gevent

asyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())

# Main example

import gevent

@asyncio_gevent.async_to_sync
async def fn(duration: float):
    await asyncio.sleep(duration)
    return 42

fn(1)
```

## Known limitations

### gevent.sleep

`gevent.sleep` will pause the current greenlet. As a result, this will, by default, result in the greenlet powering the asyncio loop to be put to sleep, so nothing will execute.

This is typically not intended.

The solution to this problem is to wrap any code that calls `gevent.sleep` in `asyncio.sync_to_async` or (equivalently) `asyncio_gevent.greenlet_to_future(gevent.spawn(f))`. This causes the function to be executed in another greenlet which prevents the main greenlet from getting blocked.

Example:

```py
import gevent.monkey

gevent.monkey.patch_all()

import asyncio
import threading

import gevent

import asyncio_gevent

asyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())


async def f():
    print("f", 1)
    await asyncio.sleep(1)
    print("f", 2)


def g():
    print("g", 1)
    gevent.sleep(2)
    print("g", 2)


async def main():
    await asyncio.gather(f(), asyncio_gevent.sync_to_async(g)())
    # OR equivalently
    # await asyncio.gather(f(), asyncio_gevent.greenlet_to_future(gevent.spawn(g)))


if __name__ == "__main__":
    asyncio.run(main())
```

The output will be (as expected):

```txt
g 1
f 1
f 2
g 2
```

If `gevent.sleep` is called inside an async function, then the async function needs to first be wrapped in `asyncio.async_to_sync`.

```py
import gevent.monkey

gevent.monkey.patch_all()

import asyncio

import gevent

import asyncio_gevent

asyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())


async def f():
    print("f", 1)
    await asyncio.sleep(1)
    print("f", 2)


async def g():
    print("g", 1)
    await asyncio.sleep(1)
    gevent.sleep(1)
    print("g", 2)


async def main():
    await asyncio.gather(
        f(), asyncio_gevent.sync_to_async(asyncio_gevent.async_to_sync(g))()
    )


if __name__ == "__main__":
    asyncio.run(main())
```

The output will again be (as expected):

```txt
g 1
f 1
f 2
g 2
```


## License

[MIT](LICENSE)

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/gfmio/asyncio-gevent",
    "name": "asyncio-gevent",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7,<4.0",
    "maintainer_email": "",
    "keywords": "asyncio,gevent",
    "author": "Fr\u00e9d\u00e9rique Mittelstaedt",
    "author_email": "hi@gfm.io",
    "download_url": "https://files.pythonhosted.org/packages/66/74/c2bc62952016e6d08a4df3461cceaaf68b0efd81d514cd087f14f426e017/asyncio_gevent-0.2.3.tar.gz",
    "platform": null,
    "description": "# asyncio-gevent\n\nasyncio-gevent makes asyncio and gevent compatible. It provides utilities for\n\n- running asyncio on gevent (by using gevent as asyncio's event loop)\n- running gevent on asyncio (by using asyncio as gevent's event loop, still work in progress)\n- converting greenlets to asyncio futures\n- converting futures to asyncio greenlets\n- wrapping blocking or spawning functions in coroutines which spawn a greenlet and wait for its completion\n- wrapping coroutines in spawning functions which block until the future is resolved\n\nasyncio-gevent is a fork and rewrite of `aiogevent` and `tulipcore` for modern python 3.\n\n## Install\n\nInstall `asyncio-gevent` from pypi using your favourite package manager.\n\n```sh\n# If you use poetry\npoetry add asyncio-gevent\n\n# If you use pip\npip install asyncio-gevent\n```\n\n## Usage\n\n### Running asyncio on gevent\n\nIn order to run `asyncio` on `gevent`, we need to set the (default) `EventLoopPolicy` to use `asyncio_gevent.EventLoopPolicy`.\n\n```py3\nimport gevent.monkey\ngevent.monkey.patch_all()\n\nimport asyncio\n\nimport asyncio_gevent\n\nasyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())\n\nasync def main():\n    await asyncio.sleep(1)\n    print(\"done\")\n\nasyncio.run(main())\n```\n\nAfter setting the event loop policy, asyncio will use an event loop that uses greenlets for scheduling.\n\nUnder the hood, it uses the default selector-based event loop in asyncio with the the gevent selector implementation.\n\nAlternatively, you can also manually set and use the event loop.\n\n```py3\nimport gevent.monkey\ngevent.monkey.patch_all()\n\nimport asyncio\n\nimport asyncio_gevent\n\nloop = asyncio_gevent.EventLoop()\nasyncio.set_event_loop(loop)\n\nasync def main():\n    await asyncio.sleep(1)\n    print(\"done\")\n\nloop.run_until_complete(main())\n```\n\n### Running gevent on asyncio\n\n> This implementation is still work-in-progress. It may work for simple examples, but otherwise fail in unexpected ways.\n\nIn order to run `gevent` on `asyncio`, `gevent` needs to be initialised to use the asyncio event loop. This is done by setting the environment variable `GEVENT_LOOP` to `asyncio_gevent.gevent_loop.GeventLoop` and then starting python.\n\n```sh\nGEVENT_LOOP=asyncio_gevent.gevent_loop.GeventLoop python3 myscript.py\n```\n\ngevent will now run on asyncio.\n\nAlternatively, you can also set the loop configuration setting, preferably right after importing `gevent` and before monkey patching.\n\n```py3\nimport gevent\ngevent.config.loop = \"asyncio_gevent.gevent_loop.GeventLoop\"\n```\n\n### Converting greenlets to asyncio futures\n\nUse `asyncio_gevent.greenlet_to_future` to convert a greenlet to an asyncio future. The future yields once the greenlet has finished execution.\n\n```py3\n# Preamble: Apply the gevent monkey patch and initialise the asyncio event loop policy\n\nimport gevent.monkey\ngevent.monkey.patch_all()\n\nimport asyncio\n\nimport asyncio_gevent\n\nasyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())\n\n# Main example\n\nimport gevent\n\ndef blocking_function() -> int:\n    gevent.sleep(10)\n    return 42\n\n\nasync def main() -> None:\n    greenlet = gevent.spawn(blocking_function)\n    future = asyncio_gevent.greenlet_to_future(greenlet)\n    result = await future\n\nasyncio.run(main())\n```\n\nIf the greenlet is already dead when the future is awaited/scheduled, then the future will resolve with the result or raise the exception thrown immediately.\n\nIf the greenlet is not yet running, the greenlet will by default be started when the future is awaited/scheduled. This is to ensure a sensible default behaviour and prevent odd concurrency issues. To prevent this auto-starting, you can pass `autostart_greenlet=False` as an argument to `greenlet_to_future`.\n\nWhen a greenlet is killed without a custom exception type, it will return a `GreenletExit` exception. In this instance, the future get cancelled. If a custom exception type is used, the future will raise the exception.\n\nIf the future gets cancelled, then by default the greenlet is killed. To prevent the greenlet from getting killed, you can pass `autokill_greenlet=False` as an argument to `greenlet_to_future`.\n\n### Converting asyncio futures to greenlets\n\nUse `asyncio_gevent.future_to_greenlet` to convert a future to a greenlet.\n\n```py3\n# Preamble: Apply the gevent monkey patch and initialise the asyncio event loop policy\n\nimport gevent.monkey\ngevent.monkey.patch_all()\n\nimport asyncio\n\nimport asyncio_gevent\n\nasyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())\n\n# Main example\n\nimport gevent\n\nasync def async_function() -> int:\n    await asyncio.sleep(1)\n    return 42\n\n\ndef main() -> None:\n    future = async_function()\n    greenlet = asyncio_gevent.future_to_greenlet(future)\n    greenlet.start()\n    greenlet.join()\n    assert greenlet.get() == 42\n\nmain()\n```\n\nThe greenlet returned by this function will not start automatically, so you need to call `Greenlet.start()` manually.\n\nIf `future` is a coroutine object, it will be scheduled as a task on the `loop` when the greenlet starts. If no `loop` argument has been passed, the running event loop will be used. If there is no running event loop, a new event loop will be started using the current event loop policy.\n\nIf the future is not already scheduled, then it won't be scheduled for execution until the greenlet starts running. To prevent the future from being scheduled automatically, you can pass `autostart_future=False` as an argument to `future_to_greenlet`.\n\nIf the greenlet gets killed, the by default the future gets cancelled. To prevent this from happening and having the future return the `GreenletExit` objct instead, you can pass `autocancel_future=False` as an argument to `future_to_greenlet`.\n\nIf the future gets cancelled, the greenlet gets killed and will return a `GreenletExit`. This default behaviour can be circumvented by passing `autokill_greenlet=False` and the greenlet will raise the `CancelledError` instead.\n\n### Wrapping blocking or spawning functions in coroutines\n\nUse `asyncio_gevent.sync_to_async` to wrap a blocking function or a function that may spawn greenlets and wait for their completion in a coroutine.\n\n```py3\n# Preamble: Apply the gevent monkey patch and initialise the asyncio event loop policy\n\nimport gevent.monkey\ngevent.monkey.patch_all()\n\nimport asyncio\n\nimport asyncio_gevent\n\nasyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())\n\n# Main example\n\nimport gevent\n\ndef blocking_function(duration: float):\n    gevent.sleep(duration)\n    return 42\n\nasync_function = asyncio_gevent.sync_to_async(blocking_function)\n\nasyncio.run(async_function(1.0))\n```\n\nThe returned corountine function will execute the original function in a spawned greenlet and await it's completion.\n\nUnder the hood, this is just a thin convenience wrapper around `asyncio_gevent.greenlet_to_future`.\n\nAs a result, `asyncio_gevent.sync_to_async` accepts the same arguments as `asyncio_gevent.greenlet_to_future` to achieve the same behaviour.\n\n`asyncio_gevent.sync_to_async` can also be used as a decorator\n\n```py3\n# Preamble: Apply the gevent monkey patch and initialise the asyncio event loop policy\n\nimport gevent.monkey\ngevent.monkey.patch_all()\n\nimport asyncio\n\nimport asyncio_gevent\n\nasyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())\n\n# Main example\n\nimport gevent\n\n@asyncio_gevent.sync_to_async()\ndef fn(duration: float):\n    gevent.sleep(duration)\n    return 42\n\nasyncio.run(fn(1.0))\n```\n\n\n### Wrapping coroutines in spawning functions\n\nUse `asyncio_gevent.async_to_sync` to wrap a coroutine function or in a blocking function that spawns a greenlet and waits until the coroutine has returned.\n\n```py3\n# Preamble: Apply the gevent monkey patch and initialise the asyncio event loop policy\n\nimport gevent.monkey\ngevent.monkey.patch_all()\n\nimport asyncio\n\nimport asyncio_gevent\n\nasyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())\n\n# Main example\n\nimport gevent\n\nasync def async_function(duration: float):\n    await asyncio.sleep(duration)\n    return 42\n\nblocking_function = asyncio_gevent.async_to_sync(async_function)\n\nblocking_function(1)\n```\n\nThe returned function will execute the coroutine on an existing running loop or a new event loop and await it's completion.\n\nUnder the hood, this is just a thin convenience wrapper around `asyncio_gevent.future_to_greenlet`.\n\nAs a result, `asyncio_gevent.async_to_sync` accepts the same arguments as `asyncio_gevent.future_to_greenlet` to achieve the same behaviour.\n\n`asyncio_gevent.async_to_sync` can also be used as a decorator.\n\n\n```py3\n# Preamble: Apply the gevent monkey patch and initialise the asyncio event loop policy\n\nimport gevent.monkey\ngevent.monkey.patch_all()\n\nimport asyncio\n\nimport asyncio_gevent\n\nasyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())\n\n# Main example\n\nimport gevent\n\n@asyncio_gevent.async_to_sync\nasync def fn(duration: float):\n    await asyncio.sleep(duration)\n    return 42\n\nfn(1)\n```\n\n## Known limitations\n\n### gevent.sleep\n\n`gevent.sleep` will pause the current greenlet. As a result, this will, by default, result in the greenlet powering the asyncio loop to be put to sleep, so nothing will execute.\n\nThis is typically not intended.\n\nThe solution to this problem is to wrap any code that calls `gevent.sleep` in `asyncio.sync_to_async` or (equivalently) `asyncio_gevent.greenlet_to_future(gevent.spawn(f))`. This causes the function to be executed in another greenlet which prevents the main greenlet from getting blocked.\n\nExample:\n\n```py\nimport gevent.monkey\n\ngevent.monkey.patch_all()\n\nimport asyncio\nimport threading\n\nimport gevent\n\nimport asyncio_gevent\n\nasyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())\n\n\nasync def f():\n    print(\"f\", 1)\n    await asyncio.sleep(1)\n    print(\"f\", 2)\n\n\ndef g():\n    print(\"g\", 1)\n    gevent.sleep(2)\n    print(\"g\", 2)\n\n\nasync def main():\n    await asyncio.gather(f(), asyncio_gevent.sync_to_async(g)())\n    # OR equivalently\n    # await asyncio.gather(f(), asyncio_gevent.greenlet_to_future(gevent.spawn(g)))\n\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\nThe output will be (as expected):\n\n```txt\ng 1\nf 1\nf 2\ng 2\n```\n\nIf `gevent.sleep` is called inside an async function, then the async function needs to first be wrapped in `asyncio.async_to_sync`.\n\n```py\nimport gevent.monkey\n\ngevent.monkey.patch_all()\n\nimport asyncio\n\nimport gevent\n\nimport asyncio_gevent\n\nasyncio.set_event_loop_policy(asyncio_gevent.EventLoopPolicy())\n\n\nasync def f():\n    print(\"f\", 1)\n    await asyncio.sleep(1)\n    print(\"f\", 2)\n\n\nasync def g():\n    print(\"g\", 1)\n    await asyncio.sleep(1)\n    gevent.sleep(1)\n    print(\"g\", 2)\n\n\nasync def main():\n    await asyncio.gather(\n        f(), asyncio_gevent.sync_to_async(asyncio_gevent.async_to_sync(g))()\n    )\n\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\nThe output will again be (as expected):\n\n```txt\ng 1\nf 1\nf 2\ng 2\n```\n\n\n## License\n\n[MIT](LICENSE)\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "asyncio & gevent in harmony",
    "version": "0.2.3",
    "split_keywords": [
        "asyncio",
        "gevent"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "md5": "a24bbf5cafa62fed67b5e6df43e7c66c",
                "sha256": "d0f581b61fd648f784c9bacdcfa7d6b585aa6efc5cb8c399301857cc10150f7e"
            },
            "downloads": -1,
            "filename": "asyncio_gevent-0.2.3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a24bbf5cafa62fed67b5e6df43e7c66c",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7,<4.0",
            "size": 21965,
            "upload_time": "2022-12-05T12:31:39",
            "upload_time_iso_8601": "2022-12-05T12:31:39.228533Z",
            "url": "https://files.pythonhosted.org/packages/39/77/918b57e9c8c12fc03bf460f8e22b8261c10bc51aea3038b4a543268ed2af/asyncio_gevent-0.2.3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "md5": "5659bef7798032290af89dfb897ca6a3",
                "sha256": "35135b323a4d4804cbaa2888fd3bcbcc23cddce358a1b4234641efb6c58b6248"
            },
            "downloads": -1,
            "filename": "asyncio_gevent-0.2.3.tar.gz",
            "has_sig": false,
            "md5_digest": "5659bef7798032290af89dfb897ca6a3",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7,<4.0",
            "size": 18619,
            "upload_time": "2022-12-05T12:31:41",
            "upload_time_iso_8601": "2022-12-05T12:31:41.161411Z",
            "url": "https://files.pythonhosted.org/packages/66/74/c2bc62952016e6d08a4df3461cceaaf68b0efd81d514cd087f14f426e017/asyncio_gevent-0.2.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2022-12-05 12:31:41",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": "gfmio",
    "github_project": "asyncio-gevent",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "asyncio-gevent"
}
        
Elapsed time: 0.01380s