{ "info": { "author": "Alexandre Gravier", "author_email": "al.gravier@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 2 - Pre-Alpha", "Environment :: Plugins", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3 :: Only", "Topic :: Software Development :: Libraries" ], "description": "# aiohook \u2013 simple plugins for asyncio, trio and curio\n\n*aiohooks* is a small library meant to help implementing a plugin system in \nPython 3.7+ asynchronous applications developed with an _anyio_-compatible \nlibrary. It can also be used to set up synchronous function hooks.\n\n## Quickstart\n\n- Declare the signature of the hook coroutine by decorating a dummy `def` or\n`async def` (without implementation, or with a default one) with `aiohook.spec`.\n- `await` the dummy coroutine where you would want the plugin one to be called.\n- Implement the dummy coros in a separate module or class and decorate them with \n`aiohook.impl('reference.to.dummy')`.\n- Call `aiohook.register(reference)` to register the decorated implementations \nin `reference` (object instance or module). \n- Non-implemented hooks result in the default implementation being called.\n- Registering multiple implementations of the same hook raises an exception.\n\n## Basic usage\n\nIn this story, your name is Wallace, you are implementing an awesome async\napplication accepting plugins. Your user, Gromit, wants to make use of your\nplugin system to inject his custom parsing logic.\n\n### Application designer\n\nYou must first define the signature of each hook function, for\ninstance in a separate _pluginspecs.py_ file:\n\n```python\nfrom typing import AsyncIterator, Optional\nimport aiohook\n\n@aiohook.spec\nasync def tokenize(sentence: str) -> AsyncIterator[str]:\n # Describe the purpose of the hook, arguments etc in the docstring\n \"\"\"Split string of characters into individual tokens.\"\"\"\n # workaround for python's type system inability to declare a generator\n # function without a body with a 'yield' somewhere\n yield\n\n@aiohook.spec\nasync def transform_token(word: str) -> Optional[str]:\n \"\"\"Preprocess each raw token, for instance as a normalization step.\"\"\"\n```\n\nIn your application code, you then call your spec'ed out functions as if they\nhad already been implemented:\n\n```python\nimport sys, asyncio, importlib, aiohook\nfrom pluginspecs import tokenize, transform_token\n\nasync def main():\n with open(sys.argv[1], 'r') as f:\n source = f.read()\n async for token in tokenize(source):\n transformed = await transform_token(token)\n print(transformed, end='')\n\nif __name__ == '__main__':\n aiohook.register(importlib.import_module(sys.argv[1]))\n asyncio.run(main())\n``` \n\n### Plugin developer\n\nGromit wants to use Wallace's application to transform text files. He creates a \npip-installable module in which the _gromify.py_ file looks as follows:\n\n```python\nimport asyncio, aiohook, random\nfrom typing import AsyncIterator, Optional\n\n@aiohook.impl('pluginspecs.tokenize')\nasync def give_word(text: str) -> AsyncIterator[str]:\n for w in text.split():\n yield w\n\n@aiohook.impl('pluginspecs.transform_token')\nasync def bark_and_pause(word: str) -> Optional[str]:\n await asyncio.sleep(random.uniform(0, 2))\n return 'woo' + 'o'*(max(len(word) - 2, 0)) + 'f'\n```\n\nGromit pip-installs his plugin and Wallace's text processing app, and then calls \nthe latter, providing as first argument the absolute import path to the gromify\nmodule.\n\nNote that I haven't actually tried the above example, and also that it makes\nlittle sense to use async functions in this case, but I think it illustrates the\nbasic usage os aiohook nicely.\n\nFor less silly examples, please refer to the sample applications used as\nfunctional tests in [tests/functional](tests/functional)\n\n## Development\n\nTwo requirements files are used to describe the development setup:\n\n- The requirements.txt file describes a working development environment with all \npinned dependencies. \n- The requirements-base.txt file contains the direct unpinned dependencies only\nnecessary for a full development environment.\n\n### Step-by-step guide to set up a development environment\n\nThis assumes that [pyenv](https://github.com/pyenv/pyenv) and pyenv-virtualenv \nare installed, and the OS is somehow Unix-like. Commands are executed from the\nroot of the repo.\n\n```shell script\npyenv install 3.7.4\npyenv virtualenv 3.7.4 aiohook-dev-3.7.4 \npyenv activate aiohook-dev-3.7.4\n```\n\nTo work on the main library, it is best to just pip install it as an editable \npackage, and install the rest of the dev requirements:\n\n```shell script\npip install -e .\npip install -r requirements.txt\n```\n\nTo work on one of the functional test applications under\n[tests/functional(tests/functional), it is useful to also install it in editable\nmode in the same environment:\n\n```shell script\npip install -e tests/functional/text_processing_app\n# Try it out\nprocess_text textproc_plugin requirements.txt requirements-transformed.txt\n```\n\n### Testing with coverage \n\n```shell script\npytest --cov=aiohook tests/\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/agravier/aiohook", "keywords": "plugin,hook,async,asyncio,trio,curio", "license": "", "maintainer": "", "maintainer_email": "", "name": "aiohook", "package_url": "https://pypi.org/project/aiohook/", "platform": "", "project_url": "https://pypi.org/project/aiohook/", "project_urls": { "Homepage": "https://github.com/agravier/aiohook" }, "release_url": "https://pypi.org/project/aiohook/0.1.2/", "requires_dist": null, "requires_python": "", "summary": "A simple plugin system for async applications", "version": "0.1.2" }, "last_serial": 5798645, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "ba640123f147cee7e3098bbbb5cab089", "sha256": "a1fb5116e5c4813db29c0f662f51889bea6789c102f4ae6b45ee0acf22d87179" }, "downloads": -1, "filename": "aiohook-0.1.0.tar.gz", "has_sig": false, "md5_digest": "ba640123f147cee7e3098bbbb5cab089", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8344, "upload_time": "2019-08-26T20:08:06", "url": "https://files.pythonhosted.org/packages/49/34/7e823e0c53db4531e47bf275412bcb1d9c56e4576c7949dccc8bdc1aa186/aiohook-0.1.0.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "609bf691b65697881fc857ada184eba0", "sha256": "86504bd999d7278534e6199b080ab0e9bcfbdd2ec264053023d0c7dbbcdc5ba9" }, "downloads": -1, "filename": "aiohook-0.1.1.tar.gz", "has_sig": false, "md5_digest": "609bf691b65697881fc857ada184eba0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8065, "upload_time": "2019-08-29T19:45:11", "url": "https://files.pythonhosted.org/packages/fa/95/6857f5bf950626432054770c53b129ed65850a07d0ab27a3d49cefd88e6d/aiohook-0.1.1.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "bb505ba9fa71714b216a90a648e5ddfb", "sha256": "702e9a35e50a102a042fef91a068bd8b6c372a0016cfb4389d15e607a6a5a63f" }, "downloads": -1, "filename": "aiohook-0.1.2.tar.gz", "has_sig": false, "md5_digest": "bb505ba9fa71714b216a90a648e5ddfb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12271, "upload_time": "2019-09-08T07:54:01", "url": "https://files.pythonhosted.org/packages/32/8b/fd881cf405e45fe6a373c58af621302b1939be6737bb2304c4a0a4e95b4a/aiohook-0.1.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "bb505ba9fa71714b216a90a648e5ddfb", "sha256": "702e9a35e50a102a042fef91a068bd8b6c372a0016cfb4389d15e607a6a5a63f" }, "downloads": -1, "filename": "aiohook-0.1.2.tar.gz", "has_sig": false, "md5_digest": "bb505ba9fa71714b216a90a648e5ddfb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12271, "upload_time": "2019-09-08T07:54:01", "url": "https://files.pythonhosted.org/packages/32/8b/fd881cf405e45fe6a373c58af621302b1939be6737bb2304c4a0a4e95b4a/aiohook-0.1.2.tar.gz" } ] }