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.