diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/importlib_metadata/_adapters.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/importlib_metadata/_adapters.py | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/importlib_metadata/_adapters.py b/.venv/lib/python3.12/site-packages/importlib_metadata/_adapters.py new file mode 100644 index 00000000..f5b30dd9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/importlib_metadata/_adapters.py @@ -0,0 +1,135 @@ +import email.message +import email.policy +import re +import textwrap + +from ._text import FoldedCase + + +class RawPolicy(email.policy.EmailPolicy): + def fold(self, name, value): + folded = self.linesep.join( + textwrap.indent(value, prefix=' ' * 8, predicate=lambda line: True) + .lstrip() + .splitlines() + ) + return f'{name}: {folded}{self.linesep}' + + +class Message(email.message.Message): + r""" + Specialized Message subclass to handle metadata naturally. + + Reads values that may have newlines in them and converts the + payload to the Description. + + >>> msg_text = textwrap.dedent(''' + ... Name: Foo + ... Version: 3.0 + ... License: blah + ... de-blah + ... <BLANKLINE> + ... First line of description. + ... Second line of description. + ... <BLANKLINE> + ... Fourth line! + ... ''').lstrip().replace('<BLANKLINE>', '') + >>> msg = Message(email.message_from_string(msg_text)) + >>> msg['Description'] + 'First line of description.\nSecond line of description.\n\nFourth line!\n' + + Message should render even if values contain newlines. + + >>> print(msg) + Name: Foo + Version: 3.0 + License: blah + de-blah + Description: First line of description. + Second line of description. + <BLANKLINE> + Fourth line! + <BLANKLINE> + <BLANKLINE> + """ + + multiple_use_keys = set( + map( + FoldedCase, + [ + 'Classifier', + 'Obsoletes-Dist', + 'Platform', + 'Project-URL', + 'Provides-Dist', + 'Provides-Extra', + 'Requires-Dist', + 'Requires-External', + 'Supported-Platform', + 'Dynamic', + ], + ) + ) + """ + Keys that may be indicated multiple times per PEP 566. + """ + + def __new__(cls, orig: email.message.Message): + res = super().__new__(cls) + vars(res).update(vars(orig)) + return res + + def __init__(self, *args, **kwargs): + self._headers = self._repair_headers() + + # suppress spurious error from mypy + def __iter__(self): + return super().__iter__() + + def __getitem__(self, item): + """ + Override parent behavior to typical dict behavior. + + ``email.message.Message`` will emit None values for missing + keys. Typical mappings, including this ``Message``, will raise + a key error for missing keys. + + Ref python/importlib_metadata#371. + """ + res = super().__getitem__(item) + if res is None: + raise KeyError(item) + return res + + def _repair_headers(self): + def redent(value): + "Correct for RFC822 indentation" + indent = ' ' * 8 + if not value or '\n' + indent not in value: + return value + return textwrap.dedent(indent + value) + + headers = [(key, redent(value)) for key, value in vars(self)['_headers']] + if self._payload: + headers.append(('Description', self.get_payload())) + self.set_payload('') + return headers + + def as_string(self): + return super().as_string(policy=RawPolicy()) + + @property + def json(self): + """ + Convert PackageMetadata to a JSON-compatible format + per PEP 0566. + """ + + def transform(key): + value = self.get_all(key) if key in self.multiple_use_keys else self[key] + if key == 'Keywords': + value = re.split(r'\s+', value) + tk = key.lower().replace('-', '_') + return tk, value + + return dict(map(transform, map(FoldedCase, self))) |