1import re
2import textwrap
3import email.message
4
5from ._text import FoldedCase
6
7
8class Message(email.message.Message):
9 multiple_use_keys = set(
10 map(
11 FoldedCase,
12 [
13 'Classifier',
14 'Obsoletes-Dist',
15 'Platform',
16 'Project-URL',
17 'Provides-Dist',
18 'Provides-Extra',
19 'Requires-Dist',
20 'Requires-External',
21 'Supported-Platform',
22 'Dynamic',
23 ],
24 )
25 )
26 """
27 Keys that may be indicated multiple times per PEP 566.
28 """
29
30 def __new__(cls, orig: email.message.Message):
31 res = super().__new__(cls)
32 vars(res).update(vars(orig))
33 return res
34
35 def __init__(self, *args, **kwargs):
36 self._headers = self._repair_headers()
37
38 # suppress spurious error from mypy
39 def __iter__(self):
40 return super().__iter__()
41
42 def __getitem__(self, item):
43 """
44 Override parent behavior to typical dict behavior.
45
46 ``email.message.Message`` will emit None values for missing
47 keys. Typical mappings, including this ``Message``, will raise
48 a key error for missing keys.
49
50 Ref python/importlib_metadata#371.
51 """
52 res = super().__getitem__(item)
53 if res is None:
54 raise KeyError(item)
55 return res
56
57 def _repair_headers(self):
58 def redent(value):
59 "Correct for RFC822 indentation"
60 if not value or '\n' not in value:
61 return value
62 return textwrap.dedent(' ' * 8 + value)
63
64 headers = [(key, redent(value)) for key, value in vars(self)['_headers']]
65 if self._payload:
66 headers.append(('Description', self.get_payload()))
67 return headers
68
69 @property
70 def json(self):
71 """
72 Convert PackageMetadata to a JSON-compatible format
73 per PEP 0566.
74 """
75
76 def transform(key):
77 value = self.get_all(key) if key in self.multiple_use_keys else self[key]
78 if key == 'Keywords':
79 value = re.split(r'\s+', value)
80 tk = key.lower().replace('-', '_')
81 return tk, value
82
83 return dict(map(transform, map(FoldedCase, self)))