diff --git a/src/poetry/console/commands/check.py b/src/poetry/console/commands/check.py index 570a7f4e768..ac75e377d57 100644 --- a/src/poetry/console/commands/check.py +++ b/src/poetry/console/commands/check.py @@ -1,6 +1,7 @@ from __future__ import annotations from typing import TYPE_CHECKING +from typing import Any from cleo.helpers import option @@ -88,6 +89,37 @@ def _validate_readme(self, readme: str | list[str], poetry_file: Path) -> list[s errors.append(f"Declared README file does not exist: {name}") return errors + def _validate_dependencies_source(self, config: dict[str, Any]) -> list[str]: + """Check dependencies's source are valid""" + sources = {k["name"] for k in config.get("source", [])} + + dependency_declarations: list[ + dict[str, str | dict[str, str] | list[dict[str, str]]] + ] = [] + # scan dependencies and group dependencies settings in pyproject.toml + if "dependencies" in config: + dependency_declarations.append(config["dependencies"]) + + for group in config.get("group", {}).values(): + if "dependencies" in group: + dependency_declarations.append(group["dependencies"]) + + all_referenced_sources: set[str] = set() + + for dependency_declaration in dependency_declarations: + for declaration in dependency_declaration.values(): + if isinstance(declaration, list): + for item in declaration: + if "source" in item: + all_referenced_sources.add(item["source"]) + elif isinstance(declaration, dict) and "source" in declaration: + all_referenced_sources.add(declaration["source"]) + + return [ + f'Invalid source "{source}" referenced in dependencies.' + for source in sorted(all_referenced_sources - sources) + ] + def handle(self) -> int: from poetry.factory import Factory from poetry.pyproject.toml import PyProjectTOML @@ -108,6 +140,8 @@ def handle(self) -> int: errors = self._validate_readme(config["readme"], poetry_file) check_result["errors"].extend(errors) + check_result["errors"] += self._validate_dependencies_source(config) + # Verify that lock file is consistent if self.option("lock") and not self.poetry.locker.is_locked(): check_result["errors"] += ["poetry.lock was not found."] diff --git a/tests/console/commands/test_check.py b/tests/console/commands/test_check.py index 421c50dcb54..96836120450 100644 --- a/tests/console/commands/test_check.py +++ b/tests/console/commands/test_check.py @@ -83,6 +83,8 @@ def test_check_invalid( Error: Project name (invalid) is same as one of its dependencies Error: Unrecognized classifiers: ['Intended Audience :: Clowns']. Error: Declared README file does not exist: never/exists.md +Error: Invalid source "not-exists" referenced in dependencies. +Error: Invalid source "not-exists2" referenced in dependencies. Error: poetry.lock was not found. Warning: A wildcard Python dependency is ambiguous.\ Consider specifying a more explicit one. diff --git a/tests/fixtures/invalid_pyproject/pyproject.toml b/tests/fixtures/invalid_pyproject/pyproject.toml index bafa0936489..94c7d9fb4d5 100644 --- a/tests/fixtures/invalid_pyproject/pyproject.toml +++ b/tests/fixtures/invalid_pyproject/pyproject.toml @@ -17,3 +17,12 @@ classifiers = [ python = "*" pendulum = {"version" = "^2.0.5", allows-prereleases = true} invalid = "1.0" +invalid_source = { "version" = "*", source = "not-exists" } +invalid_source_multi = [ + { "version" = "*", platform = "linux", source = "exists" }, + { "version" = "*", platform = "win32", source = "not-exists2" }, +] + +[[tool.poetry.source]] +name = "exists" +priority = "explicit"