diff --git a/packaging/metadata.py b/packaging/metadata.py index f78a9ec75..97b8121d9 100644 --- a/packaging/metadata.py +++ b/packaging/metadata.py @@ -82,7 +82,12 @@ def _normalize_field_name_for_dynamic(field: str) -> NormalizedDynamicFields: _setattr = object.__setattr__ -@dataclasses.dataclass(frozen=True) +# In the following we use `frozen` to prevent inconsistencies, specially with `dynamic`. +# Comparison is disabled because currently `Requirement` objects are +# unhashable/not-comparable. + + +@dataclasses.dataclass(frozen=True, eq=False) class CoreMetadata: """ Core metadata for Python packages, represented as an immutable diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 3b6a6efb5..86f2765c6 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -57,7 +57,7 @@ def test_replace(self): } metadata1 = dataclasses.replace(metadata, **attrs) req = next(iter(metadata1.requires_dist)) - assert req == Requirement("appdirs>1.4") + assert str(req) == "appdirs>1.4" with pytest.raises(InvalidCoreMetadataField): dataclasses.replace(metadata, dynamic=["myfield"]) @@ -216,7 +216,7 @@ def test_parsing(self, spec: str) -> None: pkg_info = CoreMetadata.from_pkg_info(text) if example["is_final_metadata"]: metadata = CoreMetadata.from_dist_info_metadata(text) - assert metadata == pkg_info + assert_equal_metadata(metadata, pkg_info) if example["has_dynamic_fields"]: with pytest.raises(DynamicNotAllowed): CoreMetadata.from_dist_info_metadata(text) @@ -309,7 +309,7 @@ def test_parse(self, pkg: str, version: str) -> None: recons_data = from_(recons_file) description = metadata.description.replace("\r\n", "\n") metadata = dataclasses.replace(metadata, description=description) - assert metadata == recons_data + assert_equal_metadata(metadata, recons_data) # - Make sure the reconstructed file can be parsed with compat32 attrs = dataclasses.asdict(_Compat32Metadata.from_pkg_info(recons_file)) assert CoreMetadata(**attrs) @@ -321,13 +321,27 @@ def test_parse(self, pkg: str, version: str) -> None: result_contents = to_(data) assert file_contents == result_contents result_data = from_(result_contents) - assert data == result_data + assert_equal_metadata(data, result_data) file_contents, data = result_contents, result_data # --- Helper Functions/Classes --- +def assert_equal_metadata(metadata1: CoreMetadata, metadata2: CoreMetadata): + fields = (f.name for f in dataclasses.fields(CoreMetadata)) + for field in fields: + value1, value2 = getattr(metadata1, field), getattr(metadata2, field) + if field.endswith("dist"): + # Currently `Requirement` objects are not directly comparable, + # therefore sets containing those objects are also not comparable. + # The best approach is to convert requirements to strings first. + req1, req2 = set(map(str, value1)), set(map(str, value2)) + assert req1 == req2 + else: + assert value1 == value2 + + class _Compat32Metadata(CoreMetadata): """The Core Metadata spec requires the file to be parse-able with compat32. The implementation uses a different approach to ensure UTF-8 can be used.