_entry_points.py 2.2 KB

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