Package: apluggy
apluggyA wrapper ofpluggyto support asyncio and context managers.This package provides a subclass ofpluggy.PluginManagerwhichallows async functions, context managers, and async context managers to be hooksand accepts plugin factories in addition to plugin instances for registration.Table of ContentsapluggyInstallationHow to useLinksLicenseContactInstallationYou can install apluggy with pip:pip install apluggyHow to useHere, 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 thepluggy
documentation.Start PythonYou can try this example in a Python interpreter.$pythonPython 3.10.13 (...)......>>>Import packagesImport necessary packages of this example.>>>importasyncio>>>importapluggyaspluggy>>>fromapluggyimportasynccontextmanager,contextmanagerIn this example,apluggyis imported with the aliaspluggy.The decoratorsasynccontextmanagerandcontextmanagerare imported fromapluggy. They are wrappers of the decorators of the same names in thecontextlib package. The
wrappers preserve the signatures of decorated functions, which are necessary for
pluggy to pass arguments to hook implementations correctly. (The decoratorcontextmangerinapluggyis the same object as the decoratorcontextmanagerin thedecorator
package. The decorator package does not
provideasynccontextmanagerdecorator as of version 5.1. The decoratorasynccontextmangerinapluggyis implemented in a similar way as the
decoratorcontextmanagerin the decorator package.)Create hook specification and implementation decorators>>>hookspec=pluggy.HookspecMarker('project')>>>hookimpl=pluggy.HookimplMarker('project')Define hook specificationsIn this example, we define three hooks: async function, context manager, and
async context manager.>>>classSpec:..."""A hook specification namespace."""......@hookspec...asyncdefafunc(self,arg1,arg2):...pass......@hookspec...@contextmanager...defcontext(self,arg1,arg2):...pass......@hookspec...@asynccontextmanager...asyncdefacontext(self,arg1,arg2):...passDefine pluginsWe define two plugins as classes. Each plugin implements the three hooks
defined above.>>>classPlugin_1:..."""A hook implementation namespace."""......@hookimpl...asyncdefafunc(self,arg1,arg2):...print('inside Plugin_1.afunc()')...returnarg1+arg2......@hookimpl...@contextmanager...defcontext(self,arg1,arg2):...print('inside Plugin_1.context(): before')...yieldarg1+arg2...print('inside Plugin_1.context(): after')......@hookimpl...@asynccontextmanager...asyncdefacontext(self,arg1,arg2):...print('inside Plugin_1.acontext(): before')...yieldarg1+arg2...print('inside Plugin_1.acontext(): after')>>>classPlugin_2:..."""A 2nd hook implementation namespace."""......@hookimpl...asyncdefafunc(self,arg1,arg2):...print('inside Plugin_2.afunc()')...returnarg1-arg2......@hookimpl...@contextmanager...defcontext(self,arg1,arg2):...print('inside Plugin_2.context(): before')...yieldarg1-arg2...print('inside Plugin_2.context(): after')......@hookimpl...@asynccontextmanager...asyncdefacontext(self,arg1,arg2):...print('inside Plugin_2.acontext(): before')...yieldarg1-arg2...print('inside Plugin_2.acontext(): after')Create a plugin manager and register pluginsPlugins can be registered as instances or factories. In the following
example, we register two plugins:Plugin_1as an instance, andPlugin_2as 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
moduleas a
plugin. However, it actually accepts a class instance, not a class itself.
Consequently, when plugins are loaded withload_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 hooksThe following example shows how to call hooks.Async function>>>asyncdefcall_afunc():...results=awaitpm.ahook.afunc(arg1=1,arg2=2)# ahook instead of hook...print(results)>>>asyncio.run(call_afunc())insidePlugin_2.afunc()insidePlugin_1.afunc()[-1,3]Context manager>>>withpm.with_.context(arg1=1,arg2=2)asy:# with_ instead of hook...print(y)insidePlugin_2.context():beforeinsidePlugin_1.context():before[-1,3]insidePlugin_1.context():afterinsidePlugin_2.context():afterIn the reverse order:>>>withpm.with_reverse.context(arg1=1,arg2=2)asy:# with_reverse instead of hook...print(y)insidePlugin_1.context():beforeinsidePlugin_2.context():before[3,-1]insidePlugin_2.context():afterinsidePlugin_1.context():afterAsync context manager>>>asyncdefcall_acontext():...asyncwithpm.awith.acontext(arg1=1,arg2=2)asy:# awith instead of hook...print(y)>>>asyncio.run(call_acontext())insidePlugin_2.acontext():beforeinsidePlugin_1.acontext():before[-1,3]insidePlugin_1.acontext():afterinsidePlugin_2.acontext():afterIn the reverse order:>>>asyncdefcall_acontext():...asyncwithpm.awith_reverse.acontext(arg1=1,arg2=2)asy:# awith_reverse instead of hook...print(y)>>>asyncio.run(call_acontext())insidePlugin_1.acontext():beforeinsidePlugin_2.acontext():before[3,-1]insidePlugin_2.acontext():afterinsidePlugin_1.acontext():afterLinkspluggydecoratorLicenseapluggyis licensed under theMITlicense.ContactTai Sakuma