Skip to content

Commit

Permalink
Pin test to legacy resolver to force desired error message
Browse files Browse the repository at this point in the history
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 yaml/pyyaml#702).
I already fixed a similar error here: 0a8e0b4

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` (#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 <module>
    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))
```
  • Loading branch information
jeffwidman committed Aug 5, 2023
1 parent e1598d6 commit 4d4117f
Show file tree
Hide file tree
Showing 2 changed files with 3 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -492,15 +492,15 @@
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) { [] }

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 4d4117f

Please sign in to comment.