_entry_points.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  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. This function is deprecated in favor of importlib_metadata 8.7 and
  15. Python 3.14 importlib.metadata, which validates entry points on
  16. construction.
  17. """
  18. try:
  19. ep.extras
  20. except (AttributeError, AssertionError) as ex:
  21. # Why both? See https://github.com/python/importlib_metadata/issues/488
  22. msg = (
  23. f"Problems to parse {ep}.\nPlease ensure entry-point follows the spec: "
  24. "https://packaging.python.org/en/latest/specifications/entry-points/"
  25. )
  26. raise OptionError(msg) from ex
  27. def load_group(value, group):
  28. """
  29. Given a value of an entry point or series of entry points,
  30. return each as an EntryPoint.
  31. """
  32. # normalize to a single sequence of lines
  33. lines = yield_lines(value)
  34. text = f'[{group}]\n' + '\n'.join(lines)
  35. return metadata.EntryPoints._from_text(text)
  36. def by_group_and_name(ep):
  37. return ep.group, ep.name
  38. def validate(eps: metadata.EntryPoints):
  39. """
  40. Ensure entry points are unique by group and name and validate each.
  41. """
  42. consume(map(ensure_valid, ensure_unique(eps, key=by_group_and_name)))
  43. return eps
  44. @functools.singledispatch
  45. def load(eps):
  46. """
  47. Given a Distribution.entry_points, produce EntryPoints.
  48. """
  49. groups = itertools.chain.from_iterable(
  50. load_group(value, group) for group, value in eps.items()
  51. )
  52. return validate(metadata.EntryPoints(groups))
  53. @load.register(str)
  54. def _(eps):
  55. r"""
  56. >>> ep, = load('[console_scripts]\nfoo=bar')
  57. >>> ep.group
  58. 'console_scripts'
  59. >>> ep.name
  60. 'foo'
  61. >>> ep.value
  62. 'bar'
  63. """
  64. return validate(metadata.EntryPoints(metadata.EntryPoints._from_text(eps)))
  65. load.register(type(None), lambda x: x)
  66. @pass_none
  67. def render(eps: metadata.EntryPoints):
  68. by_group = operator.attrgetter('group')
  69. groups = itertools.groupby(sorted(eps, key=by_group), by_group)
  70. return '\n'.join(f'[{group}]\n{render_items(items)}\n' for group, items in groups)
  71. def render_items(eps):
  72. return '\n'.join(f'{ep.name} = {ep.value}' for ep in sorted(eps))