From 100cee88ee5e88892e32e09e093008c5499be0e3 Mon Sep 17 00:00:00 2001 From: Bilel Omrani Date: Mon, 29 Dec 2025 23:22:15 +0100 Subject: [PATCH] Allow metadata hooks to run when project.dynamic is empty Fixes #2153 --- backend/src/hatchling/metadata/core.py | 29 +++++++++++++------------- tests/backend/metadata/test_core.py | 28 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/backend/src/hatchling/metadata/core.py b/backend/src/hatchling/metadata/core.py index 063a3d027..9e67d7cd7 100644 --- a/backend/src/hatchling/metadata/core.py +++ b/backend/src/hatchling/metadata/core.py @@ -191,21 +191,20 @@ def core(self) -> CoreMetadata: self._version = self._get_version(metadata) self.core_raw_metadata["version"] = self.version - if metadata.dynamic: - for metadata_hook in metadata_hooks.values(): - metadata_hook.update(self.core_raw_metadata) - metadata.add_known_classifiers(metadata_hook.get_known_classifiers()) - - new_fields = set(self.core_raw_metadata) - static_fields - for new_field in new_fields: - if new_field in metadata.dynamic: - metadata.dynamic.remove(new_field) - else: - message = ( - f"The field `{new_field}` was set dynamically and therefore must be " - f"listed in `project.dynamic`" - ) - raise ValueError(message) + for metadata_hook in metadata_hooks.values(): + metadata_hook.update(self.core_raw_metadata) + metadata.add_known_classifiers(metadata_hook.get_known_classifiers()) + + new_fields = set(self.core_raw_metadata) - static_fields + for new_field in new_fields: + if new_field in metadata.dynamic: + metadata.dynamic.remove(new_field) + else: + message = ( + f"The field `{new_field}` was set dynamically and therefore must be " + f"listed in `project.dynamic`" + ) + raise ValueError(message) self._core = metadata diff --git a/tests/backend/metadata/test_core.py b/tests/backend/metadata/test_core.py index a51fe3cb8..86d08adc3 100644 --- a/tests/backend/metadata/test_core.py +++ b/tests/backend/metadata/test_core.py @@ -1497,6 +1497,34 @@ def update(self, metadata): ): _ = metadata.core + def test_custom_no_dynamic(self, temp_dir, helpers): + metadata = ProjectMetadata( + str(temp_dir), + PluginManager(), + { + "project": {"name": "foo", "version": "1.0.0", "dependencies": ["bar==1.0"]}, + "tool": {"hatch": {"metadata": {"hooks": {"custom": {}}}}}, + }, + ) + + file_path = temp_dir / DEFAULT_BUILD_SCRIPT + file_path.write_text( + helpers.dedent( + """ + from hatchling.metadata.plugin.interface import MetadataHookInterface + + class CustomHook(MetadataHookInterface): + def update(self, metadata): + metadata['dependencies'] = ['bar==2.0'] + """ + ) + ) + + assert "custom" in metadata.hatch.metadata.hooks + assert metadata.core.name == "foo" + assert metadata.core.version == "1.0.0" + assert metadata.core.dependencies == ["bar==2.0"] + class TestHatchPersonalProjectConfigFile: def test_correct(self, temp_dir, helpers):