Skip to content

Commit f7cebcd

Browse files
committed
chore: use 0.9.0b7
Signed-off-by: Henry Schreiner <[email protected]>
1 parent 92543ba commit f7cebcd

File tree

3 files changed

+290
-185
lines changed

3 files changed

+290
-185
lines changed

src/scikit_build_core/_vendor/pyproject_metadata/__init__.py

Lines changed: 150 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,32 @@
22

33
"""
44
This is pyproject_metadata, a library for working with PEP 621 metadata.
5+
6+
Example usage:
7+
8+
.. code-block:: python
9+
10+
from pyproject_metadata import StandardMetadata
11+
12+
metadata = StandardMetadata.from_pyproject(
13+
parsed_pyproject, allow_extra_keys=False, all_errors=True, metadata_version="2.3"
14+
)
15+
16+
pkg_info = metadata.as_rfc822()
17+
with open("METADATA", "wb") as f:
18+
f.write(pkg_info.as_bytes())
19+
20+
ep = self.metadata.entrypoints.copy()
21+
ep["console_scripts"] = self.metadata.scripts
22+
ep["gui_scripts"] = self.metadata.gui_scripts
23+
for group, entries in ep.items():
24+
if entries:
25+
with open("entry_points.txt", "w", encoding="utf-8") as f:
26+
print(f"[{group}]", file=f)
27+
for name, target in entries.items():
28+
print(f"{name} = {target}", file=f)
29+
print(file=f)
30+
531
"""
632

733
from __future__ import annotations
@@ -42,7 +68,7 @@
4268
import packaging.utils
4369
import packaging.version
4470

45-
__version__ = "0.9.0b6"
71+
__version__ = "0.9.0b7"
4672

