Skip to content

Conversation

@zanieb
Copy link
Member

@zanieb zanieb commented 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.

@zanieb zanieb added the error messages Messaging when something goes wrong label Aug 15, 2024
----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because only package-a<=1.2.3 is available and you require package-a>1.2.3, we can conclude that the requirements are unsatisfiable.
╰─▶ Because only package-a==1.2.3+foo is available and you require package-a>1.2.3, we can conclude that the requirements are unsatisfiable.
Copy link
Member Author

Choose a reason for hiding this comment

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

Here we know there is exactly one version available, so we show that instead of a range.

╰─▶ Because package-a==1.0.0 depends on package-b==1.0.0 and only the following versions of package-a are available:
package-a==1.0.0
package-a>=2.0.0,<=3.0.0
package-a>2.0.0,<=3.0.0
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the idea from #6071, there is no 2.0.0 so we shouldn't use >= here.

@zanieb zanieb force-pushed the zb/availability-range-real branch 2 times, most recently from 143c9d2 to 3f1c777 Compare August 15, 2024 16:24
@zanieb zanieb marked this pull request as ready for review August 15, 2024 16:24
@zanieb zanieb force-pushed the zb/availability-range-real branch from 3f1c777 to 1cb945d Compare August 15, 2024 16:27
@zanieb zanieb requested a review from charliermarsh August 15, 2024 16:42
(Some(first), Some(last)) => {
let range = Range::<Version>::from_range_bounds((
Bound::Included(first.clone()),
Bound::Included(last.clone()),
Copy link
Member

@charliermarsh charliermarsh Aug 15, 2024

Choose a reason for hiding this comment

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

Interesting... This should have holes in it, right? But my guess is it doesn't help / doesn't matter?

Copy link
Member Author

Choose a reason for hiding this comment

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

It basically doesn't matter in this case, we're just using it to check if the other range is completely out of bounds.

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe I should write a note for that, maybe there are some weird cases where the holes do matter?

Copy link
Member Author

Choose a reason for hiding this comment

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

I wrote it this way to avoid doing something like for version in available_versions { range.contains(version) } to see if the range contained all or none of the versions.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I think this is cool.

Copy link
Member Author

Choose a reason for hiding this comment

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

Added a note, we probably wont simplify some things that we could.

_ => (*upper).clone(),
};

new_range = new_range.union(&Range::from_range_bounds((lower, upper)));
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if this is quadratic, and we should instead be collecting all the new ranges and then unioning them all at once at the end? But I don't see a way to do that in the Range API.

Copy link
Member Author

Choose a reason for hiding this comment

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

I wish. I think you can't just push segments yourself because it voids some guarantees.

Copy link
Member

Choose a reason for hiding this comment

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

I was hoping you could pass an iterator to union or something.

Copy link
Member Author

Choose a reason for hiding this comment

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

I added a note. We could follow-up in PubGrub. cc @konstin

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.

Makes sense.

// `foo>999`, and the the available versions are all `<10` it's useless to show.
if segment_range.is_disjoint(&available_range) {
continue;
}
Copy link
Member

Choose a reason for hiding this comment

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

What if the range is a singleton, and it's not in available versions?

Copy link
Member

Choose a reason for hiding this comment

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

(Is that impossible?)

Copy link
Member Author

Choose a reason for hiding this comment

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

Like this?

❯ uv add 'httpx==9999'
  ╰─▶ Because there is no version of httpx==9999 and example depends on httpx==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.

I think that's always handled before we get here. Otherwise, this operation is performed on the complement so I don't think it's possible.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah like that. Ok I figured it was somehow handled upstream.

@zanieb zanieb force-pushed the zb/availability-range-real branch from 1cb945d to 7cd52c5 Compare August 15, 2024 17:21
@zanieb zanieb enabled auto-merge (squash) August 15, 2024 17:22
@zanieb zanieb merged commit 0efdbcc into main Aug 15, 2024
@zanieb zanieb deleted the zb/availability-range-real branch August 15, 2024 17:28
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Aug 19, 2024
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [astral-sh/uv](https://github.com/astral-sh/uv) | patch | `0.2.36` -> `0.2.37` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>astral-sh/uv (astral-sh/uv)</summary>

### [`v0.2.37`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#0237)

[Compare Source](astral-sh/uv@0.2.36...0.2.37)

##### Performance

-   Avoid cloning requirement for unchanged markers ([#&#8203;6116](astral-sh/uv#6116))

##### Bug fixes

-   Fix loading of cached metadata for Git distributions with subdirectories ([#&#8203;6094](astral-sh/uv#6094))

##### Error messages

-   Add env var to `--link-mode=copy` warning ([#&#8203;6103](astral-sh/uv#6103))
-   Avoid displaying "failed to download" on build failures for local source distributions ([#&#8203;6075](astral-sh/uv#6075))
-   Improve display of available package ranges ([#&#8203;6118](astral-sh/uv#6118))
-   Use "your requirements" consistently in resolver error messages ([#&#8203;6113](astral-sh/uv#6113))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy40NDAuNyIsInVwZGF0ZWRJblZlciI6IjM3LjQ0MC43IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiXX0=-->
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.

3 participants