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