mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 03:04:38 +08:00
* Refresh canonical importlib and preserve native import callback Cache the original builtin __import__ callback so CPython 3.15 importlib's hierarchical locking uses the existing native parent-initialization path. Refresh the complete canonical importlib and related test/helper families from CPython d220409a13b77de7a6c30abe16ea90db01528edc through update_lib. Preserve both nondecorator importlib guards in the updater. Add explicit hash-pinned removal of reviewed obsolete canonical files, with full preflight, dry-run, idempotence and refusal tests. Preserve empty fixtures. Assisted-by: OpenAI Codex:model version unavailable * Complete canonical inspect and types closure for importlib metadata Refresh whole canonical inspect/types production and related test families through update_lib after hosted CI exposed stale __cached__ consumers. Add coroutine and async-generator state getters, with closed-state priority for reentrant frame-local finalizers, and expose the existing frame-locals proxy type with its CPython class documentation. Preserve the optional _datetime and unittest import blocks through a narrow migration adapter. Retain canonical assertions and existing guards; record the missing LazyImportType expectation before adding a conditional expected failure, and capability-skip only the two absent-_testcapi CLI fixtures. Assisted-by: OpenAI Codex:model version unavailable * Reject canonical removal junctions and read UTF-8 manifests Reject junctions alongside symlinks in the existing path-component preflight and revalidation. Decode removal manifests explicitly as UTF-8. Add regressions for whole-batch refusal, revalidation, and Unicode dry-run/apply/idempotence. Assisted-by: OpenAI Codex:model version unavailable
137 lines
3.7 KiB
Python
Vendored
137 lines
3.7 KiB
Python
Vendored
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)))
|