about summary refs log tree commit diff
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)))