Name | apluggy JSON |
Version |
1.0.2
JSON |
| download |
home_page | None |
Summary | A wrapper of "pluggy" to support asyncio and context managers |
upload_time | 2024-09-23 20:46:06 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.9 |
license | None |
keywords |
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# apluggy
[](https://pypi.org/project/apluggy)
[](https://pypi.org/project/apluggy)
[](https://github.com/simonsobs/apluggy/actions/workflows/unit-test.yml)
[](https://github.com/simonsobs/apluggy/actions/workflows/type-check.yml)
[](https://codecov.io/gh/simonsobs/apluggy)
A wrapper of [pluggy](https://pluggy.readthedocs.io/) to support asyncio and context managers.
This package provides a subclass of
[`pluggy.PluginManager`](https://pluggy.readthedocs.io/en/stable/api_reference.html#pluggy.PluginManager)
which
- allows async functions, context managers, and async context managers to be hooks
- and accepts plugin factories in addition to plugin instances for registration.
---
**Table of Contents**
- [Installation](#installation)
- [How to use](#how-to-use)
- [Start Python](#start-python)
- [Import packages](#import-packages)
- [Create hook specification and implementation decorators](#create-hook-specification-and-implementation-decorators)
- [Define hook specifications](#define-hook-specifications)
- [Define plugins](#define-plugins)
- [Create a plugin manager and register plugins](#create-a-plugin-manager-and-register-plugins)
- [Call hooks](#call-hooks)
- [Async function](#async-function)
- [Context manager](#context-manager)
- [Async context manager](#async-context-manager)
- [Links](#links)
- [License](#license)
---
## Installation
You can install apluggy with pip:
```console
pip install apluggy
```
---
## How to use
Here, we show a simple example of how to use apluggy.
We only describe the usage of additional features provided by apluggy. For the
usage of pluggy itself, please refer to the [pluggy
documentation](https://pluggy.readthedocs.io/).
### Start Python
You can try this example in a Python interpreter.
```console
$ python
Python 3.10.13 (...)
...
...
>>>
```
### Import packages
Import necessary packages of this example.
```python
>>> import asyncio
>>> import apluggy as pluggy
>>> from apluggy import asynccontextmanager, contextmanager
```
In this example, `apluggy` is imported with the alias `pluggy`.
The decorators `asynccontextmanager` and `contextmanager` are imported from
`apluggy`. They are wrappers of the decorators of the same names in the
[contextlib package](https://docs.python.org/3/library/contextlib.html). The
wrappers preserve the signatures of decorated functions, which are necessary for
pluggy to pass arguments to hook implementations correctly. (The decorator
`contextmanger` in `apluggy` is the same object as the decorator
`contextmanager` in the [decorator
package](https://pypi.org/project/decorator/). The decorator package does not
provide `asynccontextmanager` decorator as of version 5.1. The decorator
`asynccontextmanger` in `apluggy` is implemented in a similar way as the
decorator `contextmanager` in the decorator package.)
### Create hook specification and implementation decorators
```python
>>> hookspec = pluggy.HookspecMarker('project')
>>> hookimpl = pluggy.HookimplMarker('project')
```
### Define hook specifications
In this example, we define three hooks: async function, context manager, and
async context manager.
```python
>>> class Spec:
... """A hook specification namespace."""
...
... @hookspec
... async def afunc(self, arg1, arg2):
... pass
...
... @hookspec
... @contextmanager
... def context(self, arg1, arg2):
... pass
...
... @hookspec
... @asynccontextmanager
... async def acontext(self, arg1, arg2):
... pass
```
### Define plugins
We define two plugins as classes. Each plugin implements the three hooks
defined above.
```python
>>> class Plugin_1:
... """A hook implementation namespace."""
...
... @hookimpl
... async def afunc(self, arg1, arg2):
... print('inside Plugin_1.afunc()')
... return arg1 + arg2
...
... @hookimpl
... @contextmanager
... def context(self, arg1, arg2):
... print('inside Plugin_1.context(): before')
... yield arg1 + arg2
... print('inside Plugin_1.context(): after')
...
... @hookimpl
... @asynccontextmanager
... async def acontext(self, arg1, arg2):
... print('inside Plugin_1.acontext(): before')
... yield arg1 + arg2
... print('inside Plugin_1.acontext(): after')
>>> class Plugin_2:
... """A 2nd hook implementation namespace."""
...
... @hookimpl
... async def afunc(self, arg1, arg2):
... print('inside Plugin_2.afunc()')
... return arg1 - arg2
...
... @hookimpl
... @contextmanager
... def context(self, arg1, arg2):
... print('inside Plugin_2.context(): before')
... yield arg1 - arg2
... print('inside Plugin_2.context(): after')
...
... @hookimpl
... @asynccontextmanager
... async def acontext(self, arg1, arg2):
... print('inside Plugin_2.acontext(): before')
... yield arg1 - arg2
... print('inside Plugin_2.acontext(): after')
```
### Create a plugin manager and register plugins
Plugins can be registered as instances or factories. In the following
example, we register two plugins: `Plugin_1` as an instance, and `Plugin_2`
as a factory.
```python
>>> pm = pluggy.PluginManager('project')
>>> pm.add_hookspecs(Spec)
>>> _ = pm.register(Plugin_1()) # instantiation is optional.
>>> _ = pm.register(Plugin_2) # callable is considered a plugin factory.
```
[Pluggy accepts a class or
module](https://pluggy.readthedocs.io/en/stable/#define-and-collect-hooks) as a
plugin. However, it actually accepts a class instance, not a class itself.
Consequently, when plugins are loaded with
[`load_setuptools_entrypoints()`](https://pluggy.readthedocs.io/en/stable/api_reference.html#pluggy.PluginManager.load_setuptools_entrypoints),
the entry points must be class instances or modules. Classes themselves cannot
be used as entry points (if understood correctly).
So that classes themselves can be entry points, apluggy accepts a class itself for
a plugin registration. When apluggy receives a callable object, apluggy considers
the object as a plugin factory.
### Call hooks
The following example shows how to call hooks.
#### Async function
```python
>>> async def call_afunc():
... results = await pm.ahook.afunc(arg1=1, arg2=2) # ahook instead of hook
... print(results)
>>> asyncio.run(call_afunc())
inside Plugin_2.afunc()
inside Plugin_1.afunc()
[-1, 3]
```
#### Context manager
```python
>>> with pm.with_.context(arg1=1, arg2=2) as y: # with_ instead of hook
... print(y)
inside Plugin_2.context(): before
inside Plugin_1.context(): before
[-1, 3]
inside Plugin_1.context(): after
inside Plugin_2.context(): after
```
In the reverse order:
```python
>>> with pm.with_reverse.context(arg1=1, arg2=2) as y: # with_reverse instead of hook
... print(y)
inside Plugin_1.context(): before
inside Plugin_2.context(): before
[3, -1]
inside Plugin_2.context(): after
inside Plugin_1.context(): after
```
#### Async context manager
```python
>>> async def call_acontext():
... async with pm.awith.acontext(arg1=1, arg2=2) as y: # awith instead of hook
... print(y)
>>> asyncio.run(call_acontext())
inside Plugin_2.acontext(): before
inside Plugin_1.acontext(): before
[-1, 3]
inside Plugin_1.acontext(): after
inside Plugin_2.acontext(): after
```
In the reverse order:
```python
>>> async def call_acontext():
... async with pm.awith_reverse.acontext(arg1=1, arg2=2) as y: # awith_reverse instead of hook
... print(y)
>>> asyncio.run(call_acontext())
inside Plugin_1.acontext(): before
inside Plugin_2.acontext(): before
[3, -1]
inside Plugin_2.acontext(): after
inside Plugin_1.acontext(): after
```
---
## Links
- [pluggy](https://pluggy.readthedocs.io/)
- [decorator](https://pypi.org/project/decorator/)
---
## License
- _apluggy_ is licensed under the [MIT](https://spdx.org/licenses/MIT.html) license.
Raw data
{
"_id": null,
"home_page": null,
"name": "apluggy",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": null,
"author": null,
"author_email": "Tai Sakuma <tai.sakuma@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/a2/16/0ffea012c795cc9b876d4fe4644dda5954aeb5b1a9532aeae78f91624c04/apluggy-1.0.2.tar.gz",
"platform": null,
"description": "# apluggy\n\n[](https://pypi.org/project/apluggy)\n[](https://pypi.org/project/apluggy)\n\n[](https://github.com/simonsobs/apluggy/actions/workflows/unit-test.yml)\n[](https://github.com/simonsobs/apluggy/actions/workflows/type-check.yml)\n[](https://codecov.io/gh/simonsobs/apluggy)\n\nA wrapper of [pluggy](https://pluggy.readthedocs.io/) to support asyncio and context managers.\n\nThis package provides a subclass of\n[`pluggy.PluginManager`](https://pluggy.readthedocs.io/en/stable/api_reference.html#pluggy.PluginManager)\nwhich\n\n- allows async functions, context managers, and async context managers to be hooks\n- and accepts plugin factories in addition to plugin instances for registration.\n\n---\n\n**Table of Contents**\n\n- [Installation](#installation)\n- [How to use](#how-to-use)\n - [Start Python](#start-python)\n - [Import packages](#import-packages)\n - [Create hook specification and implementation decorators](#create-hook-specification-and-implementation-decorators)\n - [Define hook specifications](#define-hook-specifications)\n - [Define plugins](#define-plugins)\n - [Create a plugin manager and register plugins](#create-a-plugin-manager-and-register-plugins)\n - [Call hooks](#call-hooks)\n - [Async function](#async-function)\n - [Context manager](#context-manager)\n - [Async context manager](#async-context-manager)\n- [Links](#links)\n- [License](#license)\n\n---\n\n## Installation\n\nYou can install apluggy with pip:\n\n```console\npip install apluggy\n```\n\n---\n\n## How to use\n\nHere, we show a simple example of how to use apluggy.\n\nWe only describe the usage of additional features provided by apluggy. For the\nusage of pluggy itself, please refer to the [pluggy\ndocumentation](https://pluggy.readthedocs.io/).\n\n### Start Python\n\nYou can try this example in a Python interpreter.\n\n```console\n$ python\nPython 3.10.13 (...)\n...\n...\n>>>\n```\n\n### Import packages\n\nImport necessary packages of this example.\n\n```python\n>>> import asyncio\n>>> import apluggy as pluggy\n>>> from apluggy import asynccontextmanager, contextmanager\n\n```\n\nIn this example, `apluggy` is imported with the alias `pluggy`.\n\nThe decorators `asynccontextmanager` and `contextmanager` are imported from\n`apluggy`. They are wrappers of the decorators of the same names in the\n[contextlib package](https://docs.python.org/3/library/contextlib.html). The\nwrappers preserve the signatures of decorated functions, which are necessary for\npluggy to pass arguments to hook implementations correctly. (The decorator\n`contextmanger` in `apluggy` is the same object as the decorator\n`contextmanager` in the [decorator\npackage](https://pypi.org/project/decorator/). The decorator package does not\nprovide `asynccontextmanager` decorator as of version 5.1. The decorator\n`asynccontextmanger` in `apluggy` is implemented in a similar way as the\ndecorator `contextmanager` in the decorator package.)\n\n### Create hook specification and implementation decorators\n\n```python\n>>> hookspec = pluggy.HookspecMarker('project')\n>>> hookimpl = pluggy.HookimplMarker('project')\n\n```\n\n### Define hook specifications\n\nIn this example, we define three hooks: async function, context manager, and\nasync context manager.\n\n```python\n>>> class Spec:\n... \"\"\"A hook specification namespace.\"\"\"\n...\n... @hookspec\n... async def afunc(self, arg1, arg2):\n... pass\n...\n... @hookspec\n... @contextmanager\n... def context(self, arg1, arg2):\n... pass\n...\n... @hookspec\n... @asynccontextmanager\n... async def acontext(self, arg1, arg2):\n... pass\n\n```\n\n### Define plugins\n\nWe define two plugins as classes. Each plugin implements the three hooks\ndefined above.\n\n```python\n>>> class Plugin_1:\n... \"\"\"A hook implementation namespace.\"\"\"\n...\n... @hookimpl\n... async def afunc(self, arg1, arg2):\n... print('inside Plugin_1.afunc()')\n... return arg1 + arg2\n...\n... @hookimpl\n... @contextmanager\n... def context(self, arg1, arg2):\n... print('inside Plugin_1.context(): before')\n... yield arg1 + arg2\n... print('inside Plugin_1.context(): after')\n...\n... @hookimpl\n... @asynccontextmanager\n... async def acontext(self, arg1, arg2):\n... print('inside Plugin_1.acontext(): before')\n... yield arg1 + arg2\n... print('inside Plugin_1.acontext(): after')\n\n>>> class Plugin_2:\n... \"\"\"A 2nd hook implementation namespace.\"\"\"\n...\n... @hookimpl\n... async def afunc(self, arg1, arg2):\n... print('inside Plugin_2.afunc()')\n... return arg1 - arg2\n...\n... @hookimpl\n... @contextmanager\n... def context(self, arg1, arg2):\n... print('inside Plugin_2.context(): before')\n... yield arg1 - arg2\n... print('inside Plugin_2.context(): after')\n...\n... @hookimpl\n... @asynccontextmanager\n... async def acontext(self, arg1, arg2):\n... print('inside Plugin_2.acontext(): before')\n... yield arg1 - arg2\n... print('inside Plugin_2.acontext(): after')\n\n```\n\n### Create a plugin manager and register plugins\n\nPlugins can be registered as instances or factories. In the following\nexample, we register two plugins: `Plugin_1` as an instance, and `Plugin_2`\nas a factory.\n\n```python\n>>> pm = pluggy.PluginManager('project')\n>>> pm.add_hookspecs(Spec)\n>>> _ = pm.register(Plugin_1()) # instantiation is optional.\n>>> _ = pm.register(Plugin_2) # callable is considered a plugin factory.\n\n```\n\n[Pluggy accepts a class or\nmodule](https://pluggy.readthedocs.io/en/stable/#define-and-collect-hooks) as a\nplugin. However, it actually accepts a class instance, not a class itself.\nConsequently, when plugins are loaded with\n[`load_setuptools_entrypoints()`](https://pluggy.readthedocs.io/en/stable/api_reference.html#pluggy.PluginManager.load_setuptools_entrypoints),\nthe entry points must be class instances or modules. Classes themselves cannot\nbe used as entry points (if understood correctly).\n\nSo that classes themselves can be entry points, apluggy accepts a class itself for\na plugin registration. When apluggy receives a callable object, apluggy considers\nthe object as a plugin factory.\n\n### Call hooks\n\nThe following example shows how to call hooks.\n\n#### Async function\n\n```python\n>>> async def call_afunc():\n... results = await pm.ahook.afunc(arg1=1, arg2=2) # ahook instead of hook\n... print(results)\n\n>>> asyncio.run(call_afunc())\ninside Plugin_2.afunc()\ninside Plugin_1.afunc()\n[-1, 3]\n\n```\n\n#### Context manager\n\n```python\n>>> with pm.with_.context(arg1=1, arg2=2) as y: # with_ instead of hook\n... print(y)\ninside Plugin_2.context(): before\ninside Plugin_1.context(): before\n[-1, 3]\ninside Plugin_1.context(): after\ninside Plugin_2.context(): after\n\n```\n\nIn the reverse order:\n\n```python\n>>> with pm.with_reverse.context(arg1=1, arg2=2) as y: # with_reverse instead of hook\n... print(y)\ninside Plugin_1.context(): before\ninside Plugin_2.context(): before\n[3, -1]\ninside Plugin_2.context(): after\ninside Plugin_1.context(): after\n\n```\n\n#### Async context manager\n\n```python\n>>> async def call_acontext():\n... async with pm.awith.acontext(arg1=1, arg2=2) as y: # awith instead of hook\n... print(y)\n\n>>> asyncio.run(call_acontext())\ninside Plugin_2.acontext(): before\ninside Plugin_1.acontext(): before\n[-1, 3]\ninside Plugin_1.acontext(): after\ninside Plugin_2.acontext(): after\n\n```\n\nIn the reverse order:\n\n```python\n>>> async def call_acontext():\n... async with pm.awith_reverse.acontext(arg1=1, arg2=2) as y: # awith_reverse instead of hook\n... print(y)\n\n>>> asyncio.run(call_acontext())\ninside Plugin_1.acontext(): before\ninside Plugin_2.acontext(): before\n[3, -1]\ninside Plugin_2.acontext(): after\ninside Plugin_1.acontext(): after\n\n```\n\n---\n\n## Links\n\n- [pluggy](https://pluggy.readthedocs.io/)\n- [decorator](https://pypi.org/project/decorator/)\n\n---\n\n## License\n\n- _apluggy_ is licensed under the [MIT](https://spdx.org/licenses/MIT.html) license.\n",
"bugtrack_url": null,
"license": null,
"summary": "A wrapper of \"pluggy\" to support asyncio and context managers",
"version": "1.0.2",
"project_urls": {
"Documentation": "https://github.com/simonsobs/apluggy#readme",
"Issues": "https://github.com/simonsobs/apluggy/issues",
"Source": "https://github.com/simonsobs/apluggy"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "13492d85d872ba9764ad1825a2c95c9af7c412715c287c89fe26a216d3809edc",
"md5": "48c206b4e1220fff7644691277f53061",
"sha256": "5fac688745476f85fb226366ce25ea80e48ee7b0663beb5cd6dd33fe2344c629"
},
"downloads": -1,
"filename": "apluggy-1.0.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "48c206b4e1220fff7644691277f53061",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 13054,
"upload_time": "2024-09-23T20:46:05",
"upload_time_iso_8601": "2024-09-23T20:46:05.668865Z",
"url": "https://files.pythonhosted.org/packages/13/49/2d85d872ba9764ad1825a2c95c9af7c412715c287c89fe26a216d3809edc/apluggy-1.0.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "a2160ffea012c795cc9b876d4fe4644dda5954aeb5b1a9532aeae78f91624c04",
"md5": "b906f9f09d5f75fcb49154b9fd739b5a",
"sha256": "87376a0d1e212976d7ab0e68e88069483c93a006275529472f1649fd1f115c6c"
},
"downloads": -1,
"filename": "apluggy-1.0.2.tar.gz",
"has_sig": false,
"md5_digest": "b906f9f09d5f75fcb49154b9fd739b5a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 23107,
"upload_time": "2024-09-23T20:46:06",
"upload_time_iso_8601": "2024-09-23T20:46:06.543856Z",
"url": "https://files.pythonhosted.org/packages/a2/16/0ffea012c795cc9b876d4fe4644dda5954aeb5b1a9532aeae78f91624c04/apluggy-1.0.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-09-23 20:46:06",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "simonsobs",
"github_project": "apluggy#readme",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "apluggy"
}