Skip to content

Commit a8df305

Browse files
committed
fix: bump pyproject-metadata to handle form feeds on Python < 3.12.8
Signed-off-by: Henry Schreiner <[email protected]>
1 parent d1aaf2d commit a8df305

File tree

1 file changed

+54
-15
lines changed
  • src/scikit_build_core/_vendor/pyproject_metadata

1 file changed

+54
-15
lines changed

Diff for: src/scikit_build_core/_vendor/pyproject_metadata/__init__.py

+54-15
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import os
4141
import os.path
4242
import pathlib
43-
import re
4443
import sys
4544
import typing
4645
import warnings
@@ -68,7 +67,14 @@
6867
import packaging.utils
6968
import packaging.version
7069

71-
__version__ = "0.9.0"
70+
if sys.version_info < (3, 12, 4):
71+
import re
72+
73+
RE_EOL_STR = re.compile(r"[\r\n]+")
74+
RE_EOL_BYTES = re.compile(rb"[\r\n]+")
75+
76+
77+
__version__ = "0.9.1"
7278

7379
__all__ = [
7480
"ConfigurationError",
@@ -77,10 +83,10 @@
7783
"RFC822Policy",
7884
"Readme",
7985
"StandardMetadata",
80-
"field_to_metadata",
8186
"extras_build_system",
8287
"extras_project",
8388
"extras_top_level",
89+
"field_to_metadata",
8490
]
8591

8692

@@ -187,6 +193,37 @@ def header_store_parse(self, name: str, value: str) -> tuple[str, str]:
187193
value = value.replace("\n", "\n" + " " * size)
188194
return (name, value)
189195

196+
if sys.version_info < (3, 12, 4):
197+
# Work around Python bug https://github.com/python/cpython/issues/117313
198+
def _fold(
199+
self, name: str, value: Any, refold_binary: bool = False
200+
) -> str: # pragma: no cover
201+
if hasattr(value, "name"):
202+
return value.fold(policy=self) # type: ignore[no-any-return]
203+
maxlen = self.max_line_length if self.max_line_length else sys.maxsize
204+
205+
# this is from the library version, and it improperly breaks on chars like 0x0c, treating
206+
# them as 'form feed' etc.
207+
# we need to ensure that only CR/LF is used as end of line
208+
# lines = value.splitlines()
209+
210+
# this is a workaround which splits only on CR/LF characters
211+
if isinstance(value, bytes):
212+
lines = RE_EOL_BYTES.split(value)
213+
else:
214+
lines = RE_EOL_STR.split(value)
215+
216+
refold = self.refold_source == "all" or (
217+
self.refold_source == "long"
218+
and (
219+
(lines and len(lines[0]) + len(name) + 2 > maxlen)
220+
or any(len(x) > maxlen for x in lines[1:])
221+
)
222+
)
223+
if refold or (refold_binary and email.policy._has_surrogates(value)): # type: ignore[attr-defined]
224+
return self.header_factory(name, "".join(lines)).fold(policy=self) # type: ignore[arg-type,no-any-return]
225+
return name + ": " + self.linesep.join(lines) + self.linesep # type: ignore[arg-type]
226+
190227

191228
class RFC822Message(email.message.EmailMessage):
192229
"""
@@ -474,11 +511,9 @@ def validate(self, *, warn: bool = True) -> None: # noqa: C901
474511
msg = "The metadata_version must be one of {versions} or None (default)"
475512
errors.config_error(msg, versions=constants.KNOWN_METADATA_VERSIONS)
476513

477-
# See https://packaging.python.org/en/latest/specifications/core-metadata/#name and
478-
# https://packaging.python.org/en/latest/specifications/name-normalization/#name-format
479-
if not re.match(
480-
r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", self.name, re.IGNORECASE
481-
):
514+
try:
515+
packaging.utils.canonicalize_name(self.name, validate=True)
516+
except packaging.utils.InvalidName:
482517
msg = (
483518
"Invalid project name {name!r}. A valid name consists only of ASCII letters and "
484519
"numbers, period, underscore and hyphen. It must start and end with a letter or number"
@@ -543,13 +578,15 @@ def _write_metadata( # noqa: C901
543578
"""
544579
Write the metadata to the message. Handles JSON or Message.
545580
"""
546-
self.validate(warn=False)
581+
errors = ErrorCollector(collect_errors=self.all_errors)
582+
with errors.collect():
583+
self.validate(warn=False)
547584

548585
smart_message["Metadata-Version"] = self.auto_metadata_version
549586
smart_message["Name"] = self.name
550587
if not self.version:
551-
msg = "Missing version field"
552-
raise ConfigurationError(msg)
588+
msg = "Field {key} missing"
589+
errors.config_error(msg, key="project.version")
553590
smart_message["Version"] = str(self.version)
554591
# skip 'Platform'
555592
# skip 'Supported-Platform'
@@ -604,13 +641,15 @@ def _write_metadata( # noqa: C901
604641
if self.auto_metadata_version != "2.1":
605642
for field in self.dynamic_metadata:
606643
if field.lower() in {"name", "version", "dynamic"}:
607-
msg = f"Field cannot be set as dynamic metadata: {field}"
608-
raise ConfigurationError(msg)
644+
msg = f"Metadata field {field!r} cannot be declared dynamic"
645+
errors.config_error(msg)
609646
if field.lower() not in constants.KNOWN_METADATA_FIELDS:
610-
msg = f"Field is not known: {field}"
611-
raise ConfigurationError(msg)
647+
msg = f"Unknown metadata field {field!r} cannot be declared dynamic"
648+
errors.config_error(msg)
612649
smart_message["Dynamic"] = field
613650

651+
errors.finalize("Failed to write metadata")
652+
614653

615654
def _name_list(people: list[tuple[str, str | None]]) -> str | None:
616655
"""

0 commit comments

Comments
 (0)