base.py 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. from __future__ import annotations
  2. import sys
  3. from collections import OrderedDict
  4. from importlib.metadata import entry_points
  5. importlib_metadata_version = ()
  6. class PluginLoader:
  7. _OPTIONS = None
  8. _ENTRY_POINTS = None
  9. @classmethod
  10. def entry_points_for(cls, key):
  11. if sys.version_info >= (3, 10) or importlib_metadata_version >= (3, 6):
  12. return OrderedDict((e.name, e.load()) for e in cls.entry_points().select(group=key))
  13. return OrderedDict((e.name, e.load()) for e in cls.entry_points().get(key, {}))
  14. @staticmethod
  15. def entry_points():
  16. if PluginLoader._ENTRY_POINTS is None:
  17. PluginLoader._ENTRY_POINTS = entry_points()
  18. return PluginLoader._ENTRY_POINTS
  19. class ComponentBuilder(PluginLoader):
  20. def __init__(self, interpreter, parser, name, possible) -> None:
  21. self.interpreter = interpreter
  22. self.name = name
  23. self._impl_class = None
  24. self.possible = possible
  25. self.parser = parser.add_argument_group(title=name)
  26. self.add_selector_arg_parse(name, list(self.possible))
  27. @classmethod
  28. def options(cls, key):
  29. if cls._OPTIONS is None:
  30. cls._OPTIONS = cls.entry_points_for(key)
  31. return cls._OPTIONS
  32. def add_selector_arg_parse(self, name, choices):
  33. raise NotImplementedError
  34. def handle_selected_arg_parse(self, options):
  35. selected = getattr(options, self.name)
  36. if selected not in self.possible:
  37. msg = f"No implementation for {self.interpreter}"
  38. raise RuntimeError(msg)
  39. self._impl_class = self.possible[selected]
  40. self.populate_selected_argparse(selected, options.app_data)
  41. return selected
  42. def populate_selected_argparse(self, selected, app_data):
  43. self.parser.description = f"options for {self.name} {selected}"
  44. self._impl_class.add_parser_arguments(self.parser, self.interpreter, app_data)
  45. def create(self, options):
  46. return self._impl_class(options, self.interpreter)
  47. __all__ = [
  48. "ComponentBuilder",
  49. "PluginLoader",
  50. ]