Package: aiohook
aiohook – simple plugins for asyncio, trio and curioaiohooksis a small library meant to help implementing a plugin system in
Python 3.7+ asynchronous applications developed with ananyio-compatible
library. It can also be used to set up synchronous function hooks.QuickstartDeclare the signature of the hook coroutine by decorating a dummydeforasync def(without implementation, or with a default one) withaiohook.spec.awaitthe dummy coroutine where you would want the plugin one to be called.Implement the dummy coros in a separate module or class and decorate them withaiohook.impl('reference.to.dummy').Callaiohook.register(reference)to register the decorated implementations
inreference(object instance or module).Non-implemented hooks result in the default implementation being called.Registering multiple implementations of the same hook raises an exception.Basic usageIn this story, your name is Wallace, you are implementing an awesome async
application accepting plugins. Your user, Gromit, wants to make use of your
plugin system to inject his custom parsing logic.Application designerYou must first define the signature of each hook function, for
instance in a separatepluginspecs.pyfile:fromtypingimportAsyncIterator,Optionalimportaiohook@aiohook.specasyncdeftokenize(sentence:str)->AsyncIterator[str]:# Describe the purpose of the hook, arguments etc in the docstring"""Split string of characters into individual tokens."""# workaround for python's type system inability to declare a generator# function without a body with a 'yield' somewhereyield@aiohook.specasyncdeftransform_token(word:str)->Optional[str]:"""Preprocess each raw token, for instance as a normalization step."""In your application code, you then call your spec'ed out functions as if they
had already been implemented:importsys,asyncio,importlib,aiohookfrompluginspecsimporttokenize,transform_tokenasyncdefmain():withopen(sys.argv[1],'r')asf:source=f.read()asyncfortokenintokenize(source):transformed=awaittransform_token(token)print(transformed,end='')if__name__=='__main__':aiohook.register(importlib.import_module(sys.argv[1]))asyncio.run(main())Plugin developerGromit wants to use Wallace's application to transform text files. He creates a
pip-installable module in which thegromify.pyfile looks as follows:importasyncio,aiohook,randomfromtypingimportAsyncIterator,Optional@aiohook.impl('pluginspecs.tokenize')asyncdefgive_word(text:str)->AsyncIterator[str]:forwintext.split():yieldw@aiohook.impl('pluginspecs.transform_token')asyncdefbark_and_pause(word:str)->Optional[str]:awaitasyncio.sleep(random.uniform(0,2))return'woo'+'o'*(max(len(word)-2,0))+'f'Gromit pip-installs his plugin and Wallace's text processing app, and then calls
the latter, providing as first argument the absolute import path to the gromify
module.Note that I haven't actually tried the above example, and also that it makes
little sense to use async functions in this case, but I think it illustrates the
basic usage os aiohook nicely.For less silly examples, please refer to the sample applications used as
functional tests intests/functionalDevelopmentTwo requirements files are used to describe the development setup:The requirements.txt file describes a working development environment with all
pinned dependencies.The requirements-base.txt file contains the direct unpinned dependencies only
necessary for a full development environment.Step-by-step guide to set up a development environmentThis assumes thatpyenvand pyenv-virtualenv
are installed, and the OS is somehow Unix-like. Commands are executed from the
root of the repo.pyenvinstall3.7.4
pyenvvirtualenv3.7.4aiohook-dev-3.7.4pyenvactivateaiohook-dev-3.7.4To work on the main library, it is best to just pip install it as an editable
package, and install the rest of the dev requirements:pipinstall-e.
pipinstall-rrequirements.txtTo work on one of the functional test applications under
[tests/functional(tests/functional), it is useful to also install it in editable
mode in the same environment:pipinstall-etests/functional/text_processing_app# Try it outprocess_texttextproc_pluginrequirements.txtrequirements-transformed.txtTesting with coveragepytest--cov=aiohooktests/