diff --git a/pyproject_metadata/project_table.py b/pyproject_metadata/project_table.py index 5701a4c..893027f 100644 --- a/pyproject_metadata/project_table.py +++ b/pyproject_metadata/project_table.py @@ -241,6 +241,9 @@ def _(prefix: str, data: object, error_collector: SimpleErrorCollector) -> None: if "content-type" not in data: msg = f'Field "{prefix}" is missing required key "content-type"' error_collector.error(ConfigurationError(msg, key=prefix)) + elif data["content-type"] == "": + msg = f'Field "{prefix}.content-type" must not be empty' + error_collector.error(ConfigurationError(msg, key=f"{prefix}.content-type")) @validate_via_prefix.register(r"project\.version") diff --git a/pyproject_metadata/pyproject.py b/pyproject_metadata/pyproject.py index bdbedbd..df77733 100644 --- a/pyproject_metadata/pyproject.py +++ b/pyproject_metadata/pyproject.py @@ -127,10 +127,11 @@ def get_license( filename = _license.get("file") text = _license.get("text") - if (filename and text) or (not filename and not text): + # Reported by the project.license validator + if (filename is None) == (text is None): return None - if filename: + if filename is not None: file = project_dir.joinpath(filename) if not file.is_file(): msg = "License file not found ({filename!r})" @@ -212,14 +213,15 @@ def get_readme( # noqa: C901 if text is None: return None - if (filename and text) or (not filename and not text): + # Reported by the project.readme validator + if (filename is None) == (text is None): return None if not content_type: return None else: return None - if filename: + if filename is not None: file = project_dir.joinpath(filename) if not file.is_file(): msg = "Readme file not found ({filename!r})" diff --git a/tests/test_standard_metadata.py b/tests/test_standard_metadata.py index 66bf62a..29851be 100644 --- a/tests/test_standard_metadata.py +++ b/tests/test_standard_metadata.py @@ -308,6 +308,16 @@ def all_errors(request: pytest.FixtureRequest, monkeypatch: pytest.MonkeyPatch) 'Field "project.readme" is missing required key "content-type"', id="Missing content-type for readme (text only)", ), + pytest.param( + """ + [project] + name = "test" + version = "0.1.0" + readme = { text = "...", content-type = "" } + """, + 'Field "project.readme.content-type" must not be empty', + id="Empty content-type for readme", + ), pytest.param( """ [project] @@ -1430,18 +1440,44 @@ def test_as_json(monkeypatch: pytest.MonkeyPatch) -> None: } -def test_readme_text() -> None: +@pytest.mark.parametrize("text", ["onetwothree", ""]) +def test_readme_text(text: str) -> None: pyproject = pyproject_metadata.StandardMetadata.from_pyproject( { "project": { "name": "foo", "version": "1.2.3", - "readme": {"text": "onetwothree", "content-type": "text/plain"}, + "readme": {"text": text, "content-type": "text/plain"}, } } ) assert pyproject.readme - assert pyproject.readme.text == "onetwothree" + assert pyproject.readme.text == text + assert pyproject.readme.content_type == "text/plain" + assert pyproject.as_rfc822()["Description-Content-Type"] == "text/plain" + assert pyproject.as_json()["description"] == text + + +def test_empty_license_text() -> None: + pyproject = pyproject_metadata.StandardMetadata.from_pyproject( + {"project": {"name": "x", "version": "1.0", "license": {"text": ""}}} + ) + assert pyproject.license == pyproject_metadata.License("", file=None) + assert pyproject.as_rfc822()["License"] == "" + + +@pytest.mark.parametrize( + ("field", "value"), + [ + ("readme", {"file": "", "content-type": "text/plain"}), + ("license", {"file": ""}), + ], +) +def test_empty_metadata_filename(field: str, value: dict[str, str]) -> None: + with pytest.raises(pyproject_metadata.ConfigurationError, match="file not found"): + pyproject_metadata.StandardMetadata.from_pyproject( + {"project": {"name": "x", "version": "1.0", field: value}} + ) def test_as_rfc822(monkeypatch: pytest.MonkeyPatch) -> None: