# Aiofutures
![Python version](https://img.shields.io/badge/Python-3.7%2B-blue)
[![Tests](https://github.com/KazakovDenis/relatives/actions/workflows/cicd.yml/badge.svg)](https://github.com/KazakovDenis/aiofutures/actions/workflows/cicd.yml)
![PyPI - Downloads](https://img.shields.io/pypi/dm/aiofutures)
- [General information](#general-information)
- [Installation](#installation)
- [Usage](#usage)
- [Implicit initialization (global executor)](#implicit-initialization-global-executor)
- [Explicit initialization](#explicit-initialization)
- [UVLoop](#uvloop)
- [Notes](#notes)
- [Contribution](#contribution)
## General information
`aiofutures` provides tools to integrate an asynchronous code into your synchronous
application in a usual and easy way using standard library's `concurrent.futures.Executor` interface.
It may be useful when you want to:
- smoothly migrate your synchronous codebase to asynchronous style
- decrease a number of threads in your application
Replace this:
```python
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor() as ex:
future = ex.submit(sync_task)
result = future.result()
```
With this:
```python
from aiofutures import AsyncExecutor
with AsyncExecutor() as ex:
future = ex.submit(async_task)
result = future.result()
```
**The former spawns a lot of threads and experiences all cons of GIL, the latter
spawns the only one async thread (check out [notes](#Notes))**
## Installation
You can install `aiofutures` using pip:
```
pip install aiofutures
```
## Usage
### Implicit initialization (global executor)
Set an environment variable `AIOFUTURES_INIT` to any value and use shortcuts from the library:
```python
os.environ.setdefault('AIOFUTURES_INIT', '1')
from aiofutures import run_async
async def io_bound_task(seconds):
await asyncio.sleep(seconds)
return seconds
future = run_async(io_bound_task, 5)
print(future.result())
```
`AIOFUTURES_INIT` implicitly initializes a global `AsyncExecutor` and gives you an option to use
shortcuts `run_async` and `sync_to_async`.
### Explicit initialization
Use an instance of the `AsyncExecutor` directly:
```python
from aiofutures import AsyncExecutor
executor = AsyncExecutor()
future = executor.submit(io_bound_task, 5)
print(future.result())
```
In cases when you need to do IO synchronously within async tasks, you can use `sync_to_async`:
```python
from aiofutures import AsyncExecutor, sync_to_async
executor = AsyncExecutor()
async def io_bound_task():
# or with the shortcut
# url = await sync_to_async(fetch_url_from_db_sync)
url = await executor.sync_to_async(fetch_url_from_db_sync)
data = await fetch_data(url)
return data
future = executor.submit(io_bound_task)
print(future.result())
```
NOTE: You can use sync_to_async within tasks running in the executor only.
### UVLoop
To use with the high performance `uvloop` install it before initialization:
```python
# install before the import for the global executor
import uvloop
uvloop.install()
from aiofutures import run_async
...
# or before an explicit initialization
import uvloop
from aiofutures import AsyncExecutor
uvloop.install()
executor = AsyncExecutor()
```
### Notes
- Take into account that asyncio still ([CPython3.11](https://github.com/python/cpython/blob/4664a7cf689946f0c9854cadee7c6aa9c276a8cf/Lib/asyncio/base_events.py#L867))
resolves DNS in threads, not asynchronously
- Any blocking function will block the whole AsyncExecutor
## Contribution
All suggestions are welcome!
Raw data
{
"_id": null,
"home_page": "https://pypi.org/project/aiofutures",
"name": "aiofutures",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.7,<4.0",
"maintainer_email": "",
"keywords": "asyncio,executor,concurrent,futures",
"author": "Denis Kazakov",
"author_email": "denis@kazakov.ru.net",
"download_url": "https://files.pythonhosted.org/packages/b7/d6/3c41fe18cad6f23f1fd116ff008284500b097808afda228fed09023a4f8f/aiofutures-0.1.2.tar.gz",
"platform": null,
"description": "# Aiofutures\n![Python version](https://img.shields.io/badge/Python-3.7%2B-blue)\n[![Tests](https://github.com/KazakovDenis/relatives/actions/workflows/cicd.yml/badge.svg)](https://github.com/KazakovDenis/aiofutures/actions/workflows/cicd.yml) \n![PyPI - Downloads](https://img.shields.io/pypi/dm/aiofutures)\n\n- [General information](#general-information)\n- [Installation](#installation)\n- [Usage](#usage)\n - [Implicit initialization (global executor)](#implicit-initialization-global-executor)\n - [Explicit initialization](#explicit-initialization)\n - [UVLoop](#uvloop)\n - [Notes](#notes)\n- [Contribution](#contribution)\n\n## General information\n\n`aiofutures` provides tools to integrate an asynchronous code into your synchronous \napplication in a usual and easy way using standard library's `concurrent.futures.Executor` interface. \n \nIt may be useful when you want to:\n- smoothly migrate your synchronous codebase to asynchronous style\n- decrease a number of threads in your application \n\nReplace this:\n```python\nfrom concurrent.futures import ThreadPoolExecutor\n\nwith ThreadPoolExecutor() as ex:\n future = ex.submit(sync_task)\n result = future.result()\n```\n\nWith this:\n```python\nfrom aiofutures import AsyncExecutor\n\nwith AsyncExecutor() as ex:\n future = ex.submit(async_task)\n result = future.result()\n```\n\n**The former spawns a lot of threads and experiences all cons of GIL, the latter\nspawns the only one async thread (check out [notes](#Notes))** \n\n## Installation\n\nYou can install `aiofutures` using pip:\n\n```\npip install aiofutures\n```\n\n## Usage\n\n### Implicit initialization (global executor)\n\nSet an environment variable `AIOFUTURES_INIT` to any value and use shortcuts from the library:\n\n```python\nos.environ.setdefault('AIOFUTURES_INIT', '1')\n\nfrom aiofutures import run_async\n\nasync def io_bound_task(seconds):\n await asyncio.sleep(seconds)\n return seconds\n\nfuture = run_async(io_bound_task, 5)\nprint(future.result())\n```\n`AIOFUTURES_INIT` implicitly initializes a global `AsyncExecutor` and gives you an option to use \nshortcuts `run_async` and `sync_to_async`.\n\n### Explicit initialization\n\nUse an instance of the `AsyncExecutor` directly:\n\n```python\nfrom aiofutures import AsyncExecutor\n\nexecutor = AsyncExecutor()\nfuture = executor.submit(io_bound_task, 5)\nprint(future.result())\n```\n\nIn cases when you need to do IO synchronously within async tasks, you can use `sync_to_async`:\n\n```python\nfrom aiofutures import AsyncExecutor, sync_to_async\n\nexecutor = AsyncExecutor()\n\nasync def io_bound_task():\n # or with the shortcut\n # url = await sync_to_async(fetch_url_from_db_sync)\n url = await executor.sync_to_async(fetch_url_from_db_sync)\n data = await fetch_data(url)\n return data\n\nfuture = executor.submit(io_bound_task)\nprint(future.result())\n```\n\nNOTE: You can use sync_to_async within tasks running in the executor only.\n\n### UVLoop\n\nTo use with the high performance `uvloop` install it before initialization:\n```python\n# install before the import for the global executor\nimport uvloop\nuvloop.install()\nfrom aiofutures import run_async\n...\n\n# or before an explicit initialization\nimport uvloop\nfrom aiofutures import AsyncExecutor\nuvloop.install()\nexecutor = AsyncExecutor()\n```\n\n### Notes\n- Take into account that asyncio still ([CPython3.11](https://github.com/python/cpython/blob/4664a7cf689946f0c9854cadee7c6aa9c276a8cf/Lib/asyncio/base_events.py#L867))\nresolves DNS in threads, not asynchronously\n- Any blocking function will block the whole AsyncExecutor\n\n## Contribution\nAll suggestions are welcome!\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Smoothly migrate your synchronous codebase to the asynchronous style.",
"version": "0.1.2",
"split_keywords": [
"asyncio",
"executor",
"concurrent",
"futures"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "9ba8ed57ee927427670031816bbb3d7b5c24e05f2225619410202a4b7ede661f",
"md5": "cf65bf83776c4a16c9206030395f5b00",
"sha256": "f8d35660400033cdf1440680486d01de04c3d589d565d23d9ef595bac98448da"
},
"downloads": -1,
"filename": "aiofutures-0.1.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "cf65bf83776c4a16c9206030395f5b00",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7,<4.0",
"size": 5824,
"upload_time": "2023-03-31T21:34:49",
"upload_time_iso_8601": "2023-03-31T21:34:49.370795Z",
"url": "https://files.pythonhosted.org/packages/9b/a8/ed57ee927427670031816bbb3d7b5c24e05f2225619410202a4b7ede661f/aiofutures-0.1.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "b7d63c41fe18cad6f23f1fd116ff008284500b097808afda228fed09023a4f8f",
"md5": "1221b3c9ca7bddfc879d6600424e37be",
"sha256": "c7a6975043e0b78c6431bf3c7791099bb307de23ccfac3049a409d21b6e0b011"
},
"downloads": -1,
"filename": "aiofutures-0.1.2.tar.gz",
"has_sig": false,
"md5_digest": "1221b3c9ca7bddfc879d6600424e37be",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7,<4.0",
"size": 5106,
"upload_time": "2023-03-31T21:34:51",
"upload_time_iso_8601": "2023-03-31T21:34:51.431470Z",
"url": "https://files.pythonhosted.org/packages/b7/d6/3c41fe18cad6f23f1fd116ff008284500b097808afda228fed09023a4f8f/aiofutures-0.1.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-03-31 21:34:51",
"github": false,
"gitlab": false,
"bitbucket": false,
"lcname": "aiofutures"
}