4773
__all__ = [
4874
"ConfigurationError",
@@ -146,7 +172,7 @@ def set_payload(self, payload: str) -> None:
146172

147173
class RFC822Policy(email.policy.EmailPolicy):
148174
"""
149-
This is `email.policy.EmailPolicy`, but with a simple ``header_store_parse``
175+
This is :class:`email.policy.EmailPolicy`, but with a simple ``header_store_parse``
150176
implementation that handles multiline values, and some nice defaults.
151177
"""
152178

@@ -165,7 +191,7 @@ def header_store_parse(self, name: str, value: str) -> tuple[str, str]:
165191

166192
class RFC822Message(email.message.EmailMessage):
167193
"""
168-
This is `email.message.EmailMessage` with two small changes: it defaults to
194+
This is :class:`email.message.EmailMessage` with two small changes: it defaults to
169195
our `RFC822Policy`, and it correctly writes unicode when being called
170196
with `bytes()`.
171197
"""
@@ -184,6 +210,12 @@ def as_bytes(
184210

185211
@dataclasses.dataclass
186212
class StandardMetadata:
213+
"""
214+
This class represents the standard metadata fields for a project. It can be
215+
used to read metadata from a pyproject.toml table, validate it, and write it
216+
to an RFC822 message or JSON.
217+
"""
218+
187219
name: str
188220
version: packaging.version.Version | None = None
189221
description: str | None = None
@@ -207,14 +239,23 @@ class StandardMetadata:
207239
"""
208240
This field is used to track dynamic fields. You can't set a field not in this list.
209241
"""
242+
210243
dynamic_metadata: list[str] = dataclasses.field(default_factory=list)
211244
"""
212-
This is a list of METADATA fields that can change inbetween SDist and wheel. Requires metadata_version 2.2+.
245+
This is a list of METADATA fields that can change in between SDist and wheel. Requires metadata_version 2.2+.
213246
"""
214-
215247
metadata_version: str | None = None
248+
"""
249+
This is the target metadata version. If None, it will be computed as a minimum based on the fields set.
250+
"""
216251
all_errors: bool = False
252+
"""
253+
If True, all errors will be collected and raised in an ExceptionGroup.
254+
"""
217255
_locked_metadata: bool = False
256+
"""
257+
Internal flag to prevent setting non-dynamic fields after initialization.
258+
"""
218259

219260
def __post_init__(self) -> None:
220261
self.validate()
@@ -228,85 +269,6 @@ def __setattr__(self, name: str, value: Any) -> None:
228269
raise AttributeError(msg)
229270
super().__setattr__(name, value)
230271

231-
def validate(self, *, warn: bool = True) -> None: # noqa: C901
232-
"""
233-
Validate metadata for consistency and correctness. Will also produce warnings if
234-
``warn`` is given. Respects ``all_errors``. Checks:
235-
236-
- ``metadata_version`` is a known version or None
237-
- ``name`` is a valid project name
238-
- ``license_files`` can't be used with classic ``license``
239-
- License classifiers can't be used with SPDX license
240-
- ``description`` is a single line (warning)
241-
- ``license`` is not an SPDX license expression if metadata_version >= 2.4 (warning)
242-
- License classifiers deprecated for metadata_version >= 2.4 (warning)
243-
- ``license`` is an SPDX license expression if metadata_version >= 2.4
244-
- ``license_files`` is supported only for metadata_version >= 2.4
245-
"""
246-
errors = ErrorCollector(collect_errors=self.all_errors)
247-
248-
if self.auto_metadata_version not in constants.KNOWN_METADATA_VERSIONS:
249-
msg = f"The metadata_version must be one of {constants.KNOWN_METADATA_VERSIONS} or None (default)"
250-
errors.config_error(msg)
251-
252-
# See https://packaging.python.org/en/latest/specifications/core-metadata/#name and
253-
# https://packaging.python.org/en/latest/specifications/name-normalization/#name-format
254-
if not re.match(
255-
r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", self.name, re.IGNORECASE
256-
):
257-
msg = (
258-
f"Invalid project name {self.name!r}. A valid name consists only of ASCII letters and "
259-
"numbers, period, underscore and hyphen. It must start and end with a letter or number"
260-
)
261-
errors.config_error(msg, key="project.name")
262-
263-
if self.license_files is not None and isinstance(self.license, License):
264-
msg = "'project.license-files' must not be used when 'project.license' is not a SPDX license expression"
265-
errors.config_error(msg, key="project.license-files")
266-
267-
if isinstance(self.license, str) and any(
268-
c.startswith("License ::") for c in self.classifiers
269-
):
270-
msg = "Setting 'project.license' to an SPDX license expression is not compatible with 'License ::' classifiers"
271-
errors.config_error(msg, key="project.license")
272-
273-
if warn:
274-
if self.description and "\n" in self.description:
275-
warnings.warn(
276-
"The one-line summary 'project.description' should not contain more than one line. Readers might merge or truncate newlines.",
277-
ConfigurationWarning,
278-
stacklevel=2,
279-
)
280-
if self.auto_metadata_version not in constants.PRE_SPDX_METADATA_VERSIONS:
281-
if isinstance(self.license, License):
282-
warnings.warn(
283-
"Set 'project.license' to an SPDX license expression for metadata >= 2.4",
284-
ConfigurationWarning,
285-
stacklevel=2,
286-
)
287-
elif any(c.startswith("License ::") for c in self.classifiers):
288-
warnings.warn(
289-
"'License ::' classifiers are deprecated for metadata >= 2.4, use a SPDX license expression for 'project.license' instead",
290-
ConfigurationWarning,
291-
stacklevel=2,
292-
)
293-
294-
if (
295-
isinstance(self.license, str)
296-
and self.auto_metadata_version in constants.PRE_SPDX_METADATA_VERSIONS
297-
):
298-
msg = "Setting 'project.license' to an SPDX license expression is supported only when emitting metadata version >= 2.4"
299-
errors.config_error(msg, key="project.license")
300-
301-
if (
302-
self.license_files is not None
303-
and self.auto_metadata_version in constants.PRE_SPDX_METADATA_VERSIONS
304-
):
305-
msg = "'project.license-files' is supported only when emitting metadata version >= 2.4"
306-
errors.config_error(msg, key="project.license-files")
307-
308-
errors.finalize("Metadata validation failed")
309-
310272
@property
311273
def auto_metadata_version(self) -> str:
312274
"""
@@ -351,7 +313,7 @@ def from_pyproject( # noqa: C901
351313

352314
pyproject_table: PyProjectTable = data # type: ignore[assignment]
353315
if "project" not in pyproject_table:
354-
msg = "Section 'project' missing in pyproject.toml"
316+
msg = "Section {key} missing in pyproject.toml"
355317
pyproject.config_error(msg, key="project")
356318
pyproject.finalize("Failed to parse pyproject.toml")
357319
msg = "Unreachable code" # pragma: no cover
@@ -364,24 +326,26 @@ def from_pyproject( # noqa: C901
364326
extra_keys = extras_project(data)
365327
if extra_keys:
366328
extra_keys_str = ", ".join(sorted(f"{k!r}" for k in extra_keys))
367-
msg = f"Extra keys present in 'project': {extra_keys_str}"
368-
if allow_extra_keys is None:
369-
warnings.warn(msg, ConfigurationWarning, stacklevel=2)
370-
else:
371-
pyproject.config_error(msg)
329+
msg = "Extra keys present in {key}: {extra_keys}"
330+
pyproject.config_error(
331+
msg,
332+
key="project",
333+
extra_keys=extra_keys_str,
334+
warn=allow_extra_keys is None,
335+
)
372336

373337
dynamic = pyproject.get_dynamic(project)
374338

375339
for field in dynamic:
376340
if field in data["project"]:
377-
msg = f"Field 'project.{field}' declared as dynamic in 'project.dynamic' but is defined"
378-
pyproject.config_error(msg, key=field)
341+
msg = 'Field {key} declared as dynamic in "project.dynamic" but is defined'
342+
pyproject.config_error(msg, key=f"project.{field}")
379343

380344
raw_name = project.get("name")
381345
name = "UNKNOWN"
382346
if raw_name is None:
383-
msg = "Field 'project.name' missing"
384-
pyproject.config_error(msg, key="name")
347+
msg = "Field {key} missing"
348+
pyproject.config_error(msg, key="project.name")
385349
else:
386350
tmp_name = pyproject.ensure_str(raw_name, "project.name")
387351
if tmp_name is not None:
@@ -399,11 +363,15 @@ def from_pyproject( # noqa: C901
399363
else None
400364
)
401365
except packaging.version.InvalidVersion:
402-
msg = f"Invalid 'project.version' value, expecting a valid PEP 440 version (got {version_string!r})"
403-
pyproject.config_error(msg, key="project.version")
366+
msg = "Invalid {key} value, expecting a valid PEP 440 version"
367+
pyproject.config_error(
368+
msg, key="project.version", got=version_string
369+
)
404370
elif "version" not in dynamic:
405-
msg = "Field 'project.version' missing and 'version' not specified in 'project.dynamic'"
406-
pyproject.config_error(msg, key="version")
371+
msg = (
372+
"Field {key} missing and 'version' not specified in \"project.dynamic\""
373+
)
374+
pyproject.config_error(msg, key="project.version")
407375

408376
# Description fills Summary, which cannot be multiline
409377
# However, throwing an error isn't backward compatible,
@@ -427,8 +395,10 @@ def from_pyproject( # noqa: C901
427395
requires_python_string
428396
)
429397
except packaging.specifiers.InvalidSpecifier:
430-
msg = f"Invalid 'project.requires-python' value, expecting a valid specifier set (got {requires_python_string!r})"
431-
pyproject.config_error(msg, key="project.requires-python")
398+
msg = "Invalid {key} value, expecting a valid specifier set"
399+
pyproject.config_error(
400+
msg, key="project.requires-python", got=requires_python_string
401+
)
432402

433403
self = None
434404
with pyproject.collect():
@@ -496,6 +466,86 @@ def as_json(self) -> dict[str, str | list[str]]:
496466
self._write_metadata(smart_message)
497467
return message
498468

469+
def validate(self, *, warn: bool = True) -> None: # noqa: C901
470+
"""
471+
Validate metadata for consistency and correctness. Will also produce
472+
warnings if ``warn`` is given. Respects ``all_errors``. This is called
473+
when loading a pyproject.toml, and when making metadata. Checks:
474+
475+
- ``metadata_version`` is a known version or None
476+
- ``name`` is a valid project name
477+
- ``license_files`` can't be used with classic ``license``
478+
- License classifiers can't be used with SPDX license
479+
- ``description`` is a single line (warning)
480+
- ``license`` is not an SPDX license expression if metadata_version >= 2.4 (warning)
481+
- License classifiers deprecated for metadata_version >= 2.4 (warning)
482+
- ``license`` is an SPDX license expression if metadata_version >= 2.4
483+
- ``license_files`` is supported only for metadata_version >= 2.4
484+
"""
485+
errors = ErrorCollector(collect_errors=self.all_errors)
486+
487+
if self.auto_metadata_version not in constants.KNOWN_METADATA_VERSIONS:
488+
msg = "The metadata_version must be one of {versions} or None (default)"
489+
errors.config_error(msg, versions=constants.KNOWN_METADATA_VERSIONS)
490+
491+
# See https://packaging.python.org/en/latest/specifications/core-metadata/#name and
492+
# https://packaging.python.org/en/latest/specifications/name-normalization/#name-format
493+
if not re.match(
494+
r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", self.name, re.IGNORECASE
495+
):
496+
msg = (
497+
"Invalid project name {name!r}. A valid name consists only of ASCII letters and "
498+
"numbers, period, underscore and hyphen. It must start and end with a letter or number"
499+
)
500+
errors.config_error(msg, key="project.name", name=self.name)
501+
502+
if self.license_files is not None and isinstance(self.license, License):
503+
msg = '{key} must not be used when "project.license" is not a SPDX license expression'
504+
errors.config_error(msg, key="project.license-files")
505+
506+
if isinstance(self.license, str) and any(
507+
c.startswith("License ::") for c in self.classifiers
508+
):
509+
msg = "Setting {key} to an SPDX license expression is not compatible with 'License ::' classifiers"
510+
errors.config_error(msg, key="project.license")
511+
512+
if warn:
513+
if self.description and "\n" in self.description:
514+
warnings.warn(
515+
'The one-line summary "project.description" should not contain more than one line. Readers might merge or truncate newlines.',
516+
ConfigurationWarning,
517+
stacklevel=2,
518+
)
519+
if self.auto_metadata_version not in constants.PRE_SPDX_METADATA_VERSIONS:
520+
if isinstance(self.license, License):
521+
warnings.warn(
522+
'Set "project.license" to an SPDX license expression for metadata >= 2.4',
523+
ConfigurationWarning,
524+
stacklevel=2,
525+
)
526+
elif any(c.startswith("License ::") for c in self.classifiers):
527+
warnings.warn(
528+
"'License ::' classifiers are deprecated for metadata >= 2.4, use a SPDX license expression for \"project.license\" instead",
529+
ConfigurationWarning,
530+
stacklevel=2,
531+
)
532+
533+
if (
534+
isinstance(self.license, str)
535+
and self.auto_metadata_version in constants.PRE_SPDX_METADATA_VERSIONS
536+
):
537+
msg = "Setting {key} to an SPDX license expression is supported only when emitting metadata version >= 2.4"
538+
errors.config_error(msg, key="project.license")
539+
540+
if (
541+
self.license_files is not None
542+
and self.auto_metadata_version in constants.PRE_SPDX_METADATA_VERSIONS
543+
):
544+
msg = "{key} is supported only when emitting metadata version >= 2.4"
545+
errors.config_error(msg, key="project.license-files")
546+
547+
errors.finalize("Metadata validation failed")
548+
499549
def _write_metadata( # noqa: C901
500550
self, smart_message: _SmartMessageSetter | _JSonMessageSetter
501551
) -> None:

0 commit comments

Comments
 (0)