From 9ae0a6a57c99c2a87f0ea7dad6cea6b1a8535466 Mon Sep 17 00:00:00 2001 From: AryanRG Date: Sat, 17 Oct 2026 04:13:15 +0530 Subject: [PATCH] Fix inherited metadata field validation and serialization Use the core Metadata class as the field schema so that subclasses do not reject inherited fields or omit their headers when writing RFC822 metadata. Keep instance attribute access for validation and value retrieval. Cover both factories and both validation modes with subclass round trips. Assisted-by: OpenAI Codex --- src/packaging/metadata.py | 4 ++-- tests/test_metadata.py | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/packaging/metadata.py b/src/packaging/metadata.py index b94c2a2ea..1ffe415d1 100644 --- a/src/packaging/metadata.py +++ b/src/packaging/metadata.py @@ -860,7 +860,7 @@ def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> Metadata: # Can't use getattr() as that triggers descriptor protocol which # will fail due to no value for the instance argument. try: - field_metadata_version = cls.__dict__[key].added + field_metadata_version = Metadata.__dict__[key].added except KeyError: exc = InvalidMetadata(key, f"unrecognized field: {key!r}") collector.error(exc) @@ -1033,7 +1033,7 @@ def _write_metadata(self, message: RFC822Message) -> None: """ Return an RFC822 message with the metadata. """ - for name, validator in self.__class__.__dict__.items(): + for name, validator in Metadata.__dict__.items(): if isinstance(validator, _Validator) and name != "description": value = getattr(self, name) email_name = _RAW_TO_EMAIL_MAPPING[name] diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 728b0538c..3bb3941e3 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -318,6 +318,29 @@ def test_repr(self) -> None: class TestMetadata: + @pytest.mark.parametrize("validate", [False, True]) + @pytest.mark.parametrize("from_email", [False, True]) + def test_subclass_round_trip(self, validate: bool, from_email: bool) -> None: + class CustomMetadata(metadata.Metadata): + pass + + raw: metadata.RawMetadata = { + "metadata_version": "2.6", + "name": "example", + "version": "1.0", + "summary": "An example package", + "requires_dist": ["dependency>=2"], + "description": "Package description\n", + } + expected = metadata.Metadata.from_raw(raw).as_rfc822().as_bytes() + if from_email: + parsed = CustomMetadata.from_email(expected, validate=validate) + else: + parsed = CustomMetadata.from_raw(raw, validate=validate) + + assert isinstance(parsed, CustomMetadata) + assert parsed.as_rfc822().as_bytes() == expected + def _invalid_with_cause( self, meta: metadata.Metadata,