From bbf944c019366ff74447c15ac740052d91104daa Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Wed, 23 Apr 2025 15:43:28 -0400 Subject: [PATCH] feat: partially dynamic metadata proposal Signed-off-by: Henry Schreiner --- pyproject_metadata/__init__.py | 5 ++++- pyproject_metadata/constants.py | 17 +++++++++++++++++ tests/test_standard_metadata.py | 15 +++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/pyproject_metadata/__init__.py b/pyproject_metadata/__init__.py index ab31190..065e46d 100644 --- a/pyproject_metadata/__init__.py +++ b/pyproject_metadata/__init__.py @@ -360,7 +360,10 @@ def from_pyproject( # noqa: C901 dynamic = pyproject.get_dynamic(project) for field in dynamic: - if field in data["project"]: + if ( + field in data["project"] + and field not in constants.PROJECT_DYNAMIC_STATIC + ): msg = 'Field {key} declared as dynamic in "project.dynamic" but is defined' pyproject.config_error(msg, key=f"project.{field}") diff --git a/pyproject_metadata/constants.py b/pyproject_metadata/constants.py index afe4281..08554be 100644 --- a/pyproject_metadata/constants.py +++ b/pyproject_metadata/constants.py @@ -16,6 +16,7 @@ "KNOWN_PROJECT_FIELDS", "KNOWN_TOPLEVEL_FIELDS", "PRE_SPDX_METADATA_VERSIONS", + "PROJECT_DYNAMIC_STATIC", "PROJECT_TO_METADATA", ] @@ -48,6 +49,22 @@ def __dir__() -> list[str]: "version": frozenset(["Version"]), } +PROJECT_DYNAMIC_STATIC = { + "authors", + "classifiers", + "dependencies", + "keywords", + "license-files", + "maintainers", + "urls", + "entry-points", + "gui-scripts", + "optional-dependencies", + "scripts", + "license", +} + + KNOWN_TOPLEVEL_FIELDS = {"build-system", "project", "tool", "dependency-groups"} KNOWN_BUILD_SYSTEM_FIELDS = {"backend-path", "build-backend", "requires"} KNOWN_PROJECT_FIELDS = set(PROJECT_TO_METADATA) diff --git a/tests/test_standard_metadata.py b/tests/test_standard_metadata.py index 424ddb1..54deab9 100644 --- a/tests/test_standard_metadata.py +++ b/tests/test_standard_metadata.py @@ -1466,6 +1466,21 @@ def test_statically_defined_dynamic_field() -> None: ) +def test_dual_defined_dynamic_field() -> None: + metadata = pyproject_metadata.StandardMetadata.from_pyproject( + { + "project": { + "name": "example", + "version": "1.2.3", + "dependencies": ["example"], + "dynamic": ["dependencies"], + }, + } + ) + assert len(metadata.dependencies) == 1 + assert metadata.dynamic == ["dependencies"] + + @pytest.mark.parametrize( "value", [