A wrapper of "pluggy" to support asyncio and context managers
Project description
apluggy
A wrapper of pluggy to support asyncio and context managers.
This package provides a subclass of
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
You can install apluggy with pip:
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.
Start Python
You can try this example in a Python interpreter.
$ python
Python 3.10.13 (...)
...
...
>>>
Import packages
Import necessary packages of this example.
>>> import asyncio
>>> from contextlib import asynccontextmanager, contextmanager
>>> import apluggy as pluggy
In this example, apluggy
is imported with the alias pluggy
.
Create hook specification and implementation decorators
>>> 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.
>>> 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.
>>> 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.
>>> 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 as a
plugin. However, it actually accepts a class instance, not a class itself.
Consequently, when plugins are loaded with
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
>>> 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
>>> 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:
>>> 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
>>> 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:
>>> 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
License
- apluggy is licensed under the MIT license.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file apluggy-1.1.0.tar.gz
.
File metadata
- Download URL: apluggy-1.1.0.tar.gz
- Upload date:
- Size: 27.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.8
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 |
3273887b6d4b2d0d7cba0f620f8e9e5c492a40b492b93eb928bf28c95475a6ee
|
|
MD5 |
874ab211ef7e153da8ce736d7c7af9bb
|
|
BLAKE2b-256 |
97ec01f4e36a8991d934459dba376ebb34bc7f01d1b04dd4546f39dffb6f802a
|
Provenance
The following attestation bundles were made for apluggy-1.1.0.tar.gz
:
Publisher:
pypi.yml
on simonsobs/apluggy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1
-
Predicate type:
https://docs.pypi.org/attestations/publish/v1
-
Subject name:
apluggy-1.1.0.tar.gz
-
Subject digest:
3273887b6d4b2d0d7cba0f620f8e9e5c492a40b492b93eb928bf28c95475a6ee
- Sigstore transparency entry: 171937204
- Sigstore integration time:
-
Permalink:
simonsobs/apluggy@0412c37bb8571c3629efe5ea01825a421c1c226b
-
Branch / Tag:
refs/tags/v1.1.0
- Owner: https://github.com/simonsobs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com
-
Runner Environment:
github-hosted
-
Publication workflow:
pypi.yml@0412c37bb8571c3629efe5ea01825a421c1c226b
-
Trigger Event:
push
-
Statement type:
File details
Details for the file apluggy-1.1.0-py3-none-any.whl
.
File metadata
- Download URL: apluggy-1.1.0-py3-none-any.whl
- Upload date:
- Size: 12.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.8
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 |
b0918965ef763513c042b961191095e0d3bd258fcb1ebd9fa369ab969338a9dc
|
|
MD5 |
db0f5d7f8077ce4584d58470755f3cf1
|
|
BLAKE2b-256 |
26428dd33468d0587c6734af92ff6d68cd3cc5fe4376edc7e5b4a9f97d81ff5b
|
Provenance
The following attestation bundles were made for apluggy-1.1.0-py3-none-any.whl
:
Publisher:
pypi.yml
on simonsobs/apluggy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1
-
Predicate type:
https://docs.pypi.org/attestations/publish/v1
-
Subject name:
apluggy-1.1.0-py3-none-any.whl
-
Subject digest:
b0918965ef763513c042b961191095e0d3bd258fcb1ebd9fa369ab969338a9dc
- Sigstore transparency entry: 171937206
- Sigstore integration time:
-
Permalink:
simonsobs/apluggy@0412c37bb8571c3629efe5ea01825a421c1c226b
-
Branch / Tag:
refs/tags/v1.1.0
- Owner: https://github.com/simonsobs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com
-
Runner Environment:
github-hosted
-
Publication workflow:
pypi.yml@0412c37bb8571c3629efe5ea01825a421c1c226b
-
Trigger Event:
push
-
Statement type: