Commit f6913202 by Y.D.X Committed by GitHub

`poetry check` command validates readme (files must exist) (#7444)

parent 6d57e843
...@@ -114,6 +114,12 @@ The file(s) can be of any format, but if you intend to publish to PyPI keep the ...@@ -114,6 +114,12 @@ The file(s) can be of any format, but if you intend to publish to PyPI keep the
https://packaging.python.org/en/latest/guides/making-a-pypi-friendly-readme/) in https://packaging.python.org/en/latest/guides/making-a-pypi-friendly-readme/) in
mind. README paths are implicitly relative to `pyproject.toml`. mind. README paths are implicitly relative to `pyproject.toml`.
{{% note %}}
Whether paths are case-sensitive follows platform defaults, but it is recommended to keep cases.
To be specific, you can set `readme = "rEaDmE.mD"` for `README.md` on macOS and Windows, but Linux users can't `poetry install` after cloning your repo. This is because macOS and Windows are case-insensitive and case-preserving.
{{% /note %}}
The contents of the README file(s) are used to populate the [Description The contents of the README file(s) are used to populate the [Description
field](https://packaging.python.org/en/latest/specifications/core-metadata/#description-optional) field](https://packaging.python.org/en/latest/specifications/core-metadata/#description-optional)
of your distribution's metadata (similar to `long_description` in setuptools). of your distribution's metadata (similar to `long_description` in setuptools).
......
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING
from cleo.helpers import option from cleo.helpers import option
from poetry.console.commands.command import Command from poetry.console.commands.command import Command
if TYPE_CHECKING:
from pathlib import Path
class CheckCommand(Command): class CheckCommand(Command):
name = "check" name = "check"
description = ( description = (
...@@ -23,7 +29,7 @@ class CheckCommand(Command): ...@@ -23,7 +29,7 @@ class CheckCommand(Command):
), ),
] ]
def validate_classifiers( def _validate_classifiers(
self, project_classifiers: set[str] self, project_classifiers: set[str]
) -> tuple[list[str], list[str]]: ) -> tuple[list[str], list[str]]:
"""Identify unrecognized and deprecated trove classifiers. """Identify unrecognized and deprecated trove classifiers.
...@@ -73,6 +79,17 @@ class CheckCommand(Command): ...@@ -73,6 +79,17 @@ class CheckCommand(Command):
return errors, warnings return errors, warnings
def _validate_readme(self, readme: str | list[str], poetry_file: Path) -> list[str]:
"""Check existence of referenced readme files"""
readmes = [readme] if isinstance(readme, str) else readme
errors = []
for name in readmes:
if not (poetry_file.parent / name).exists():
errors.append(f"Declared README file does not exist: {name}")
return errors
def handle(self) -> int: def handle(self) -> int:
from poetry.factory import Factory from poetry.factory import Factory
from poetry.pyproject.toml import PyProjectTOML from poetry.pyproject.toml import PyProjectTOML
...@@ -84,10 +101,15 @@ class CheckCommand(Command): ...@@ -84,10 +101,15 @@ class CheckCommand(Command):
# Validate trove classifiers # Validate trove classifiers
project_classifiers = set(config.get("classifiers", [])) project_classifiers = set(config.get("classifiers", []))
errors, warnings = self.validate_classifiers(project_classifiers) errors, warnings = self._validate_classifiers(project_classifiers)
check_result["errors"].extend(errors) check_result["errors"].extend(errors)
check_result["warnings"].extend(warnings) check_result["warnings"].extend(warnings)
# Validate readme (files must exist)
if "readme" in config:
errors = self._validate_readme(config["readme"], poetry_file)
check_result["errors"].extend(errors)
# Verify that lock file is consistent # Verify that lock file is consistent
if self.option("lock") and not self.poetry.locker.is_locked(): if self.option("lock") and not self.poetry.locker.is_locked():
check_result["errors"] += ["poetry.lock was not found."] check_result["errors"] += ["poetry.lock was not found."]
......
...@@ -81,6 +81,7 @@ def test_check_invalid( ...@@ -81,6 +81,7 @@ def test_check_invalid(
Error: 'description' is a required property Error: 'description' is a required property
Error: Project name (invalid) is same as one of its dependencies Error: Project name (invalid) is same as one of its dependencies
Error: Unrecognized classifiers: ['Intended Audience :: Clowns']. Error: Unrecognized classifiers: ['Intended Audience :: Clowns'].
Error: Declared README file does not exist: never/exists.md
Error: poetry.lock was not found. Error: poetry.lock was not found.
Warning: A wildcard Python dependency is ambiguous.\ Warning: A wildcard Python dependency is ambiguous.\
Consider specifying a more explicit one. Consider specifying a more explicit one.
......
...@@ -4,6 +4,7 @@ version = "1.0.0" ...@@ -4,6 +4,7 @@ version = "1.0.0"
authors = [ authors = [
"Foo <foo@bar.com>" "Foo <foo@bar.com>"
] ]
readme = "never/exists.md"
license = "INVALID" license = "INVALID"
classifiers = [ classifiers = [
"Environment :: Console", "Environment :: Console",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment