Skip to content

Commit 2348482

Browse files
authored
chore: bump validate-pyproject to 0.9.0 (#265)
* chore: bump to 0.9.0rc1 Signed-off-by: Henry Schreiner <[email protected]> * chore: bump to final release Signed-off-by: Henry Schreiner <[email protected]> --------- Signed-off-by: Henry Schreiner <[email protected]>
1 parent 67fb709 commit 2348482

File tree

4 files changed

+53
-23
lines changed

4 files changed

+53
-23
lines changed

src/pdm/backend/_vendor/pyproject_metadata/__init__.py

+15-19
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
import pdm.backend._vendor.packaging.utils
7070
import pdm.backend._vendor.packaging.version
7171

72-
__version__ = "0.9.0b7"
72+
__version__ = "0.9.0"
7373

7474
__all__ = [
7575
"ConfigurationError",
@@ -127,7 +127,6 @@ class _SmartMessageSetter:
127127
reduce boilerplate.
128128
129129
If a value is None, do nothing.
130-
If a value contains a newline, indent it (may produce a warning in the future).
131130
"""
132131

133132
message: email.message.Message
@@ -253,23 +252,10 @@ class StandardMetadata:
253252
"""
254253
If True, all errors will be collected and raised in an ExceptionGroup.
255254
"""
256-
_locked_metadata: bool = False
257-
"""
258-
Internal flag to prevent setting non-dynamic fields after initialization.
259-
"""
260255

261256
def __post_init__(self) -> None:
262257
self.validate()
263258

264-
def __setattr__(self, name: str, value: Any) -> None:
265-
if self._locked_metadata:
266-
metadata_name = name.replace("_", "-")
267-
locked_fields = constants.KNOWN_METADATA_FIELDS - set(self.dynamic)
268-
if metadata_name in locked_fields:
269-
msg = f"Field {name!r} is not dynamic"
270-
raise AttributeError(msg)
271-
super().__setattr__(name, value)
272-
273259
@property
274260
def auto_metadata_version(self) -> str:
275261
"""
@@ -443,7 +429,6 @@ def from_pyproject( # noqa: C901
443429
metadata_version=metadata_version,
444430
all_errors=all_errors,
445431
)
446-
self._locked_metadata = True
447432

448433
pyproject.finalize("Failed to parse pyproject.toml")
449434
assert self is not None
@@ -482,6 +467,7 @@ def validate(self, *, warn: bool = True) -> None: # noqa: C901
482467
- License classifiers deprecated for metadata_version >= 2.4 (warning)
483468
- ``license`` is an SPDX license expression if metadata_version >= 2.4
484469
- ``license_files`` is supported only for metadata_version >= 2.4
470+
- ``project_url`` can't contain keys over 32 characters
485471
"""
486472
errors = ErrorCollector(collect_errors=self.all_errors)
487473

@@ -545,6 +531,11 @@ def validate(self, *, warn: bool = True) -> None: # noqa: C901
545531
msg = "{key} is supported only when emitting metadata version >= 2.4"
546532
errors.config_error(msg, key="project.license-files")
547533

534+
for name in self.urls:
535+
if len(name) > 32:
536+
msg = "{key} names cannot be more than 32 characters long"
537+
errors.config_error(msg, key="project.urls", got=name)
538+
548539
errors.finalize("Metadata validation failed")
549540

550541
def _write_metadata( # noqa: C901
@@ -566,8 +557,7 @@ def _write_metadata( # noqa: C901
566557
if self.description:
567558
smart_message["Summary"] = self.description
568559
smart_message["Keywords"] = ",".join(self.keywords) or None
569-
if "homepage" in self.urls:
570-
smart_message["Home-page"] = self.urls["homepage"]
560+
# skip 'Home-page'
571561
# skip 'Download-URL'
572562
smart_message["Author"] = _name_list(self.authors)
573563
smart_message["Author-Email"] = _email_list(self.authors)
@@ -582,14 +572,20 @@ def _write_metadata( # noqa: C901
582572
if self.license_files is not None:
583573
for license_file in sorted(set(self.license_files)):
584574
smart_message["License-File"] = os.fspath(license_file.as_posix())
575+
elif (
576+
self.auto_metadata_version not in constants.PRE_SPDX_METADATA_VERSIONS
577+
and isinstance(self.license, License)
578+
and self.license.file
579+
):
580+
smart_message["License-File"] = os.fspath(self.license.file.as_posix())
585581

586582
for classifier in self.classifiers:
587583
smart_message["Classifier"] = classifier
588584
# skip 'Provides-Dist'
589585
# skip 'Obsoletes-Dist'
590586
# skip 'Requires-External'
591587
for name, url in self.urls.items():
592-
smart_message["Project-URL"] = f"{name.capitalize()}, {url}"
588+
smart_message["Project-URL"] = f"{name}, {url}"
593589
if self.requires_python:
594590
smart_message["Requires-Python"] = str(self.requires_python)
595591
for dep in self.dependencies:

src/pdm/backend/_vendor/pyproject_metadata/constants.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def __dir__() -> list[str]:
4848
"version": frozenset(["Version"]),
4949
}
5050

51-
KNOWN_TOPLEVEL_FIELDS = {"build-system", "project", "tool"}
51+
KNOWN_TOPLEVEL_FIELDS = {"build-system", "project", "tool", "dependency-groups"}
5252
KNOWN_BUILD_SYSTEM_FIELDS = {"backend-path", "build-backend", "requires"}
5353
KNOWN_PROJECT_FIELDS = set(PROJECT_TO_METADATA)
5454

@@ -58,9 +58,9 @@ def __dir__() -> list[str]:
5858
"classifier",
5959
"description",
6060
"description-content-type",
61-
"download-url", # Not specified via pyproject standards
61+
"download-url", # Not specified via pyproject standards, deprecated by PEP 753
6262
"dynamic", # Can't be in dynamic
63-
"home-page", # Not specified via pyproject standards
63+
"home-page", # Not specified via pyproject standards, deprecated by PEP 753
6464
"keywords",
6565
"license",
6666
"license-expression",

src/pdm/backend/_vendor/pyproject_metadata/project_table.py

+34
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from __future__ import annotations
1212

1313
import sys
14+
import typing
1415
from typing import Any, Dict, List, Union
1516

1617
if sys.version_info < (3, 11):
@@ -28,6 +29,7 @@
2829
"BuildSystemTable",
2930
"ContactTable",
3031
"Dynamic",
32+
"IncludeGroupTable",
3133
"LicenseTable",
3234
"ProjectTable",
3335
"PyProjectTable",
@@ -107,12 +109,44 @@ class LicenseTable(TypedDict, total=False):
107109
total=False,
108110
)
109111

112+
# total=False here because this could be
113+
# extended in the future
114+
IncludeGroupTable = TypedDict(
115+
"IncludeGroupTable",
116+
{"include-group": str},
117+
total=False,
118+
)
119+
110120
PyProjectTable = TypedDict(
111121
"PyProjectTable",
112122
{
113123
"build-system": BuildSystemTable,
114124
"project": ProjectTable,
115125
"tool": Dict[str, Any],
126+
"dependency-groups": Dict[str, List[Union[str, IncludeGroupTable]]],
116127
},
117128
total=False,
118129
)
130+
131+
# Tests for type checking
132+
if typing.TYPE_CHECKING:
133+
PyProjectTable(
134+
{
135+
"build-system": BuildSystemTable(
136+
{"build-backend": "one", "requires": ["two"]}
137+
),
138+
"project": ProjectTable(
139+
{
140+
"name": "one",
141+
"version": "0.1.0",
142+
}
143+
),
144+
"tool": {"thing": object()},
145+
"dependency-groups": {
146+
"one": [
147+
"one",
148+
IncludeGroupTable({"include-group": "two"}),
149+
]
150+
},
151+
}
152+
)

src/pdm/backend/_vendor/vendor.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
packaging==24.1
22
tomli==2.0.1
33
tomli_w==1.0.0
4-
pyproject-metadata==0.9.0b7
4+
pyproject-metadata==0.9.0
55
editables==0.5

0 commit comments

Comments
 (0)