aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/importlib_metadata/_adapters.py
diff options
context:
space:
mode:
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.py135
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)))