1import functools 2import operator 3import itertools 4 5from .extern.jaraco.text import yield_lines 6from .extern.jaraco.functools import pass_none 7from ._importlib import metadata 8from ._itertools import ensure_unique 9from .extern.more_itertools import consume 10 11 12def ensure_valid(ep): 13 """ 14 Exercise one of the dynamic properties to trigger 15 the pattern match. 16 """ 17 ep.extras 18 19 20def load_group(value, group): 21 """ 22 Given a value of an entry point or series of entry points, 23 return each as an EntryPoint. 24 """ 25 # normalize to a single sequence of lines 26 lines = yield_lines(value) 27 text = f'[{group}]\n' + '\n'.join(lines) 28 return metadata.EntryPoints._from_text(text) 29 30 31def by_group_and_name(ep): 32 return ep.group, ep.name 33 34 35def 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 42 43@functools.singledispatch 44def load(eps): 45 """ 46 Given a Distribution.entry_points, produce EntryPoints. 47 """ 48 groups = itertools.chain.from_iterable( 49 load_group(value, group) 50 for group, value in eps.items()) 51 return validate(metadata.EntryPoints(groups)) 52 53 54@load.register(str) 55def _(eps): 56 r""" 57 >>> ep, = load('[console_scripts]\nfoo=bar') 58 >>> ep.group 59 'console_scripts' 60 >>> ep.name 61 'foo' 62 >>> ep.value 63 'bar' 64 """ 65 return validate(metadata.EntryPoints(metadata.EntryPoints._from_text(eps))) 66 67 68load.register(type(None), lambda x: x) 69 70 71@pass_none 72def render(eps: metadata.EntryPoints): 73 by_group = operator.attrgetter('group') 74 groups = itertools.groupby(sorted(eps, key=by_group), by_group) 75 76 return '\n'.join( 77 f'[{group}]\n{render_items(items)}\n' 78 for group, items in groups 79 ) 80 81 82def render_items(eps): 83 return '\n'.join( 84 f'{ep.name} = {ep.value}' 85 for ep in sorted(eps) 86 ) 87