Skip to content

Conversation

yumeminami
Copy link
Contributor

@yumeminami yumeminami commented Aug 12, 2025

Summary

Fixes issue #15190 where uv sync --no-sources fails to switch from editable to registry package installations. The problem occurred because the installer's satisfaction check didn't consider the --no-sources flag when determining if an existing editable installation was compatible with a registry requirement.

Solution

Modified RequirementSatisfaction::check() to reject non-registry installations when SourceStrategy::Disabled and the requirement is from registry. Added SourceStrategy parameter threading through the entire call chain from commands to the satisfaction check to ensure consistent behavior between uv sync --no-sources and uv pip install --no-sources.

@zanieb
Copy link
Member

zanieb commented Aug 12, 2025

Hey thanks for taking a swing at this!

Naively, I think I'd expect this to be implemented in SitePackages instead? e.g.,

/// Returns if the installed packages satisfy the given requirements.
pub fn satisfies_spec(
&self,
requirements: &[UnresolvedRequirementSpecification],
constraints: &[NameRequirementSpecification],
overrides: &[UnresolvedRequirementSpecification],
markers: &ResolverMarkerEnvironment,
) -> Result<SatisfiesResult> {

/// Like [`SitePackages::satisfies_spec`], but with resolved names for all requirements.
pub fn satisfies_requirements<'a>(
&self,
requirements: impl ExactSizeIterator<Item = &'a Requirement>,
constraints: impl Iterator<Item = &'a Requirement>,
overrides: impl Iterator<Item = &'a Requirement>,
markers: &ResolverMarkerEnvironment,
) -> Result<SatisfiesResult> {

Did you look at that? Does that not work for some reason?

@yumeminami
Copy link
Contributor Author

yumeminami commented Aug 13, 2025

@zanieb

After reviewing the uv sync call chain, I found that it does not use SitePackages::satisfies_spec() or
satisfies_requirements(). Instead, it follows this path:

uv syncoperations::install()Planner::build()RequirementSatisfaction::check()

The bug is in RequirementSatisfaction::check() at

RequirementSource::Registry { specifier, .. } => {
if specifier.contains(distribution.version()) {
return Self::Satisfied;
}
Self::Mismatch
}

Why Not Pass SourceStrategy?

Initially, I considered passing SourceStrategy to RequirementSatisfaction::check(), but this would require:

  1. Extensive API changes: Modifying function signatures across multiple layers
  2. High impact: Affecting many unrelated code paths
  3. Complex propagation: Threading the parameter through the entire call chain

@yumeminami
Copy link
Contributor Author

@zanieb

same problem use uv pip install --no-sources

(test_uv_pip_no_sources) ➜  test_uv_pip_no_sources git:(fix/sync-no-sources-editable-switch) ✗ cat pyproject.toml             
[project]
name = "test_no_sources"
version = "0.0.1"

dependencies = ["git_mcp_server"]

[tool.uv.sources]
git_mcp_server = { path = "./git_mcp", editable = true }


(test_uv_pip_no_sources) ➜  test_uv_pip_no_sources git:(fix/sync-no-sources-editable-switch) ✗ head git_mcp/pyproject.toml
[project]
name = "git_mcp_server"
version = "0.1.9"
description = "Git MCP Server - Unified command-line tool for managing Git repositories across GitHub and GitLab"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
    "python-gitlab>=4.4.0",
    "PyGithub>=2.1.0",
    "click>=8.1.0",
    
   
(test_uv_pip_no_sources) ➜  test_uv_pip_no_sources git:(fix/sync-no-sources-editable-switch) ✗ uv pip install --no-sources git_mcp_server
Resolved 64 packages in 48ms
Installed 1 package in 7ms
 + git-mcp-server==0.1.8
(test_uv_pip_no_sources) ➜  test_uv_pip_no_sources git:(fix/sync-no-sources-editable-switch) ✗ uv pip install -e .                       
Resolved 65 packages in 24ms
      Built test-no-sources @ file:///Users/wingmunfung/workspace/uv/test_uv_pip_no_sources
Prepared 1 package in 520ms
Uninstalled 2 packages in 4ms
Installed 2 packages in 3ms
 - git-mcp-server==0.1.8
 + git-mcp-server==0.1.9 (from file:///Users/wingmunfung/workspace/uv/test_uv_pip_no_sources/git_mcp)
 ~ test-no-sources==0.0.1 (from file:///Users/wingmunfung/workspace/uv/test_uv_pip_no_sources)
 
 
(test_uv_pip_no_sources) ➜  test_uv_pip_no_sources git:(fix/sync-no-sources-editable-switch) ✗ uv pip install --no-sources git_mcp_server
Audited 1 package in 15ms


(test_uv_pip_no_sources) ➜  test_uv_pip_no_sources git:(fix/sync-no-sources-editable-switch) ✗ uv pip show git_mcp_server                
Name: git-mcp-server
Version: 0.1.9
Location: /Users/wingmunfung/workspace/uv/test_uv_pip_no_sources/.venv/lib/python3.13/site-packages
Editable project location: /Users/wingmunfung/workspace/uv/test_uv_pip_no_sources/git_mcp
Requires: click, gitpython, httpx, keyring, mcp, pre-commit, pydantic, pygithub, python-gitlab, pyyaml, rich, tool
Required-by: test-no-sources
(test_uv_pip_no_sources) ➜  

I think the git-mcp-server version should be 0.1.8 while I run v pip install --no-sources git_mcp_server

@zanieb
Copy link
Member

zanieb commented Aug 13, 2025

Yes sorry, RequirementSatisfaction::check and the Planner::build methods are relevant too.

I'm not sure if we need to propagate the source strategy? That should be reflected in the resolution?

I think we should be able to tell if an installed distribution matches the resolution. Naively, I'd image we want something like this?

diff --git a/crates/uv-installer/src/satisfies.rs b/crates/uv-installer/src/satisfies.rs
index b7e824202..48709e2cc 100644
--- a/crates/uv-installer/src/satisfies.rs
+++ b/crates/uv-installer/src/satisfies.rs
@@ -31,7 +31,9 @@ impl RequirementSatisfaction {
         match source {
             // If the requirement comes from a registry, check by name.
             RequirementSource::Registry { specifier, .. } => {
-                if specifier.contains(distribution.version()) {
+                if matches!(distribution, InstalledDist::Registry { .. })
+                    && specifier.contains(distribution.version())
+                {
                     return Self::Satisfied;
                 }
                 Self::Mismatch

@yumeminami yumeminami force-pushed the fix/sync-no-sources-editable-switch branch 4 times, most recently from 1f43846 to b552e40 Compare August 13, 2025 11:20
@yumeminami
Copy link
Contributor Author

@zanieb

Thank you for your feedback.

After applying your modification, I was able to install the package again using uv sync --no-sources instead of getting no output.

However, running uv pip install --no-sources will throws an exception.

Upon closer inspection, I found that the call chains for sync and pip install differ. In particular, pip install uses a CandidateSelector, whose CandidateSelector::get_installed() strategy is inconsistent with the matching logic in RequirementSatisfaction::check().

This mismatch causes the resolver to treat the package as ResolvedDist::Installed(does not check it is a Url or a Registry), while RequirementSatisfaction::check() flags it as mismatched—leading to a panic.

(test_uv_sync) ➜ test_uv_sync uv-dev pip install --no-sources anyio -v
DEBUG uv 0.8.9+2 (250c3a746 2025-08-13)
DEBUG Searching for default Python interpreter in virtual environments
DEBUG Found `cpython-3.13.2-macos-aarch64-none` at `/Users/wingmunfung/test_uv_sync/.venv/bin/python3` (active virtual environment)
DEBUG Using Python 3.13.2 environment at: .venv
DEBUG Acquired lock for `.venv`
DEBUG Registry check: distribution=Url(InstalledDirectUrlDist { name: PackageName("anyio"), version: "4.3.0", direct_url: LocalDirectory { url: "file:///Users/wingmunfung/test_uv_sync/local_dep", dir_info: DirInfo { editable: Some(true) }, subdirectory: None }, url: DisplaySafeUrl { scheme: "file", cannot_be_a_base: false, username: "", password: None, host: None, port: None, path: "/Users/wingmunfung/test_uv_sync/local_dep", query: None, fragment: None }, editable: true, path: "/Users/wingmunfung/test_uv_sync/.venv/lib/python3.13/site-packages/anyio-4.3.0.dist-info", cache_info: Some(CacheInfo { timestamp: Some(Timestamp(SystemTime { tv_sec: 1755059554, tv_nsec: 377749146 })), commit: None, tags: None, env: {}, directories: {"src": None} }) }), specifier=VersionSpecifiers([]), version="4.3.0"
DEBUG At least one requirement is not satisfied: anyio
DEBUG Using request timeout of 30s
DEBUG Solving with installed Python version: 3.13.2
DEBUG Solving with target Python version: >=3.13.2
DEBUG Adding direct dependency: anyio*
DEBUG Found stale response for: https://pypi.org/simple/anyio/
DEBUG Sending revalidation request for: https://pypi.org/simple/anyio/
DEBUG Found not-modified response for: https://pypi.org/simple/anyio/
DEBUG Found installed version of anyio==4.3.0 (from file:///Users/wingmunfung/test_uv_sync/local_dep) that satisfies *
DEBUG Searching for a compatible version of anyio (*)
DEBUG Found installed version of anyio==4.3.0 (from file:///Users/wingmunfung/test_uv_sync/local_dep) that satisfies *
DEBUG Selecting: anyio==4.3.0 [installed] (installed)
DEBUG Tried 1 versions: anyio 1
DEBUG marker environment resolution took 0.248s
Resolved 1 package in 251ms
DEBUG Registry check: distribution=Url(InstalledDirectUrlDist { name: PackageName("anyio"), version: "4.3.0", direct_url: LocalDirectory { url: "file:///Users/wingmunfung/test_uv_sync/local_dep", dir_info: DirInfo { editable: Some(true) }, subdirectory: None }, url: DisplaySafeUrl { scheme: "file", cannot_be_a_base: false, username: "", password: None, host: None, port: None, path: "/Users/wingmunfung/test_uv_sync/local_dep", query: None, fragment: None }, editable: true, path: "/Users/wingmunfung/test_uv_sync/.venv/lib/python3.13/site-packages/anyio-4.3.0.dist-info", cache_info: Some(CacheInfo { timestamp: Some(Timestamp(SystemTime { tv_sec: 1755059554, tv_nsec: 377749146 })), commit: None, tags: None, env: {}, directories: {"src": None} }) }), specifier=VersionSpecifiers([VersionSpecifier { operator: Equal, version: "4.3.0" }]), version="4.3.0"
DEBUG Requirement installed, but mismatched: 
Installed: Url(InstalledDirectUrlDist { name: PackageName("anyio"), version: "4.3.0", direct_url: LocalDirectory { url: "file:///Users/wingmunfung/test_uv_sync/local_dep", dir_info: DirInfo { editable: Some(true) }, subdirectory: None }, url: DisplaySafeUrl { scheme: "file", cannot_be_a_base: false, username: "", password: None, host: None, port: None, path: "/Users/wingmunfung/test_uv_sync/local_dep", query: None, fragment: None }, editable: true, path: "/Users/wingmunfung/test_uv_sync/.venv/lib/python3.13/site-packages/anyio-4.3.0.dist-info", cache_info: Some(CacheInfo { timestamp: Some(Timestamp(SystemTime { tv_sec: 1755059554, tv_nsec: 377749146 })), commit: None, tags: None, env: {}, directories: {"src": None} }) }) 
Requested: Registry { specifier: VersionSpecifiers([VersionSpecifier { operator: Equal, version: "4.3.0" }]), index: None, conflict: None }

thread 'main2' panicked at crates/uv-installer/src/plan.rs:150:17:
internal error: entered unreachable code: Installed distribution could not be Found in site-packages: anyio==4.3.0 (from file:///Users/wingmunfung/test_uv_sync/local_dep)
Note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace
DEBUG Released lock at `/Users/wingmunfung/test_uv_sync/.venv/.lock`

Thread 'main' panicked at /Users/wingmunfung/workspace/uv/crates/uv/src/lib.rs:2300:10:
Tokio executor failed, was there a panic?: Any { .. }

@yumeminami
Copy link
Contributor Author

// 1. Apparently, we never return direct URL distributions as [`ResolvedDist::Installed`].
// If you trace the resolver, we only ever return [`ResolvedDist::Installed`] if you go
// through the [`CandidateSelector`], and we only go through the [`CandidateSelector`]
// for registry distributions.
//
// 2. We expect any distribution returned as [`ResolvedDist::Installed`] to hit the
// "Requirement already installed" path (hence the `unreachable!`) a few lines below it.
// So, e.g., if a package is marked as `--reinstall`, we _expect_ that it's not passed in
// as [`ResolvedDist::Installed`] here.

Doesn’t the log above violate the assumption in the comment #15234 (comment)?

DEBUG Found installed version of anyio==4.3.0 (from file:///Users/wingmunfung/test_uv_sync/local_dep) that satisfies *
DEBUG Selecting: anyio==4.3.0 [installed] (installed)
DEBUG Requirement installed, but mismatched: 
Installed: Url(InstalledDirectUrlDist { name: PackageName("anyio"), version: "4.3.0", direct_url: LocalDirectory { url: "file:///Users/wingmunfung/test_uv_sync/local_dep", dir_info: DirInfo { editable: Some(true) }, subdirectory: None }, url: DisplaySafeUrl { scheme: "file", cannot_be_a_base: false, username: "", password: None, host: None, port: None, path: "/Users/wingmunfung/test_uv_sync/local_dep", query: None, fragment: None }, editable: true, path: "/Users/wingmunfung/test_uv_sync/.venv/lib/python3.13/site-packages/anyio-4.3.0.dist-info", cache_info: Some(CacheInfo { timestamp: Some(Timestamp(SystemTime { tv_sec: 1755059554, tv_nsec: 377749146 })), commit: None, tags: None, env: {}, directories: {"src": None} }) }) 
Requested: Registry { specifier: VersionSpecifiers([VersionSpecifier { operator: Equal, version: "4.3.0" }]), index: None, conflict: None }

@yumeminami
Copy link
Contributor Author

@zanieb

Could you help me look into this issue?

@zanieb
Copy link
Member

zanieb commented Aug 14, 2025

Hey, I'll look when I get a chance — I've been a bit busy and don't know the answer off the top of my head so I'll need to dig into the problem.

@charliermarsh
Copy link
Member

I think that comment is probably wrong. I think CandidateSelector::select can return InstalledDirectUrlDist if you have an already-installed URL that matches a user requirement (e.g., you have anyio @ 4.3.0 installed from Git, and you run uv pip install anyio).

I think the error you're seeing is because you changed the logic in the install plan, but you need to make a corresponding change in the resolver, such that when resolving, we don't choose the already-installed URL distribution if the user didn't request a URL. In other words: you need to reflect the change in both the install plan and the resolver, and modifying satisfies only affects the install plan.

It's not totally clear to me whether we want that behavior, though. It's clear for the uv sync case, but like, if we make this change as described, then uv pip install git+https://github.com/agronholm/anyio followed by uv pip install anyio would uninstall the Git-based anyio and then reinstall it from PyPI. That would be a breaking change, so we probably make sure we want to do that before finishing the PR.

(Alternatively, if we could somehow make this change only for the uv sync case, that'd be easier to ship.)

@yumeminami
Copy link
Contributor Author

@charliermarsh

My current change is limited to handling uv sync --no-sources only:

  1. Check whether the SourceStrategy is false.
  2. Collect the registry packages from the resolution.
  3. Check for already InstalledDist::Url(_)/InstalledDist::LegacyEditable(_) packages.
  4. Mark them for reinstallation.

Do you think this scoped fix is small enough for now?

I also experimented with modifying the resolver. Specifically, I tried passing SourceStrategy into the CandidateSelector, and alternatively, filtering out InstalledDist::Url and InstalledDist::LegacyEditable in CandidateSelector::get_installed() so they’re no longer considered as installed. This does solve the issue for pip install --no-sources, but it comes with some drawbacks:

  • CandidateSelector is used in many modules, so the change could have unintended consequences.
  • It requires passing parameters deeply, which means modifying many call sites.
  • It affects a large number of unit tests.

@charliermarsh
Copy link
Member

@yumeminami -- Sorry, will get back to you soon, this is on my list.

@charliermarsh charliermarsh self-requested a review August 22, 2025 10:23
@charliermarsh charliermarsh self-assigned this Aug 22, 2025
@charliermarsh charliermarsh added the bug Something isn't working label Aug 22, 2025
@charliermarsh
Copy link
Member

@yumeminami -- What do you think of #15450? It's a slightly different approach whereby the check logic uses different rules depending on whether we're using uv pip or uv sync.

@yumeminami
Copy link
Contributor Author

@charliermarsh

Thanks your feedback!

I took a look of #15450 and I think it did the thing like as discussed in #15234 (comment) by passing the SyncModel(Sourcestrategy in the comment) argument through multiple functions until it reaches RequirementSatisfaction::check.

I have some suggestions:

  1. SyncModel implies it's about uv sync? I'm not sure, but it actually controls the "packet matching strategy" and I feel like Stateful and Stateless might be a bit ambiguous. And I sugget use this InstallationStrategy, what do you think?
/// Defines the strategy for determining whether an installed package satisfies a requirement.
  ///
  /// This enum controls how the package installer validates existing installations against
  /// new requirements, particularly when there are differences in source types (registry vs local).
  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  pub enum InstallationStrategy {
      /// Permissive strategy: Accept existing installations even if source type differs.
      ///
      /// This strategy mirrors traditional pip behavior, where already-installed packages
      /// are reused if they satisfy the version requirements, regardless of how they were
      /// originally installed. For example:
      /// - If `./path/to/package` is installed and later `package` is requested from registry,
      ///   the existing installation will be reused if the version matches.
      /// - This enables incremental package management where users can mix installation sources.
      ///
      /// Used by: `uv pip install`, `uv pip sync`
      Permissive,

      /// Strict strategy: Require exact source type matching between installed and requested packages.
      ///
      /// This strategy enforces that the installation source must match the requirement source.
      /// It prevents reusing packages that were installed from different sources, ensuring
      /// declarative and reproducible environments. For example:
      /// - If an editable package is installed but a registry version is requested,
      ///   the editable installation will be replaced with the registry version.
      /// - This is essential for `--no-sources` scenarios where local installations
      ///   should be replaced with registry equivalents.
      ///
      /// Used by: `uv sync`, `uv run`, `uv tool run`
      Strict,
  }
  1. Does this PR fix the issue in Fix uv sync --no-sources not switching from editable to registry installations #15234 (comment) about pip install --no-soures? If the checks detect a Statefule condition and a distribution mismatch InstalledDist::Registry { .. }, should we prompt the user?

@yumeminami
Copy link
Contributor Author

yumeminami commented Aug 23, 2025

I’ve attempted to add SyncModel as a property of the SitePackages struct so that we could update the construction of SitePackages instances instead of modifying multiple function signatures and calls.

And I found Rust does not support default parameters in functions—by default, Stateful is used, and Stateless must be explicitly passed when needed. This makes the solution more involved than the approach in PR #15450.

😢 😢

@charliermarsh
Copy link
Member

Yeah I think your names are better (mine were sort of lazy, to validate the idea).

I think this should only apply to uv sync personally, and even uv pip install --no-sources should have the same behavior whereby we "accept" the implicit registry requirement.

We might (?) need to thread this through to the candidate selector / resolver as well, is the only other downside... That's why some tests are failing, I think.

@yumeminami
Copy link
Contributor Author

Yeah, I agree.

If we want the uv pip install --no-sources has the same behave as uv sycn --no-sources, the candidate selector strategy need to align the RequirementSatisfaction::check func.

@yumeminami
Copy link
Contributor Author

@charliermarsh

Later, I will base #15450 then modify the candidate selector strategy and new a pr again.(maybe Tuesday or Wednesday)

When --no-sources is used, both uv sync and uv pip install should prefer
registry installations over editable/URL installations. Previously, the
resolver would still select installed editable packages even when sources
were disabled, causing inconsistent behavior.

This fix modifies both the resolver's candidate selector and the installer's
satisfaction checker to reject non-registry installations when sources are
disabled, ensuring proper switching from editable to registry packages.
@yumeminami yumeminami force-pushed the fix/sync-no-sources-editable-switch branch from 849071e to 9b89f34 Compare August 27, 2025 03:40
@yumeminami
Copy link
Contributor Author

@charliermarsh

Now th PR should fix uv sync --no-sources and uv pip install --no-sources.

@yumeminami
Copy link
Contributor Author

how to rerun the CI and why the install python job will fail?

@charliermarsh
Copy link
Member

@yumeminami -- Sorry, I was trying to convey that I think this should only affect uv sync (and uv pip install should be unchanged). Do you agree?

@yumeminami
Copy link
Contributor Author

yumeminami commented Sep 16, 2025 via email

@charliermarsh
Copy link
Member

Okay thanks. I will revisit this and look to get it merged.

@charliermarsh
Copy link
Member

Okay, this should be good to go. It only changes the behavior for uv sync (and uv run, which calls uv sync behind the hood), where we have full control over the sources and resolution.

@yumeminami
Copy link
Contributor Author

@charliermarsh thanks!

@zanieb zanieb merged commit accfb48 into astral-sh:main Sep 17, 2025
98 checks passed
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Sep 24, 2025
This MR contains the following updates:

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

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.8.22`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#0822)

[Compare Source](astral-sh/uv@0.8.21...0.8.22)

Released on 2025-09-23.

##### Python

- Upgrade Pyodide to 0.28.3 ([#&#8203;15999](astral-sh/uv#15999))

##### Security

- Upgrade `astral-tokio-tar` to 0.5.5 which [hardens tar archive extraction](GHSA-3wgq-wrwc-vqmv) ([#&#8203;16004](astral-sh/uv#16004))

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

[Compare Source](astral-sh/uv@0.8.20...0.8.21)

Released on 2025-09-23.

##### Enhancements

- Refresh lockfile when `--refresh` is provided ([#&#8203;15994](astral-sh/uv#15994))

##### Preview features

Add support for S3 request signing ([#&#8203;15925](astral-sh/uv#15925))

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

[Compare Source](astral-sh/uv@0.8.19...0.8.20)

Released on 2025-09-22.

##### Enhancements

- Add `--force` flag for `uv cache clean` ([#&#8203;15992](astral-sh/uv#15992))
- Improve resolution errors with proxied packages ([#&#8203;15200](astral-sh/uv#15200))

##### Preview features

- Allow upgrading pre-release versions of the same minor Python version ([#&#8203;15959](astral-sh/uv#15959))

##### Bug fixes

- Hide `freethreaded+debug` Python downloads in `uv python list` ([#&#8203;15985](astral-sh/uv#15985))
- Retain the cache lock and temporary caches during `uv run` and `uvx` ([#&#8203;15990](astral-sh/uv#15990))

##### Documentation

- Add `package` level conflicts to the conflicting dependencies docs ([#&#8203;15963](astral-sh/uv#15963))
- Document pyodide support ([#&#8203;15962](astral-sh/uv#15962))
- Document support for free-threaded and debug Python versions ([#&#8203;15961](astral-sh/uv#15961))
- Expand the contribution docs on issue selection ([#&#8203;15966](astral-sh/uv#15966))
- Tweak title for viewing version in project guide ([#&#8203;15964](astral-sh/uv#15964))

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

[Compare Source](astral-sh/uv@0.8.18...0.8.19)

Released on 2025-09-19.

##### Python

- Add CPython 3.14.0rc3
- Upgrade OpenSSL to 3.5.3

See the [python-build-standalone release notes](https://github.com/astral-sh/python-build-standalone/releases/tag/20250918) for more details.

##### Bug fixes

- Make `uv cache clean` parallel process safe ([#&#8203;15888](astral-sh/uv#15888))
- Fix implied `platform_machine` marker for `win_arm64` platform tag ([#&#8203;15921](astral-sh/uv#15921))

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

[Compare Source](astral-sh/uv@0.8.17...0.8.18)

Released on 2025-09-17.

##### Enhancements

- Add PyG packages to torch backend ([#&#8203;15911](astral-sh/uv#15911))
- Add handling for unnamed conda environments in base environment detection ([#&#8203;15681](astral-sh/uv#15681))
- Allow selection of debug build interpreters ([#&#8203;11520](astral-sh/uv#11520))
- Improve `uv init` defaults for native build backend cache keys ([#&#8203;15705](astral-sh/uv#15705))
- Error when `pyproject.toml` target does not exist for dependency groups ([#&#8203;15831](astral-sh/uv#15831))
- Infer check URL from publish URL when known ([#&#8203;15886](astral-sh/uv#15886))
- Support Gitlab CI/CD as a trusted publisher ([#&#8203;15583](astral-sh/uv#15583))
- Add GraalPy 25.0.0 with support for Python 3.12 ([#&#8203;15900](astral-sh/uv#15900))
- Add `--no-clear` to `uv venv` to disable removal prompts ([#&#8203;15795](astral-sh/uv#15795))
- Add conflict detection between `--only-group` and `--extra` flags ([#&#8203;15788](astral-sh/uv#15788))
- Allow `[project]` to be missing from a `pyproject.toml` ([#&#8203;14113](astral-sh/uv#14113))
- Always treat conda environments named `base` and `root` as base environments ([#&#8203;15682](astral-sh/uv#15682))
- Improve log message when direct build for `uv_build` is skipped ([#&#8203;15898](astral-sh/uv#15898))
- Log when the cache is disabled ([#&#8203;15828](astral-sh/uv#15828))
- Show pyx organization name after authenticating ([#&#8203;15823](astral-sh/uv#15823))
- Use `_CONDA_ROOT` to detect Conda base environments ([#&#8203;15680](astral-sh/uv#15680))
- Include blake2b hash in `uv publish` upload form ([#&#8203;15794](astral-sh/uv#15794))
- Fix misleading debug message when removing environments in `uv sync` ([#&#8203;15881](astral-sh/uv#15881))

##### Deprecations

- Deprecate `tool.uv.dev-dependencies` ([#&#8203;15469](astral-sh/uv#15469))
- Revert "feat(ci): build loongarch64 binaries in CI ([#&#8203;15387](astral-sh/uv#15387))" ([#&#8203;15820](astral-sh/uv#15820))

##### Preview features

- Propagate preview flag to client for `native-auth` feature ([#&#8203;15872](astral-sh/uv#15872))
- Store native credentials for realms with the https scheme stripped ([#&#8203;15879](astral-sh/uv#15879))
- Use the root index URL when retrieving credentials from the native store ([#&#8203;15873](astral-sh/uv#15873))

##### Bug fixes

- Fix `uv sync --no-sources` not switching from editable to registry installations ([#&#8203;15234](astral-sh/uv#15234))
- Avoid display of an empty string when a path is the working directory ([#&#8203;15897](astral-sh/uv#15897))
- Allow cached environment reuse with `@latest` ([#&#8203;15827](astral-sh/uv#15827))
- Allow escaping spaces in --env-file handling ([#&#8203;15815](astral-sh/uv#15815))
- Avoid ANSI codes in debug! messages ([#&#8203;15843](astral-sh/uv#15843))
- Improve BSD tag construction ([#&#8203;15829](astral-sh/uv#15829))
- Include SHA when listing lockfile changes ([#&#8203;15817](astral-sh/uv#15817))
- Invert the logic for determining if a path is a base conda environment ([#&#8203;15679](astral-sh/uv#15679))
- Load credentials for explicit members when lowering ([#&#8203;15844](astral-sh/uv#15844))
- Re-add `triton` as a torch backend package ([#&#8203;15910](astral-sh/uv#15910))
- Respect `UV_INSECURE_NO_ZIP_VALIDATION=1` in duplicate header errors ([#&#8203;15912](astral-sh/uv#15912))

##### Documentation

- Add GitHub Actions to PyPI trusted publishing example ([#&#8203;15753](astral-sh/uv#15753))
- Add Coiled integration documentation ([#&#8203;14430](astral-sh/uv#14430))
- Add verbose output to the getting help section ([#&#8203;15915](astral-sh/uv#15915))
- Document `NO_PROXY` support ([#&#8203;15816](astral-sh/uv#15816))
- Document cache-keys for native build backends ([#&#8203;15811](astral-sh/uv#15811))
- Add documentation for dependency group `requires-python` ([#&#8203;14282](astral-sh/uv#14282))

</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:eyJjcmVhdGVkSW5WZXIiOiI0MS4xMTUuNiIsInVwZGF0ZWRJblZlciI6IjQxLjEyNS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiXX0=-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants