Implementing a plugin using pluggy
# curl.py import requests def main(url): session = requests.Session() request = requests.Request('GET', url) prepped = request.prepare() response = session.send(prepped) print(response.text) if __name__ == '__main__': main('http://localhost:8000/curl.py')
# hookspec.py import pluggy hookspec = pluggy.HookspecMarker('curl') @hookspec def curl_prepare_headers(headers, session): """Prepare the HTTP headers. :param headers: HTTP headers structure to modify in-place. :type headers: dict :param session: HTTP session object :type session: requests.Session """ pass
curl_
-prefix is convention
# plugin.py import curl @curl.hookimpl def curl_prepare_headers(headers): headers['X-Spam'] = 'eggs'
# curl.py import requests, pluggy, hookspec, importlib hookimpl = pluggy.HookimplMarker('curl') def main(url): pm = pluggy.PluginManager('curl') pm.add_hookspecs(hookspec) plugin = importlib.import_module('plugin') pm.register(plugin) session = requests.Session() request = requests.Request('GET', url) prepped = request.prepare() pm.hook.curl_prepare_headers(headers=prepped.headers, session=session) response = session.send(prepped) print(response.text) if __name__ == '__main__': main('http://localhost:8000/curl.py')
None
is swallowed
# hookspec.py import pluggy hookspec = pluggy.HookspecMarker('curl') @hookspec def curl_prepare_headers(headers, session): pass @hookspec def curl_filter_request(request): """Filter a request. Return False to stop the request. """
# plugin.py import curl @curl.hookimpl def curl_prepare_headers(headers): headers['X-Spam'] = 'eggs' @curl.hookimpl def curl_filter_request(request): return True
# curl.py import requests, pluggy, hookspec, importlib hookimpl = pluggy.HookimplMarker('curl') def main(url): pm = pluggy.PluginManager('curl') pm.add_hookspecs(hookspec) plugin = importlib.import_module('plugin') pm.register(plugin) session = requests.Session() request = requests.Request('GET', url) prepped = request.prepare() pm.hook.curl_prepare_headers(headers=prepped.headers) filters = pm.hook.curl_filter_request(request=prepped) if all(filters): response = session.send(prepped) print(response.text) else: print('E: request not allowed') if __name__ == '__main__': main('http://localhost:8000/curl.py')
Entire application composed of plugins
# curl.py import sys, pluggy, hookspec, core CORE_PLUGINS = [core] def main(argv): pm = pluggy.PluginManager('curl') pm.add_hookspecs(hookspec) for plugin in CORE_PLUGINS: pm.register(plugin) ret = pm.hook.curl_main(pluginmanager=pm, argv=argv) sys.exit(ret) if __name__ == '__main__': main(['http://localhost:8000/curl.py'])
# core.py import requests, pluggy hookimpl = pluggy.HookimplMarker('curl') @hookimpl def curl_main(pluginmanager, argv): pm = pluginmanager config = Config(argv, pm) pm.hook.curl_configure(config=config) cli_session = CliSession(config) pm.hook.curl_sessionstart(session=cli_session) pm.hook.curl_make_request(config=config, session=cli_session) pm.hook.curl_sessionfinish(session=cli_session) pm.hook.curl_unconfigure(config=config) return 0
# core.py class Config: def __init__(self, argv, pm): self.url = argv[0] self.pm = pm class CliSession: def __init__(self, config): self.config = config self.http_session = requests.Session() @hookimpl def curl_make_request(session, config): request = requests.Request('GET', config.url) prepped = request.prepare() config.pm.hook.curl_prepare_headers(headers=prepped.headers) response = session.http_session.send(prepped) print(response.text)
# core.py import argparse class Config: def __init__(self, argv, pm): self.pm = pm parser = argparse.ArgumentParser(prog='curl') parser.add_argument('--version', action='version', version='1.0') pm.hook.curl_addargument(parser=parser) self.args = parser.parse_args(argv) @hookimpl def curl_addargument(parser): parser.add_argument('url', action='store') @hookimpl def curl_make_request(session, config): request = requests.Request('GET', config.args.url) prepped = request.prepare() config.pm.hook.curl_prepare_headers(headers=prepped.headers) response = session.http_session.send(prepped) print(response.text)
main() +- PyTestPluginManager() +- Config() +- import+register default built-in plugins | +- pytest_plugin_registerd() +- pytest_namespace() +- pytest_addoption() +- pytest_cmdline_parse() 1:1 +- pytest_cmdline_main() 1:1 +- Session() +- pytest_configure() +- pytest_session_start() +- pytest_collection() 1:1 | +- pytest_collectreport() per item | +- pytest_collection_modifyitems() | +- pytest_collection_finish() +- pytest_runtestloop() | +- pytest_runtest_protocol() per item | +- pytest_runtest_logstart() | +- pytest_runtest_setup() | +- pytest_runtest_call() | +- pytest_runtest_teardown() +- pytest_sessionfinish() +- pytest_unconfigure()
Thanks for listening!
flub@devork.be
@flubdevork
And thanks to cobe.io for sponsoring my EuroPython attendance.