From d54432288eb091ecfc4eab6c7c90d29149975706 Mon Sep 17 00:00:00 2001 From: layday Date: Fri, 2 Sep 2022 23:01:39 +0300 Subject: [PATCH] build: add circular dependency protection Fixes #511. --- src/build/__init__.py | 13 ++++++++++--- tests/test_projectbuilder.py | 10 +++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/build/__init__.py b/src/build/__init__.py index ea534b3d8..80f19a121 100644 --- a/src/build/__init__.py +++ b/src/build/__init__.py @@ -176,6 +176,11 @@ def check_dependency( import importlib_metadata req = packaging.requirements.Requirement(req_string) + normalised_req_string = str(req) + + if normalised_req_string in ancestral_req_strings: + # cyclical dependency, already checked. + return if req.marker: extras = frozenset(('',)).union(parent_extras) @@ -190,15 +195,17 @@ def check_dependency( dist = importlib_metadata.distribution(req.name) # type: ignore[no-untyped-call] except importlib_metadata.PackageNotFoundError: # dependency is not installed in the environment. - yield ancestral_req_strings + (req_string,) + yield ancestral_req_strings + (normalised_req_string,) else: if req.specifier and not req.specifier.contains(dist.version, prereleases=True): # the installed version is incompatible. - yield ancestral_req_strings + (req_string,) + yield ancestral_req_strings + (normalised_req_string,) elif dist.requires: for other_req_string in dist.requires: # yields transitive dependencies that are not satisfied. - yield from check_dependency(other_req_string, ancestral_req_strings + (req_string,), req.extras) + yield from check_dependency( + other_req_string, ancestral_req_strings + (normalised_req_string,), req.extras + ) def _find_typo(dictionary: Mapping[str, str], expected: str) -> None: diff --git a/tests/test_projectbuilder.py b/tests/test_projectbuilder.py index 5988cbb0a..d5b1bdf30 100644 --- a/tests/test_projectbuilder.py +++ b/tests/test_projectbuilder.py @@ -133,24 +133,24 @@ def read_text(self, filename): ('extras_dep[extra_without_associated_deps]', None), ( 'extras_dep[extra_with_unmet_deps]', - ('extras_dep[extra_with_unmet_deps]', "unmet_dep; extra == 'extra_with_unmet_deps'"), + ('extras_dep[extra_with_unmet_deps]', 'unmet_dep; extra == "extra_with_unmet_deps"'), ), ( 'extras_dep[recursive_extra_with_unmet_deps]', ( 'extras_dep[recursive_extra_with_unmet_deps]', - "recursive_dep; extra == 'recursive_extra_with_unmet_deps'", + 'recursive_dep; extra == "recursive_extra_with_unmet_deps"', 'recursive_unmet_dep', ), ), ('extras_dep[extra_with_met_deps]', None), ('missing_dep; python_version>"10"', None), ('missing_dep; python_version<="1"', None), - ('missing_dep; python_version>="1"', ('missing_dep; python_version>="1"',)), + ('missing_dep; python_version>="1"', ('missing_dep; python_version >= "1"',)), ('extras_dep == 1.0.0', None), - ('extras_dep == 2.0.0', ('extras_dep == 2.0.0',)), + ('extras_dep == 2.0.0', ('extras_dep==2.0.0',)), ('extras_dep[extra_without_associated_deps] == 1.0.0', None), - ('extras_dep[extra_without_associated_deps] == 2.0.0', ('extras_dep[extra_without_associated_deps] == 2.0.0',)), + ('extras_dep[extra_without_associated_deps] == 2.0.0', ('extras_dep[extra_without_associated_deps]==2.0.0',)), ('prerelease_dep >= 1.0.0', None), ('circular_dep', None), ],