Skip to content

Comments

Special case "only available" bounds in resolver errors#6071

Closed
zanieb wants to merge 2 commits intomainfrom
zb/only-available-exclusive
Closed

Special case "only available" bounds in resolver errors#6071
zanieb wants to merge 2 commits intomainfrom
zb/only-available-exclusive

Conversation

@zanieb
Copy link
Member

@zanieb zanieb commented Aug 13, 2024

So, this is a little nuanced, but it confused me a couple times and I'm poking around in this code so I figured I'd give it a try.

In the following:

❯  uv pip install 'httpx>9999'
  × No solution found when resolving dependencies:
  ╰─▶ Because only httpx<=9999 is available and you require httpx>9999, we can conclude that the requirements are
      unsatisfiable.

We say only httpx<=9999 is available, which suggests that httpx 9999 is available — but it's not, and we know that. The problem is that we naively take the complement of the requirement (httpx>9999).

Here, we special case inclusive bounds to check if the version in question is actually available — if not, we switch to an exclusive bound.

Note this also applies to the reverse case

❯ uv pip install 'httpx<0.000001'
  × No solution found when resolving dependencies:
  ╰─▶ Because only httpx>=0.1 is available and you require httpx<0.1, we can conclude that the requirements are
      unsatisfiable.

However, for simplicity, I haven't tackled the case where we display multiple version segments yet. So for more complex requests, our messaging is still confusing:

❯ uv pip install 'httpx>99,<999'
  × No solution found when resolving dependencies:
  ╰─▶ Because only the following versions of httpx are available:
          httpx<=99
          httpx>=999
      and you require httpx>99,<999, we can conclude that the requirements are unsatisfiable.

This will require further refactoring, which I'll track in a new issue.

@zanieb zanieb added the error messages Messaging when something goes wrong label Aug 13, 2024
@zanieb zanieb marked this pull request as ready for review August 13, 2024 23:17
if let [(lower, upper)] = segments.as_slice() {
// If the bound is inclusive, and the version is _not_
// available, change it to an exclusive bound.
let lower = match lower {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestions welcome on the pattern here, but I'm going to open an issue to follow-up to make this reusable in the multi-segment case as well.

@zanieb zanieb requested a review from charliermarsh August 14, 2024 00:28
Copy link
Member

@charliermarsh charliermarsh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable!

@zanieb zanieb force-pushed the zb/only-available-exclusive branch from 6bb48e7 to d227fcb Compare August 15, 2024 14:10
@zanieb zanieb force-pushed the zb/only-available-exclusive branch from d227fcb to 8eed661 Compare August 15, 2024 14:29
We're using the wrong type here, which never mattered since it was a
single bound but will matter in the future :)
@zanieb
Copy link
Member Author

zanieb commented Aug 15, 2024

#6118 is more comprehensive and would conflict.

@zanieb zanieb closed this Aug 15, 2024
zanieb added a commit that referenced this pull request Aug 15, 2024
Includes the changes from #6071 but
takes them way further.

When we have the set of available versions for a package, we can do a
much better job displaying an error.

For example:

```
❯ uv add 'httpx>999,<9999'
  × No solution found when resolving dependencies:
  ╰─▶ Because only the following versions of httpx are available:
          httpx<=999
          httpx>=9999
      and example==0.1.0 depends on httpx>999,<9999, we can conclude that example==0.1.0 cannot be used.
      And because only example==0.1.0 is available and you require example, we can conclude that the requirements are unsatisfiable.
```

The resolver has demonstrated that the requested range cannot be used
because there are only versions in ranges _outside_ the requested range.
However, the display of the range of available versions is pretty bad!
We say there are versions of httpx available in ranges that definitely
have no versions available.

With this pull request, the error becomes:

```
❯ uv add 'httpx>999,<9999'
  × No solution found when resolving dependencies:
  ╰─▶ Because only httpx<=1.0.0b0 is available and example depends on httpx>999,<9999, we can conclude that example's
      requirements are unsatisfiable.
      And because your workspace requires example, we can conclude that your workspace's requirements are unsatisfiable.
```

We achieve this by:

1. Dropping ranges disjoint with the range of available versions, e.g.,
this removes `httpx>=9999`
2. Replacing ranges that capture the _entire_ range of available
versions with the smaller range, e.g., this replaces `httpx<=999` with
`<=1.0.0b0`.

~Note that when we perform (2), we may include an additional bound that
is not relevant, e.g., we include the lower bound of `>=0.6.7`. This is
a bit extraneous, but I don't think it's confusing. We can consider some
advanced logic to avoid that later.~ (edit: I did this, it wasn't hard)

We also improve error messages when there is _only_ one version
available by showing that version instead of a range.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

error messages Messaging when something goes wrong

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants