Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,63 @@
<!-- prettier-ignore-start -->


## 0.9.0

Released on 2025-10-07.

### Breaking changes

This breaking release is primarily motivated by the release of Python 3.14, which contains some breaking changes (we recommend reading the ["What's new in Python 3.14"](https://docs.python.org/3/whatsnew/3.14.html) page). uv may use Python 3.14 in cases where it previously used 3.13, e.g., if you have not pinned your Python version and do not have any Python versions installed on your machine. While we think this is uncommon, we prefer to be cautious. We've included some additional small changes that could break workflows.

There are no breaking changes to [`uv_build`](https://docs.astral.sh/uv/concepts/build-backend/). If you have an upper bound in your `[build-system]` table, you should update it.

- **Python 3.14 is now the default stable version**

The default Python version has changed from 3.13 to 3.14. This applies to Python version installation when no Python version is requested, e.g., `uv python install`. By default, uv will use the system Python version if present, so this may not cause changes to general use of uv. For example, if Python 3.13 is installed already, then `uv venv` will use that version. If no Python versions are installed on a machine and automatic downloads are enabled, uv will now use 3.14 instead of 3.13, e.g., for `uv venv` or `uvx python`. This change will not affect users who are using a `.python-version` file to pin to a specific Python version.

- **Allow use of free-threaded variants in Python 3.14+ without explicit opt-in** ([#16142](https://github.com/astral-sh/uv/pull/16142))

Previously, free-threaded variants of Python were considered experimental and required explicit opt-in (i.e., with `3.14t`) for usage. Now uv will allow use of free-threaded Python 3.14+ interpreters without explicit selection. The GIL-enabled build of Python will still be preferred, e.g., when performing an installation with `uv python install 3.14`. However, e.g., if a free-threaded interpreter comes before a GIL-enabled build on the `PATH`, it will be used. This change does not apply to free-threaded Python 3.13 interpreters, which will continue to require opt-in.

- **Use Python 3.14 stable Docker images** ([#16150](https://github.com/astral-sh/uv/pull/16150))

Previously, the Python 3.14 images had an `-rc` suffix, e.g., `python:3.14-rc-alpine` or
`python:3.14-rc-trixie`. Now, the `-rc` suffix has been removed to match the stable
[upstream images](https://hub.docker.com/_/python). The `-rc` images tags will no longer be
updated. This change should not break existing workflows.

- **Upgrade Alpine Docker image to Alpine 3.22**

Previously, the `uv:alpine` Docker image was based on Alpine 3.21. Now, this image is based on Alpine 3.22. The previous image can be recovered with `uv:alpine3.21` and will continue to be updated until a future release.

- **Upgrade Debian Docker images to Debian 13 "Trixie"**

Previously, the `uv:debian` and `uv:debian-slim` Docker images were based on Debian 12 "Bookworm". Now, these images are based on Debian 13 "Trixie". The previous images can be recovered with `uv:bookworm` and `uv:bookworm-slim` and will continue to be updated until a future release.

- **Fix incorrect output path when a trailing `/` is used in `uv build`** ([#15133](https://github.com/astral-sh/uv/pull/15133))

When using `uv build` in a workspace, the artifacts are intended to be written to a `dist` directory in the workspace root. A bug caused workspace root determination to fail when the input path included a trailing `/` causing the `dist` directory to be placed in the child directory. This bug has been fixed in this release. For example, `uv build child/` is used, the output path will now be in `<workspace root>/dist/` rather than `<workspace root>/child/dist/`.

### Python

- Add CPython 3.14.0
- Add CPython 3.13.8

### Enhancements

- Don't warn when dependency is constraint by other dependency ([#16149](https://github.com/astral-sh/uv/pull/16149))

### Bug fixes

- Fix `uv python upgrade / install` output when there is a no-op for one request ([#16158](https://github.com/astral-sh/uv/pull/16158))
- Surface pinned-version hint when `uv tool upgrade` can’t move the tool ([#16081](https://github.com/astral-sh/uv/pull/16081))
- Ban pre-release versions in `uv python upgrade` requests ([#16160](https://github.com/astral-sh/uv/pull/16160))
- Fix `uv python upgrade` replacement of installed binaries on pre-release to stable ([#16159](https://github.com/astral-sh/uv/pull/16159))

### Documentation

- Update `uv pip compile` args in `layout.md` ([#16155](https://github.com/astral-sh/uv/pull/16155))

## 0.8.24

Released on 2025-10-06.
Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/uv-build/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "uv-build"
version = "0.8.24"
version = "0.9.0"
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/uv-build/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uv-build"
version = "0.8.24"
version = "0.9.0"
description = "The uv build backend"
authors = [{ name = "Astral Software Inc.", email = "hey@astral.sh" }]
requires-python = ">=3.8"
Expand Down
2 changes: 1 addition & 1 deletion crates/uv-version/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "uv-version"
version = "0.8.24"
version = "0.9.0"
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
Expand Down
8 changes: 8 additions & 0 deletions crates/uv-workspace/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ impl Workspace {
let path = std::path::absolute(path)
.map_err(WorkspaceError::Normalize)?
.clone();
// Remove `.` and `..`
let path = uv_fs::normalize_path(&path);
// Trim trailing slashes.
let path = path.components().collect::<PathBuf>();

let project_path = path
.ancestors()
Expand Down Expand Up @@ -1363,6 +1367,10 @@ impl ProjectWorkspace {
let project_path = std::path::absolute(install_path)
.map_err(WorkspaceError::Normalize)?
.clone();
// Remove `.` and `..`
let project_path = uv_fs::normalize_path(&project_path);
// Trim trailing slashes.
let project_path = project_path.components().collect::<PathBuf>();

// Check if workspaces are explicitly disabled for the project.
if project_pyproject_toml
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "uv"
version = "0.8.24"
version = "0.9.0"
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
Expand Down
62 changes: 62 additions & 0 deletions crates/uv/tests/it/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2084,3 +2084,65 @@ fn venv_included_in_sdist() -> Result<()> {

Ok(())
}

/// Ensure that workspace discovery works with and without trailing slash.
///
/// <https://github.com/astral-sh/uv/issues/13914>
#[test]
fn test_workspace_trailing_slash() {
let context = TestContext::new("3.12");

// Create a workspace with a root and a member.
context.init().arg("--lib").assert().success();
context.init().arg("--lib").arg("child").assert().success();

uv_snapshot!(context.filters(), context.build().arg("child"), @r"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Building source distribution (uv build backend)...
Building wheel from source distribution (uv build backend)...
Successfully built dist/child-0.1.0.tar.gz
Successfully built dist/child-0.1.0-py3-none-any.whl
");

// Check that workspace discovery still works.
uv_snapshot!(context.filters(), context.build().arg("child/"), @r"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Building source distribution (uv build backend)...
Building wheel from source distribution (uv build backend)...
Successfully built dist/child-0.1.0.tar.gz
Successfully built dist/child-0.1.0-py3-none-any.whl
");

// Check general normalization too.
uv_snapshot!(context.filters(), context.build().arg("./child/"), @r"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Building source distribution (uv build backend)...
Building wheel from source distribution (uv build backend)...
Successfully built dist/child-0.1.0.tar.gz
Successfully built dist/child-0.1.0-py3-none-any.whl
");

uv_snapshot!(context.filters(), context.build().arg("./child/../child/"), @r"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Building source distribution (uv build backend)...
Building wheel from source distribution (uv build backend)...
Successfully built dist/child-0.1.0.tar.gz
Successfully built dist/child-0.1.0-py3-none-any.whl
");
}
2 changes: 1 addition & 1 deletion docs/concepts/build-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ To use uv as a build backend in an existing project, add `uv_build` to the

```toml title="pyproject.toml"
[build-system]
requires = ["uv_build>=0.8.24,<0.9.0"]
requires = ["uv_build>=0.9.0,<0.10.0"]
build-backend = "uv_build"
```

Expand Down
6 changes: 3 additions & 3 deletions docs/concepts/projects/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ dependencies = []
example-pkg = "example_pkg:main"

[build-system]
requires = ["uv_build>=0.8.24,<0.9.0"]
requires = ["uv_build>=0.9.0,<0.10.0"]
build-backend = "uv_build"
```

Expand All @@ -134,7 +134,7 @@ dependencies = []
example-pkg = "example_pkg:main"

[build-system]
requires = ["uv_build>=0.8.24,<0.9.0"]
requires = ["uv_build>=0.9.0,<0.10.0"]
build-backend = "uv_build"
```

Expand Down Expand Up @@ -195,7 +195,7 @@ requires-python = ">=3.11"
dependencies = []

[build-system]
requires = ["uv_build>=0.8.24,<0.9.0"]
requires = ["uv_build>=0.9.0,<0.10.0"]
build-backend = "uv_build"
```

Expand Down
6 changes: 3 additions & 3 deletions docs/concepts/projects/workspaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ bird-feeder = { workspace = true }
members = ["packages/*"]

[build-system]
requires = ["uv_build>=0.8.24,<0.9.0"]
requires = ["uv_build>=0.9.0,<0.10.0"]
build-backend = "uv_build"
```

Expand Down Expand Up @@ -106,7 +106,7 @@ tqdm = { git = "https://github.com/tqdm/tqdm" }
members = ["packages/*"]

[build-system]
requires = ["uv_build>=0.8.24,<0.9.0"]
requires = ["uv_build>=0.9.0,<0.10.0"]
build-backend = "uv_build"
```

Expand Down Expand Up @@ -188,7 +188,7 @@ dependencies = ["bird-feeder", "tqdm>=4,<5"]
bird-feeder = { path = "packages/bird-feeder" }

[build-system]
requires = ["uv_build>=0.8.24,<0.9.0"]
requires = ["uv_build>=0.9.0,<0.10.0"]
build-backend = "uv_build"
```

Expand Down
4 changes: 2 additions & 2 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ uv provides a standalone installer to download and install uv:
Request a specific version by including it in the URL:

```console
$ curl -LsSf https://astral.sh/uv/0.8.24/install.sh | sh
$ curl -LsSf https://astral.sh/uv/0.9.0/install.sh | sh
```

=== "Windows"
Expand All @@ -41,7 +41,7 @@ uv provides a standalone installer to download and install uv:
Request a specific version by including it in the URL:

```pwsh-session
PS> powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/0.8.24/install.ps1 | iex"
PS> powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/0.9.0/install.ps1 | iex"
```

!!! tip
Expand Down
4 changes: 2 additions & 2 deletions docs/guides/integration/aws-lambda.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ the second stage, we'll copy this directory over to the final image, omitting th
other unnecessary files.

```dockerfile title="Dockerfile"
FROM ghcr.io/astral-sh/uv:0.8.24 AS uv
FROM ghcr.io/astral-sh/uv:0.9.0 AS uv

# First, bundle the dependencies into the task root.
FROM public.ecr.aws/lambda/python:3.13 AS builder
Expand Down Expand Up @@ -334,7 +334,7 @@ And confirm that opening http://127.0.0.1:8000/ in a web browser displays, "Hell
Finally, we'll update the Dockerfile to include the local library in the deployment package:

```dockerfile title="Dockerfile"
FROM ghcr.io/astral-sh/uv:0.8.24 AS uv
FROM ghcr.io/astral-sh/uv:0.9.0 AS uv

# First, bundle the dependencies into the task root.
FROM public.ecr.aws/lambda/python:3.13 AS builder
Expand Down
10 changes: 5 additions & 5 deletions docs/guides/integration/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ $ docker run --rm -it ghcr.io/astral-sh/uv:debian uv --help
The following distroless images are available:

- `ghcr.io/astral-sh/uv:latest`
- `ghcr.io/astral-sh/uv:{major}.{minor}.{patch}`, e.g., `ghcr.io/astral-sh/uv:0.8.24`
- `ghcr.io/astral-sh/uv:{major}.{minor}.{patch}`, e.g., `ghcr.io/astral-sh/uv:0.9.0`
- `ghcr.io/astral-sh/uv:{major}.{minor}`, e.g., `ghcr.io/astral-sh/uv:0.8` (the latest patch
version)

Expand Down Expand Up @@ -95,7 +95,7 @@ And the following derived images are available:

As with the distroless image, each derived image is published with uv version tags as
`ghcr.io/astral-sh/uv:{major}.{minor}.{patch}-{base}` and
`ghcr.io/astral-sh/uv:{major}.{minor}-{base}`, e.g., `ghcr.io/astral-sh/uv:0.8.24-alpine`.
`ghcr.io/astral-sh/uv:{major}.{minor}-{base}`, e.g., `ghcr.io/astral-sh/uv:0.9.0-alpine`.

In addition, starting with `0.8` each derived image also sets `UV_TOOL_BIN_DIR` to `/usr/local/bin`
to allow `uv tool install` to work as expected with the default user.
Expand Down Expand Up @@ -136,7 +136,7 @@ Note this requires `curl` to be available.
In either case, it is best practice to pin to a specific uv version, e.g., with:

```dockerfile
COPY --from=ghcr.io/astral-sh/uv:0.8.24 /uv /uvx /bin/
COPY --from=ghcr.io/astral-sh/uv:0.9.0 /uv /uvx /bin/
```

!!! tip
Expand All @@ -154,7 +154,7 @@ COPY --from=ghcr.io/astral-sh/uv:0.8.24 /uv /uvx /bin/
Or, with the installer:

```dockerfile
ADD https://astral.sh/uv/0.8.24/install.sh /uv-installer.sh
ADD https://astral.sh/uv/0.9.0/install.sh /uv-installer.sh
```

### Installing a project
Expand Down Expand Up @@ -590,5 +590,5 @@ Verified OK
!!! tip

These examples use `latest`, but best practice is to verify the attestation for a specific
version tag, e.g., `ghcr.io/astral-sh/uv:0.8.24`, or (even better) the specific image digest,
version tag, e.g., `ghcr.io/astral-sh/uv:0.9.0`, or (even better) the specific image digest,
such as `ghcr.io/astral-sh/uv:0.5.27@sha256:5adf09a5a526f380237408032a9308000d14d5947eafa687ad6c6a2476787b4f`.
2 changes: 1 addition & 1 deletion docs/guides/integration/github.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
uses: astral-sh/setup-uv@v6
with:
# Install a specific version of uv.
version: "0.8.24"
version: "0.9.0"
```

## Setting up Python
Expand Down
Loading
Loading