pygin.py 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. import functools
  2. import importlib
  3. from collections import namedtuple
  4. from importlib import resources
  5. from typing import Type, Any, List
  6. # Basic structure for storing information about one plugin
  7. Plugin = namedtuple("Plugin", ("name", "func"))
  8. # Dictionary with information about all registered plugins
  9. _PLUGINS = {}
  10. # From https://realpython.com/python-import/#example-a-package-of-plugins
  11. def register(func):
  12. """Decorator for registering a new plugin"""
  13. package, _, plugin = func.__module__.rpartition(".")
  14. pkg_info = _PLUGINS.setdefault(package, {})
  15. pkg_info[plugin] = Plugin(name=plugin, func=func)
  16. return func
  17. def names(package: str) -> List[str]:
  18. """List all plugins in one package"""
  19. _import_all(package)
  20. return sorted(_PLUGINS[package])
  21. def get(package: str, plugin: str) -> Type[Any]:
  22. """Get a given plugin"""
  23. _import(package, plugin)
  24. return _PLUGINS[package][plugin].func
  25. def call(package: str, plugin: str, *args, **kwargs) -> Any:
  26. """Call the given plugin"""
  27. plugin_func = get(package, plugin)
  28. return plugin_func(*args, **kwargs)
  29. def _import(package: str, plugin: str) -> None:
  30. """Import the given plugin file from a package"""
  31. importlib.import_module(f"{package}.{plugin}")
  32. def _import_all(package: str) -> None:
  33. """Import all plugins in a package"""
  34. files = resources.contents(package)
  35. plugins = [f[:-3] for f in files if f.endswith(".py") and f[0] != "_"]
  36. for plugin in plugins:
  37. _import(package, plugin)
  38. def names_factory(package: str):
  39. """Create a names() function for one package"""
  40. return functools.partial(names, package)
  41. def get_factory(package: str):
  42. """Create a get() function for one package"""
  43. return functools.partial(get, package)
  44. def call_factory(package: str):
  45. """Create a call() function for one package"""
  46. return functools.partial(call, package)