From 7a57db5f08e95c982f66bbb54d788ec076408686 Mon Sep 17 00:00:00 2001 From: ralbertazzi Date: Sat, 20 May 2023 14:50:24 +0200 Subject: [PATCH] fix: check for mutually exclusive markers --- src/poetry/factory.py | 41 +++++++++++++++++-- tests/console/commands/test_check.py | 3 +- .../fixtures/invalid_pyproject/pyproject.toml | 18 ++++---- tests/test_factory.py | 3 +- 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/src/poetry/factory.py b/src/poetry/factory.py index 5e926b6177a..8a5c8ee8397 100644 --- a/src/poetry/factory.py +++ b/src/poetry/factory.py @@ -4,6 +4,7 @@ import logging import re +from itertools import combinations from typing import TYPE_CHECKING from typing import Any from typing import cast @@ -358,7 +359,15 @@ def validate( results["errors"].extend(validate_object(config)) - # A project should not depend on itself. + cls._validate_project_should_not_depend_on_itself(config, results) + cls._validate_dependencies_have_mutually_exclusive_markers(config, results) + + return results + + @staticmethod + def _validate_project_should_not_depend_on_itself( + config: dict[str, Any], results: dict[str, list[str]] + ) -> None: dependencies = set(config.get("dependencies", {}).keys()) dependencies.update(config.get("dev-dependencies", {}).keys()) groups = config.get("group", {}).values() @@ -369,7 +378,33 @@ def validate( if canonicalize_name(config["name"]) in dependencies: results["errors"].append( - f"Project name ({config['name']}) is same as one of its dependencies" + f"Project name ({config['name']}) is same as one of its dependencies." ) - return results + @classmethod + def _validate_dependencies_have_mutually_exclusive_markers( + cls, config: dict[str, Any], results: dict[str, list[str]] + ) -> None: + dep_groups = [ + config.get("dependencies", {}), + config.get("dev-dependencies", {}), + *( + group.get("dependencies", {}) + for group in config.get("group", {}).values() + ), + ] + for group in dep_groups: + for name, constraints in group.items(): + if isinstance(constraints, list) and len(constraints) > 1: + deps = [ + cls.create_dependency(name, constraint) + for constraint in constraints + ] + for d1, d2 in combinations(deps, 2): + if not d1.marker.intersect(d2.marker).is_empty(): + error = ( + f"Dependency {name} does not have mutually exclusive" + " markers." + ) + if error not in results["errors"]: + results["errors"].append(error) diff --git a/tests/console/commands/test_check.py b/tests/console/commands/test_check.py index 323364c8def..baa2bc0cb8f 100644 --- a/tests/console/commands/test_check.py +++ b/tests/console/commands/test_check.py @@ -43,7 +43,8 @@ def test_check_invalid( expected = """\ 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: Dependency pillow does not have mutually exclusive markers. Error: Unrecognized classifiers: ['Intended Audience :: Clowns']. 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 55b0b6282af..289f5f98ed2 100644 --- a/tests/fixtures/invalid_pyproject/pyproject.toml +++ b/tests/fixtures/invalid_pyproject/pyproject.toml @@ -1,18 +1,20 @@ [tool.poetry] -name = "invalid" -version = "1.0.0" -authors = [ - "Foo " -] -license = "INVALID" +authors = ["Foo "] classifiers = [ "Environment :: Console", "Intended Audience :: Clowns", "Natural Language :: Ukranian", "Topic :: Communications :: Chat :: AOL Instant Messenger", ] +license = "INVALID" +name = "invalid" +version = "1.0.0" [tool.poetry.dependencies] -python = "*" -pendulum = {"version" = "^2.0.5", allows-prereleases = true} invalid = "1.0" +pendulum = { "version" = "^2.0.5", allows-prereleases = true } +pillow = [ + { version = "^9", python = "^3.9" }, + { version = "^7", python = "^3.7" }, +] +python = "*" diff --git a/tests/test_factory.py b/tests/test_factory.py index b57030709ea..391b171652e 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -518,7 +518,8 @@ def test_create_poetry_fails_on_invalid_configuration( expected = """\ The Poetry configuration is invalid: - 'description' is a required property - - Project name (invalid) is same as one of its dependencies + - Project name (invalid) is same as one of its dependencies. + - Dependency pillow does not have mutually exclusive markers. """ assert str(e.value) == expected