_entry_points.py 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import functools
  2. import itertools
  3. import operator
  4. from jaraco.functools import pass_none
  5. from jaraco.text import yield_lines
  6. from more_itertools import consume
  7. from ._importlib import metadata
  8. from ._itertools import ensure_unique
  9. from .errors import OptionError
  10. def ensure_valid(ep):
  11. """
  12. Exercise one of the dynamic properties to trigger
  13. the pattern match.
  14. """
  15. try:
  16. ep.extras
  17. except (AttributeError, AssertionError) as ex:
  18. # Why both? See https://github.com/python/importlib_metadata/issues/488
  19. msg = (
  20. f"Problems to parse {ep}.\nPlease ensure entry-point follows the spec: "
  21. "https://packaging.python.org/en/latest/specifications/entry-points/"
  22. )
  23. raise OptionError(msg) from ex
  24. def load_group(value, group):
  25. """
  26. Given a value of an entry point or series of entry points,
  27. return each as an EntryPoint.
  28. """
  29. # normalize to a single sequence of lines
  30. lines = yield_lines(value)
  31. text = f'[{group}]\n' + '\n'.join(lines)
  32. return metadata.EntryPoints._from_text(text)
  33. def by_group_and_name(ep):
  34. return ep.group, ep.name
  35. def validate(eps: metadata.EntryPoints):
  36. """
  37. Ensure entry points are unique by group and name and validate each.
  38. """
  39. consume(map(ensure_valid, ensure_unique(eps, key=by_group_and_name)))
  40. return eps
  41. @functools.singledispatch
  42. def load(eps):
  43. """
  44. Given a Distribution.entry_points, produce EntryPoints.
  45. """
  46. groups = itertools.chain.from_iterable(
  47. load_group(value, group) for group, value in eps.items()
  48. )
  49. return validate(metadata.EntryPoints(groups))
  50. @load.register(str)
  51. def _(eps):
  52. r"""
  53. >>> ep, = load('[console_scripts]\nfoo=bar')
  54. >>> ep.group
  55. 'console_scripts'
  56. >>> ep.name
  57. 'foo'
  58. >>> ep.value
  59. 'bar'
  60. """
  61. return validate(metadata.EntryPoints(metadata.EntryPoints._from_text(eps)))
  62. load.register(type(None), lambda x: x)
  63. @pass_none
  64. def render(eps: metadata.EntryPoints):
  65. by_group = operator.attrgetter('group')
  66. groups = itertools.groupby(sorted(eps, key=by_group), by_group)
  67. return '\n'.join(f'[{group}]\n{render_items(items)}\n' for group, items in groups)
  68. def render_items(eps):
  69. return '\n'.join(f'{ep.name} = {ep.value}' for ep in sorted(eps))