From 10c7544f09aeb71450ac7d8f74ebee6268eddfcd Mon Sep 17 00:00:00 2001 From: Jeff Widman Date: Sat, 5 Aug 2023 08:57:49 -0700 Subject: [PATCH] Pin test to `legacy` resolver to force desired error message Under `pip-tools` `7.0.0`, the default flipped from `--resolver=legacy` to `--resolver=backtracking`. As a result, the error message changed. Furthermore, the new error message is hidden behind an exception thrown by `cython>=3.0.0`/`pyyaml<6.0.1` conflict (details in https://github.com/yaml/pyyaml/pull/702). I already fixed a similar error here: https://github.com/dependabot/dependabot-core/commit/0a8e0b46eb928069ea5847875f6faf0232573200 So the first thing I did was bump `pyyaml` to `6.0.1`. While not strictly necessary, it'll make later debugging easier when / if someone is trying to repro this error under the new `backtracking` resolver. Secondly, I specified `--resolver=legacy` to preserve the old behavior even after we upgrade `pip-tools` (https://github.com/dependabot/dependabot-core/pull/7711). This is acceptable for the purposes of this test because this isn't testing the resolver behavior, it's testing that we properly convert the `pip-tools` native error to a well-known `Dependabot::DependencyFileNotResolvable` error. I did take a peek at the `pip-tools` internals to see if this error messsage was specific solely to the `legacy` resolver, but the logic looks pretty generic... ie, this likely could be raised under the new resolver: https://github.com/jazzband/pip-tools/blob/9e4aeff8f3b5518586091700bec9bbb570154a38/piptools/exceptions.py#L53 This is admittedly a short-term solution because the `legacy` resolver is deprecated and will be removed at some point in the future... so at that point we'll need to find another way to reproduce the error. But enough other things could change upstream in `pip`/`pip-tools` (such as they might switch the error or remove it when they remove the legacy resolver) so we can worry about that when we get there rather than prematurely optimizing. Technically I suppose we could mock the response from calling `pip-compile`, but I hate mocking what I don't own... much prefer to simply coerce it to return the expected error message. For future reference, here's the error message under `--resolver=legacy`: ```shell ```shell [dependabot-core-dev] ~/python/spec/fixtures/pip_compile_files $ pyenv exec pip-compile --build-isolation --output-file=../requirements/incompatible_versions.txt --no-emit-index-url -P pyyaml==6.0.1 --resolver=legacy incompatible_versions.in WARNING: the legacy dependency resolver is deprecated and will be removed in future versions of pip-tools. Using legacy resolver. Consider using backtracking resolver with `--resolver=backtracking`. Could not find a version that matches pyyaml<5.4,==6.0.1,>=3.10 Tried: 3.10, 3.10, 3.11, 3.11, 3.12, 3.12, 3.13, 5.1, 5.1.1, 5.1.2, 5.2, 5.3, 5.3.1, 5.4, 5.4.1, 6.0, 6.0, 6.0.1, 6.0.1 Skipped pre-versions: 3.13b1, 3.13rc1, 4.2b1, 4.2b2, 4.2b4, 5.1b1, 5.1b3, 5.1b5, 5.2b1, 5.3b1, 5.4b1, 5.4b2, 6.0b1 There are incompatible versions in the resolved dependencies: pyyaml==6.0.1 PyYAML<5.4,>=3.10 (from awscli==1.18.198->-r incompatible_versions.in (line 3)) pyyaml (from jinja2-cli[yaml]==0.7.0->-r incompatible_versions.in (line 2)) ``` And here's the error message under `--resolver=backtracking`: ```shell [dependabot-core-dev] ~/python/spec/fixtures/pip_compile_files $ pyenv exec pip-compile --build-isolation --output-file=../requirements/incompatible_versions.txt --no-emit-index-url -P pyyaml==6.0.1 --resolver=backtracking incompatible_versions.in ERROR: Cannot install -r incompatible_versions.in (line 3) and jinja2-cli[yaml]==0.7.0 because these package versions have conflicting dependencies. Traceback (most recent call last): File "/usr/local/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pip/_vendor/resolvelib/resolvers.py", line 316, in _backjump name, candidate = broken_state.mapping.popitem() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ KeyError: 'dictionary is empty' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/local/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/resolver.py", line 92, in resolve result = self._result = resolver.resolve( ^^^^^^^^^^^^^^^^^ File "/usr/local/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pip/_vendor/resolvelib/resolvers.py", line 546, in resolve state = resolution.resolve(requirements, max_rounds=max_rounds) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pip/_vendor/resolvelib/resolvers.py", line 434, in resolve success = self._backjump(causes) ^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pip/_vendor/resolvelib/resolvers.py", line 318, in _backjump raise ResolutionImpossible(causes) pip._vendor.resolvelib.resolvers.ResolutionImpossible: [RequirementInformation(requirement=SpecifierRequirement('pyyaml; extra == "yaml"'), parent=ExtrasCandidate(base=LinkCandidate('https://files.pythonhosted.org/packages/83/52/c1bb249f49b204c14bf603e1b1a6dc2db8c3b631c4fe00a2872360085cd6/jinja2_cli-0.7.0-py2.py3-none-any.whl (from https://pypi.org/simple/jinja2-cli/)'), extras=frozenset({'yaml'}))), RequirementInformation(requirement=SpecifierRequirement('PyYAML<5.4,>=3.10; python_version != "3.4"'), parent=LinkCandidate('https://files.pythonhosted.org/packages/4e/68/35522bf3b5ef9186f3cd7cfdc00fe708c019fada077a21009ff78757b0f9/awscli-1.18.198-py2.py3-none-any.whl (from https://pypi.org/simple/awscli/)'))] The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/usr/local/.pyenv/versions/3.11.4/bin/pip-compile", line 8, in sys.exit(cli()) ^^^^^ File "/usr/local/.pyenv/versions/3.11.4/lib/python3.11/site-packages/click/core.py", line 1157, in __call__ return self.main(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/.pyenv/versions/3.11.4/lib/python3.11/site-packages/click/core.py", line 1078, in main rv = self.invoke(ctx) ^^^^^^^^^^^^^^^^ File "/usr/local/.pyenv/versions/3.11.4/lib/python3.11/site-packages/click/core.py", line 1434, in invoke return ctx.invoke(self.callback, **ctx.params) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/.pyenv/versions/3.11.4/lib/python3.11/site-packages/click/core.py", line 783, in invoke return __callback(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/.pyenv/versions/3.11.4/lib/python3.11/site-packages/click/decorators.py", line 33, in new_func return f(get_current_context(), *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/.pyenv/versions/3.11.4/lib/python3.11/site-packages/piptools/scripts/compile.py", line 657, in cli results = resolver.resolve(max_rounds=max_rounds) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/.pyenv/versions/3.11.4/lib/python3.11/site-packages/piptools/resolver.py", line 604, in resolve is_resolved = self._do_resolve( ^^^^^^^^^^^^^^^^^ File "/usr/local/.pyenv/versions/3.11.4/lib/python3.11/site-packages/piptools/resolver.py", line 636, in _do_resolve resolver.resolve( File "/usr/local/.pyenv/versions/3.11.4/lib/python3.11/site-packages/pip/_internal/resolution/resolvelib/resolver.py", line 101, in resolve raise error from e pip._internal.exceptions.DistributionNotFound: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/topics/dependency-resolution/#dealing-with-dependency-conflicts ``` Furthermore, the `cython>=3.0.0`/`pyyaml<=6.0.1` conflict only appears when I do `--resolver=backtracking`, otherwise under the legacy resolver I don't see that error, even on older version of pyyaml: ```shell [dependabot-core-dev] ~/python/spec/fixtures/pip_compile_files $ pyenv exec pip-compile --build-isolation --output-file=../requirements/incompatible_versions.txt --no-emit-index-url -P pyyaml==5.4 --resolver=legacy incompatible_versions.in WARNING: the legacy dependency resolver is deprecated and will be removed in future versions of pip-tools. Using legacy resolver. Consider using backtracking resolver with `--resolver=backtracking`. Could not find a version that matches pyyaml<5.4,==5.4,>=3.10 Tried: 3.10, 3.10, 3.11, 3.11, 3.12, 3.12, 3.13, 5.1, 5.1.1, 5.1.2, 5.2, 5.3, 5.3.1, 5.4, 5.4.1, 6.0, 6.0, 6.0.1, 6.0.1 Skipped pre-versions: 3.13b1, 3.13rc1, 4.2b1, 4.2b2, 4.2b4, 5.1b1, 5.1b3, 5.1b5, 5.2b1, 5.3b1, 5.4b1, 5.4b2, 6.0b1 There are incompatible versions in the resolved dependencies: pyyaml==5.4 PyYAML<5.4,>=3.10 (from awscli==1.18.198->-r incompatible_versions.in (line 3)) pyyaml (from jinja2-cli[yaml]==0.7.0->-r incompatible_versions.in (line 2)) ``` --- .../python/file_updater/pip_compile_file_updater_spec.rb | 4 ++-- python/spec/fixtures/requirements/incompatible_versions.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/spec/dependabot/python/file_updater/pip_compile_file_updater_spec.rb b/python/spec/dependabot/python/file_updater/pip_compile_file_updater_spec.rb index 662e35340dd..df2a7698d96 100644 --- a/python/spec/dependabot/python/file_updater/pip_compile_file_updater_spec.rb +++ b/python/spec/dependabot/python/file_updater/pip_compile_file_updater_spec.rb @@ -492,7 +492,7 @@ let(:manifest_fixture_name) { "incompatible_versions.in" } let(:generated_fixture_name) { "incompatible_versions.txt" } let(:dependency_name) { "pyyaml" } - let(:dependency_version) { "5.4" } + let(:dependency_version) { "6.0.1" } let(:dependency_previous_version) { "5.3.1" } let(:dependency_requirements) { [] } let(:dependency_previous_requirements) { [] } @@ -500,7 +500,7 @@ it "raises an error indicating the dependencies are not resolvable", :slow do expect { updated_files }.to raise_error(Dependabot::DependencyFileNotResolvable) do |err| expect(err.message).to include( - "There are incompatible versions in the resolved dependencies:\n pyyaml==5.4" + "There are incompatible versions in the resolved dependencies:\n pyyaml==6.0.1" ) end end diff --git a/python/spec/fixtures/requirements/incompatible_versions.txt b/python/spec/fixtures/requirements/incompatible_versions.txt index 51659426d4c..0900c79af25 100644 --- a/python/spec/fixtures/requirements/incompatible_versions.txt +++ b/python/spec/fixtures/requirements/incompatible_versions.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile with python 3.9 # To update, run: # -# pip-compile --output-file=requirements.txt requirements.in +# pip-compile --resolver=legacy --output-file=requirements.txt requirements.in # ansible==2.10.4 # via -r requirements.in