diff --git a/src/poetry/core/factory.py b/src/poetry/core/factory.py index 02095772f..070976a32 100644 --- a/src/poetry/core/factory.py +++ b/src/poetry/core/factory.py @@ -224,10 +224,10 @@ def _configure_package_metadata( package.readme_content = root / readme["text"] package.readme_content_type = readme["content-type"] elif custom_readme := tool_poetry.get("readme"): - if isinstance(custom_readme, str): - package.readmes = (root / custom_readme,) - else: - package.readmes = tuple(root / readme for readme in custom_readme) + custom_readmes = ( + (custom_readme,) if isinstance(custom_readme, str) else custom_readme + ) + package.readmes = tuple(root / r for r in custom_readmes if r) @classmethod def _configure_entry_points( diff --git a/src/poetry/core/masonry/metadata.py b/src/poetry/core/masonry/metadata.py index af723c2aa..a64c4df8b 100644 --- a/src/poetry/core/masonry/metadata.py +++ b/src/poetry/core/masonry/metadata.py @@ -59,8 +59,20 @@ def from_package(cls, package: ProjectPackage) -> Metadata: elif package.readmes: descriptions = [] for readme in package.readmes: - with readme.open(encoding="utf-8") as f: - descriptions.append(f.read()) + try: + descriptions.append(readme.read_text(encoding="utf-8")) + except FileNotFoundError as e: + raise FileNotFoundError( + f"Readme path `{readme}` does not exist." + ) from e + except IsADirectoryError as e: + raise IsADirectoryError( + f"Readme path `{readme}` is a directory." + ) from e + except PermissionError as e: + raise PermissionError( + f"Readme path `{readme}` is not readable." + ) from e meta.description = "\n".join(descriptions) meta.keywords = ",".join(package.keywords) diff --git a/tests/masonry/test_metadata.py b/tests/masonry/test_metadata.py new file mode 100644 index 000000000..d7515faa4 --- /dev/null +++ b/tests/masonry/test_metadata.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +from pathlib import Path +from typing import TYPE_CHECKING + +import pytest + +from poetry.core.masonry.metadata import Metadata +from poetry.core.packages.project_package import ProjectPackage + + +if TYPE_CHECKING: + from pytest_mock import MockerFixture + + +def test_from_package_readme(tmp_path: Path) -> None: + readme_path = tmp_path / "README.md" + readme_path.write_text("This is a description\néöß", encoding="utf-8") + + package = ProjectPackage("foo", "1.0") + package.readmes = (readme_path,) + + metadata = Metadata.from_package(package) + + assert metadata.description == "This is a description\néöß" + + +def test_from_package_multiple_readmes(tmp_path: Path) -> None: + readme_path1 = tmp_path / "README1.md" + readme_path1.write_text("Description 1", encoding="utf-8") + + readme_path2 = tmp_path / "README2.md" + readme_path2.write_text("Description 2", encoding="utf-8") + + package = ProjectPackage("foo", "1.0") + package.readmes = (readme_path1, readme_path2) + + metadata = Metadata.from_package(package) + + assert metadata.description == "Description 1\nDescription 2" + + +@pytest.mark.parametrize( + ("exception", "message"), + [ + (FileNotFoundError, "Readme path `MyReadme.md` does not exist."), + (IsADirectoryError, "Readme path `MyReadme.md` is a directory."), + (PermissionError, "Readme path `MyReadme.md` is not readable."), + ], +) +def test_from_package_readme_issues( + mocker: MockerFixture, exception: type[OSError], message: str +) -> None: + package = ProjectPackage("foo", "1.0") + package.readmes = (Path("MyReadme.md"),) + + mocker.patch("pathlib.Path.read_text", side_effect=exception) + + with pytest.raises(exception) as e: + Metadata.from_package(package) + + assert str(e.value) == message diff --git a/tests/test_factory.py b/tests/test_factory.py index e8bb2e30d..225a52fb3 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -510,6 +510,29 @@ def test_create_poetry_classifiers( assert poetry.package.all_classifiers == expected +def test_create_poetry_no_readme(tmp_path: Path) -> None: + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text( + '[tool.poetry]\nname="foo"\nversion="1"\nauthors = []\ndescription = ""\n', + encoding="utf-8", + ) + poetry = Factory().create_poetry(tmp_path) + + assert not poetry.package.readmes + + +def test_create_poetry_empty_readme(tmp_path: Path) -> None: + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text( + '[tool.poetry]\nname="foo"\nversion="1"\nauthors = []\ndescription = ""\n' + 'readme = ""\n', + encoding="utf-8", + ) + poetry = Factory().create_poetry(tmp_path) + + assert not poetry.package.readmes + + def test_validate() -> None: complete = fixtures_dir / "complete.toml" with complete.open("rb") as f: