diff --git a/src/poetry/console/commands/check.py b/src/poetry/console/commands/check.py index 570a7f4e768..81da5836442 100644 --- a/src/poetry/console/commands/check.py +++ b/src/poetry/console/commands/check.py @@ -1,6 +1,9 @@ from __future__ import annotations +from collections import defaultdict +from functools import reduce from typing import TYPE_CHECKING +from typing import Any from cleo.helpers import option @@ -88,6 +91,40 @@ 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""" + dependencies_source: dict[str, set[str]] = defaultdict(set) + all_sources = {k["name"] for k in config.get("source")} + errors = [] + + # scan root dependencies and group dependencies + dependencies_refs = [config.get("dependencies", {})] + [ + group.get("dependencies", {}) for group in config.get("group", {}).values() + ] + + for dependency_ref in dependencies_refs: + for dependency in dependency_ref: + if isinstance(dependency_ref[dependency], dict): + dependencies_source[dependency].add( + dependency_ref[dependency].get("source", None) + ) + + all_referenced_sources = reduce( + lambda i, j: i | j, dependencies_source.values() + ) + if all_referenced_sources not in all_sources: + errors.extend([ + f"Invalid source {source} referenced in dependencies" + for source in all_referenced_sources - all_sources + ]) + + if k := [k for k in dependencies_source if len(dependencies_source[k]) > 1]: + errors.extend([ + f"Dependency {k} referenced with multiple sources" for k in k + ]) + + return errors + def handle(self) -> int: from poetry.factory import Factory from poetry.pyproject.toml import PyProjectTOML @@ -108,6 +145,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."]