{ "info": { "author": "Konstantin Enchant", "author_email": "sirkonst@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3 :: Only" ], "description": "[![Python versions](https://img.shields.io/badge/python-3.5%2C%203.6%2C%203.7-green.svg)]()\n[![PyPI version](https://badge.fury.io/py/async-reduce.svg)](https://pypi.org/project/async-reduce/)\n[![coverage report](https://gitlab.com/sirkonst/async-reduce/badges/master/coverage.svg)]()\n\n\nAbout Async-Reduce\n==================\n\n``async_reduce(coroutine)`` allows aggregate all *similar simultaneous* ready \nto run `coroutine`s and reduce to running **only one** `coroutine`.\nOther aggregated `coroutine`s will get result from single `coroutine`.\n\nIt can boost application performance in highly competitive execution of the\nsimilar asynchronous operations and reduce load for inner systems.\n\n\nQuick example\n-------------\n\n```python\nfrom async_reduce import async_reduce\n\n\nasync def fetch_user_data(user_id: int) -> dict:\n \"\"\"\" Get user data from inner service \"\"\"\n url = 'http://inner-service/user/{}'.format(user_id)\n\n return await http.get(url, timeout=10).json()\n\n\n@web_server.router('/users/(\\d+)')\nasync def handler_user_detail(request, user_id: int):\n \"\"\" Handler for get detail information about user \"\"\"\n\n # all simultaneous requests of fetching user data for `user_id` will \n # reduced to single request\n user_data = await async_reduce(\n fetch_user_data(user_id)\n )\n\n # sometimes ``async_reduce`` cannot detect similar coroutines and\n # you should provide special argument `ident` for manually determination\n user_statistics = await async_reduce(\n DataBase.query('user_statistics').where(id=user_id).fetch_one(),\n ident='db_user_statistics:{}'.format(user_id)\n )\n\n return Response(...)\n```\n\nIn that example without using ``async_reduce`` if client performs **N** \nsimultaneous requests like `GET http://web_server/users/42` *web_server*\nperforms **N** requests to *inner-service* and **N** queries to *database*.\nIn total: **N** simultaneous requests emits **2 * N** requests to inner systems. \n\nWith ``async_reduce`` if client performs **N** simultaneous requests *web_server*\nperforms **one** request to *inner-service* and **one** query to *database*.\nIn total: **N** simultaneous requests emit only **2** requests to inner systems.\n\nSee other real [examples](https://github.com/sirkonst/async-reduce/tree/master/examples).\n\n\nSimilar coroutines determination \n--------------------------------\n\n``async_reduce(coroutine)`` tries to detect similar coroutines by hashing local \nvariables bounded on call. It does not work correctly if:\n\n* one of the arguments is not hashable\n* coroutine function is a method of class with specific state (like ORM)\n* coroutine function has closure to unhashable variable\n\nYou can disable auto-determination by setting custom key to argument ``ident``.\n\n\nUse as decorator\n----------------\n\nAlso library provide special decorator ``@async_reduceable()``, example:\n\n```python\nfrom async_reduce import async_reduceable\n\n\n@async_reduceable()\nasync def fetch_user_data(user_id: int) -> dict:\n \"\"\"\" Get user data from inner service \"\"\"\n url = 'http://inner-servicce/user/{}'.format(user_id)\n\n return await http.get(url, timeout=10).json()\n\n\n@web_server.router('/users/(\\d+)')\nasync def handler_user_detail(request, user_id: int):\n \"\"\" Handler for get detail information about user \"\"\"\n return await fetch_user_data(user_id)\n```\n\n\nHooks\n-----\n\nLibrary supports hooks. Add-on hooks:\n\n* **DebugHooks** - print about all triggered hooks\n* **StatisticsOverallHooks** - general statistics on the use of `async_reduce`\n* **StatisticsDetailHooks** - like `StatisticsOverallHooks` but detail statistics\nabout all `coroutine` processed by `async_reduce`\n\nExample:\n\n```python\nfrom async_reduce import AsyncReducer\nfrom async_reduce.hooks import DebugHooks\n\n# define custom async_reduce with hooks\nasync_reduce = AsyncReducer(hooks=DebugHooks())\n\n\nasync def handler_user_detail(request, user_id: int):\n user_data = await async_reduce(fetch_user_data(user_id))\n```\n\nSee more detail example in [examples/example_hooks.py](https://github.com/sirkonst/async-reduce/blob/master/examples/example_hooks.py).\n\nYou can write custom hooks via inherit from [BaseHooks](https://github.com/sirkonst/async-reduce/blob/master/async_reduce/hooks/base.py).\n\n\nCaveats\n-------\n\n* If single `coroutine` raises exceptions all aggregated `coroutine`s will get\nsame exception too\n\n* If single `coroutine` is stuck all aggregated `coroutine`s will stuck too. \nLimit execution time for `coroutine` and add retries (optional) to avoid it.\n\n* Be careful when return mutable value from `coroutine` because single value \nwill shared. Prefer to use non-mutable value as coroutine return.\n\n\nDevelopment\n-----------\n\nSee [DEVELOPMENT.md](https://github.com/sirkonst/async-reduce/blob/master/DEVELOPMENT.md).\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/sirkonst/async-reduce", "keywords": "asyncio,reduce", "license": "MIT", "maintainer": "Konstantin Enchant", "maintainer_email": "sirkonst@gmail.com", "name": "async-reduce", "package_url": "https://pypi.org/project/async-reduce/", "platform": "", "project_url": "https://pypi.org/project/async-reduce/", "project_urls": { "Homepage": "https://github.com/sirkonst/async-reduce" }, "release_url": "https://pypi.org/project/async-reduce/0.2.1/", "requires_dist": [ "typing ; python_version < \"3.6.1\"", "tox ; extra == 'develop'", "wheel ; extra == 'develop'", "twine ; extra == 'develop'", "pytest ; extra == 'testing'", "pytest-asyncio ; extra == 'testing'", "coverage ; extra == 'testing'", "asynctest ; extra == 'testing'", "pylama ; extra == 'testing'", "mypy ; extra == 'testing'" ], "requires_python": ">=3.5", "summary": "Reducer for similar simultaneously coroutines", "version": "0.2.1" }, "last_serial": 5338719, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "cb589ce06144cad23ffbf901fa242fb3", "sha256": "bcdac61981dfb9f22beb8c2f77c607fa4f85f38356faef6c359ffed80b27508d" }, "downloads": -1, "filename": "async_reduce-0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "cb589ce06144cad23ffbf901fa242fb3", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 5546, "upload_time": "2019-04-04T17:19:55", "url": "https://files.pythonhosted.org/packages/42/66/0d39cffb4a44d61b948834b3f96867ff7e92ee680884b8f5cc347162b405/async_reduce-0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "84096b7cb0f81acd36d13b5e6562dd94", "sha256": "190553fe74249d12762ea52dd672a6ea044d7ced523cc0f270d4df87ebe98eb1" }, "downloads": -1, "filename": "async_reduce-0.1.tar.gz", "has_sig": false, "md5_digest": "84096b7cb0f81acd36d13b5e6562dd94", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 4940, "upload_time": "2019-04-04T17:19:56", "url": "https://files.pythonhosted.org/packages/e3/e9/6e7a8b738b4a5d483753790e46ae8958a8b3303b3d99c3fae83fe4b2d596/async_reduce-0.1.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "3320be1e2ad2ef71e217c27c7c39b8ae", "sha256": "b9e5f0ae3d07e27270c140b0eb692f9563f0de7e565da24477716f2b25cbdc26" }, "downloads": -1, "filename": "async_reduce-0.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "3320be1e2ad2ef71e217c27c7c39b8ae", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 5591, "upload_time": "2019-04-06T08:53:31", "url": "https://files.pythonhosted.org/packages/04/64/fa674afce49178146104dd5a675b122fde8ac957dcf69abc7eb1f2c873c3/async_reduce-0.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "92ec28b1ea55145805fe8172c569c20f", "sha256": "dd1be01d03c30320b41a467e9734972115e6e32e58bac889722060f296be6794" }, "downloads": -1, "filename": "async_reduce-0.1.1.tar.gz", "has_sig": false, "md5_digest": "92ec28b1ea55145805fe8172c569c20f", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 4974, "upload_time": "2019-04-06T08:53:33", "url": "https://files.pythonhosted.org/packages/ba/db/29503b642c457901a4b3bca5d46683520860f65828116ce214a210b653ba/async_reduce-0.1.1.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "df87970e142ea5b41d8c36ef72d212e8", "sha256": "131680c1338c3be12c6cf7f349a850980df183e47e8444864c9fbdc1e84737a7" }, "downloads": -1, "filename": "async_reduce-0.1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "df87970e142ea5b41d8c36ef72d212e8", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 6201, "upload_time": "2019-04-16T19:15:56", "url": "https://files.pythonhosted.org/packages/92/96/b45d653d8936775e505799d4b0ebbe129068e2d130c39722a62ace411440/async_reduce-0.1.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ebb0df37248b309fc48d7dc7f9bed658", "sha256": "612e1c260ca727ee5d5052c781d903832fe4f57b8d50727b80eb18b3344fbb0e" }, "downloads": -1, "filename": "async_reduce-0.1.2.tar.gz", "has_sig": false, "md5_digest": "ebb0df37248b309fc48d7dc7f9bed658", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 5353, "upload_time": "2019-04-16T19:15:58", "url": "https://files.pythonhosted.org/packages/36/d4/f2800fdca2c7af9d7d8500a8bfdee84d3bd28bd43fd778740cbd3d97e37e/async_reduce-0.1.2.tar.gz" } ], "0.2": [ { "comment_text": "", "digests": { "md5": "04e51df9f691aa75919e669ff1718fcf", "sha256": "f520a3fcd4f577a7eacaf0cb173648a056df2fcd4379e2f6516bd879b17aa3d3" }, "downloads": -1, "filename": "async_reduce-0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "04e51df9f691aa75919e669ff1718fcf", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 11916, "upload_time": "2019-05-06T05:55:38", "url": "https://files.pythonhosted.org/packages/78/4e/42255c65d061a3e7010900763682494ec9549bc8406a48e522edeeaef78f/async_reduce-0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "fd1a5470285355dca1c7d042871b25ba", "sha256": "e22e42f0cf289f97e6ed0b9ea0cc3eded0d1fd1b72a45bc17c99914751fb76df" }, "downloads": -1, "filename": "async_reduce-0.2.tar.gz", "has_sig": false, "md5_digest": "fd1a5470285355dca1c7d042871b25ba", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 8963, "upload_time": "2019-05-06T05:55:40", "url": "https://files.pythonhosted.org/packages/1f/a1/4e0d6ba53465ebeb4ad7c257df2297bd41525d80ba2ef83e911019e7a931/async_reduce-0.2.tar.gz" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "d253711f8d3ad0a9c891b2f4d803fa40", "sha256": "8e7ff98dcf8145f3f1ffaf1afc552a6173db523a1d261745f7ee44c8870cf4b5" }, "downloads": -1, "filename": "async_reduce-0.2.1-py3-none-any.whl", "has_sig": false, "md5_digest": "d253711f8d3ad0a9c891b2f4d803fa40", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 12062, "upload_time": "2019-05-30T17:53:29", "url": "https://files.pythonhosted.org/packages/a3/f0/4edf3e25ebe26cc4d43f54807cdaacd4f8e92e81ec11b022ed8aca7966aa/async_reduce-0.2.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e46b2285a2ff09d9bf52e4467905444b", "sha256": "18494c057571222b9e222a2e16b8d64ad038c900e8530ee087cca55ac06dee2e" }, "downloads": -1, "filename": "async_reduce-0.2.1.tar.gz", "has_sig": false, "md5_digest": "e46b2285a2ff09d9bf52e4467905444b", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 9083, "upload_time": "2019-05-30T17:53:31", "url": "https://files.pythonhosted.org/packages/6b/ca/dfe7eb2cc0e89935cede9a14973c894e5255272c188723da10b7287948a6/async_reduce-0.2.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "d253711f8d3ad0a9c891b2f4d803fa40", "sha256": "8e7ff98dcf8145f3f1ffaf1afc552a6173db523a1d261745f7ee44c8870cf4b5" }, "downloads": -1, "filename": "async_reduce-0.2.1-py3-none-any.whl", "has_sig": false, "md5_digest": "d253711f8d3ad0a9c891b2f4d803fa40", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 12062, "upload_time": "2019-05-30T17:53:29", "url": "https://files.pythonhosted.org/packages/a3/f0/4edf3e25ebe26cc4d43f54807cdaacd4f8e92e81ec11b022ed8aca7966aa/async_reduce-0.2.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e46b2285a2ff09d9bf52e4467905444b", "sha256": "18494c057571222b9e222a2e16b8d64ad038c900e8530ee087cca55ac06dee2e" }, "downloads": -1, "filename": "async_reduce-0.2.1.tar.gz", "has_sig": false, "md5_digest": "e46b2285a2ff09d9bf52e4467905444b", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 9083, "upload_time": "2019-05-30T17:53:31", "url": "https://files.pythonhosted.org/packages/6b/ca/dfe7eb2cc0e89935cede9a14973c894e5255272c188723da10b7287948a6/async_reduce-0.2.1.tar.gz" } ] }