Package: simplug
simplugA simple plugin system for python with async hooks supportedInstallationpipinstall-UsimplugExamplesA toy examplefromsimplugimportSimplugsimplug=Simplug('project')classMySpec:"""A hook specification namespace."""@simplug.specdefmyhook(self,arg1,arg2):"""My special little hook that you can customize."""classPlugin_1:"""A hook implementation namespace."""@simplug.impldefmyhook(self,arg1,arg2):print("inside Plugin_1.myhook()")returnarg1+arg2classPlugin_2:"""A 2nd hook implementation namespace."""@simplug.impldefmyhook(self,arg1,arg2):print("inside Plugin_2.myhook()")returnarg1-arg2simplug.register(Plugin_1,Plugin_2)results=simplug.hooks.myhook(arg1=1,arg2=2)print(results)insidePlugin_1.myhook()insidePlugin_2.myhook()[3,-1]Note that the hooks are executed in the order the plugins are registered. This is different frompluggy.A complete exampleSeeexamples/complete/.Runningpython -m examples.completegets us:Yourfood.Enjoysomeegg,egg,egg,salt,pepper,egg,egg
Somecondiments?Wehavepickledwalnuts,steaksauce,mushypeas,mintsauceAfter install the plugin:>pipinstall--editableexamples.complete.plugin
>python-mexamples.complete# run againYourfood.Enjoysomeegg,egg,egg,salt,pepper,egg,egg,lovelyspam,wonderousspam
Somecondiments?Wehavepickledwalnuts,mushypeas,mintsauce,spamsauce
NowthisiswhatIcallacondimentstray!UsageDefinition of hooksHooks are specified and implemented by decorating the functions withsimplug.specandsimplug.implrespectively.simplugis initialized by:simplug=Simplug('project')The'project'is a unique name to mark the project, which makes sureSimplug('project')get the same instance each time.Note that ifsimplugis initialized withoutproject, then a name is generated automatically as suchproject-0,project-1, etc.Hook specification is marked bysimplug.spec:simplug=Simplug('project')@simplug.specdefsetup(args):...simplug.speccan take some arguments:required: Whether this hook is required to be implemented in pluginsresult: An enumerator to specify the way to collec the results.SimplugResult.ALL: Collect all results from all pluginsSimplugResult.ALL_AVAILS: Get all the results from the hook, as a list, not includingNonesSimplugResult.ALL_FIRST: Executing all implementations and get the first resultSimplugResult.ALL_LAST: Executing all implementations and get the last resultSimplugResult.TRY_ALL_FIRST: Executing all implementations and get the first result, if no result is returned, returnNoneSimplugResult.TRY_ALL_LAST: Executing all implementations and get the last result, if no result is returned, returnNoneSimplugResult.ALL_FIRST_AVAIL: Executing all implementations and get the first non-NoneresultSimplugResult.ALL_LAST_AVAIL: Executing all implementations and get the last non-NoneresultSimplugResult.TRY_ALL_FIRST_AVAIL: Executing all implementations and get the first non-Noneresult, if no result is returned, returnNoneSimplugResult.TRY_ALL_LAST_AVAIL: Executing all implementations and get the last non-Noneresult, if no result is returned, returnNoneSimplugResult.FIRST: Get the first result, don't execute other implementationsSimplugResult.LAST: Get the last result, don't execute other implementationsSimplugResult.TRY_FIRST: Get the first result, don't execute other implementations, if no result is returned, returnNoneSimplugResult.TRY_LAST: Get the last result, don't execute other implementations, if no result is returned, returnNoneSimplugResult.FIRST_AVAIL: Get the first non-Noneresult, don't execute other implementationsSimplugResult.LAST_AVAIL: Get the last non-Noneresult, don't execute other implementationsSimplugResult.TRY_FIRST_AVAIL: Get the first non-Noneresult, don't execute other implementations, if no result is returned, returnNoneSimplugResult.TRY_LAST_AVAIL: Get the last non-Noneresult, don't execute other implementations, if no result is returned, returnNoneSimplugResult.SINGLE: Get the result from a single implementationSimplugResult.TRY_SINGLE: Get the result from a single implementation, if no result is returned, returnNoneA callable to collect the result, takecallsas the argument, a 3-element tuple with first element as the implementation, second element as the positional arguments, and third element as the keyword arguments.Hook implementation is marked bysimplug.impl, which takes no additional arguments.The name of the function has to match the name of the function bysimplug.spec. And the signatures of the specification function and the implementation function have to be the same in terms of names. This means you can specify default values in the specification function, but you don't have to write the default values in the implementation function.Note that default values in implementation functions will be ignored.Also note if a hook specification is under a namespace, it can takeselfas argument. However, this argument will be ignored while the hook is being called (selfwill beNone, and you still have to specify it in the function definition).Loading plugins from setuptools entrypointYou have to callsimplug.load_entrypoints(group)after the hook specifications are defined to load the plugins registered by setuptools entrypoint. Ifgroupis not given, the project name will be used.The plugin registryThe plugins are registered bysimplug.register(*plugins). Each plugin ofpluginscan be either a python object or a str denoting a module that can be imported byimportlib.import_module.The python object must have an attributename,__name__or__class.__name__forsimplugto determine the name of the plugin. If the plugin name is determined from__name__or__class__.__name__, it will be lowercased.If a plugin is loaded from setuptools entrypoint, then the entrypoint name will be used (no matter what name is defined inside the plugin)You can enable or disable a plugin temporarily after registration by:simplug.disable('plugin_name')simplug.enable('plugin_name')You can use following methods to inspect the plugin registry:simplug.get_plugin: Get the plugin by namesimplug.get_all_plugins: Get a dictionary of name-plugin mappings of all pluginssimplug.get_all_plugin_names: Get the names of all plugins, in the order it will be executed.simplug.get_enabled_plugins: Get a dictionary of name-plugin mappings of all enabled pluginssimplug.get_enabled_plugin_names: Get the names of all enabled plugins, in the order it will be executed.Calling hooksHooks are call bysimplug.hooks.<hook_name>(<arguments>)and results are collected based on theresultargument passed insimplug.specwhen defining hooks.Async hooksIt makes no big difference to define an async hook:@simplug.specasyncdefasync_hook(arg):...# to supress warnings for sync implementation@simplug.spec(warn_sync_impl_on_async=False)asyncdefasync_hook(arg):...One can implement this hook in either an async or a sync way. However, when implementing it in a sync way, a warning will be raised. To suppress the warning, one can pass aFalsevalue of argumentwarn_sync_impl_on_asynctosimplug.spec.To call the async hooks (simplug.hooks.async_hook(arg)), you will just need to call it like any other async functions (usingasyncio.run, for example)APIhttps://pwwang.github.io/simplug/