Summary
In a project that only uses workspace packages, I get this error when building a workspace package which has a build-dependency on another workspace package which has a dependency on another workspace package. In my example, my-tool lists my-backend as a build-dependency. my-backend depends on my-util.
$ uv build --wheel --package my-tool
Building wheel...
× Failed to build `/home/user/uv-testing/my-workspace/my-tool`
├─▶ Failed to resolve requirements from `build-system.requires`
├─▶ No solution found when resolving: `my-backend @ file:///home/user/uv-testing/my-workspace/my-backend`
├─▶ Failed to resolve dependencies for package `my-backend==0.1.0`
╰─▶ Package `my-util` was included as a URL dependency. URL dependencies must be expressed as direct requirements or
constraints. Consider adding `my-util @ file:///home/user/uv-testing/my-workspace/my-util` to your dependencies
or constraints file.
I cannot tell whether this is a bug or something that is covered by https://docs.astral.sh/uv/reference/internals/resolver/#url-dependencies. To me, it seems as though depending on workspace packages doesn't count as an "index package" and isn't subject to the same issues that resolving index packages have (since we directly list workspace = true as the source of the package). Since neither my-util nor my-backend is included as a dependency in my-tool's wheel, it seems wrong to enforce these resolution requirements onto building.
The docs state,
uv requires that URLs are either declared directly ... or by other URL dependencies.
In the error it seems as though my-backend is attempting to be resolved as a URL dependency (I guess something happens in uv which takes a workspace package and transforms it to a URL dependency) and hence it should be OK for it to include other url dependencies.
Reproducible Pyprojects
The pyproject's for the example are as follows:
my-workspace/pyproject.toml
[project]
name = "my-workspace"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
[tool.uv]
package = false
[tool.uv.workspace]
members = ["my-util", "my-backend", "my-tool"]
[tool.uv.sources]
my-backend = { workspace = true }
my-util = { workspace = true }
my-workspace/my-util/pyproject.toml
[project]
name = "my-util"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
my-workspace/my-backend/pyproject.toml
[project]
name = "my-backend"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["my-util"]
my-workspace/my-tool/pyproject.toml
[project]
name = "my-tool"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
[build-system]
requires = ["my-backend", "setuptools"] # note setuptools is included here since I didn't actually make a real backend for this example, but it should be irrelevant to this issue
Workarounds
The most succinct workaround is to have my-tool declare my-util as a build-dependency. I.e.
my-workspace/my-tool/pyproject.toml
[project]
name = "my-tool"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
[build-system]
requires = ["my-backend", "setuptools", "my-util"]
In my circumstance, I have a really large workspace with 100s of workspace packages which all depend on my-backend or equivalent sort of packages (i.e. plugins). These plugins can be changed often and their dependencies on other local workspace packages can also be changed. It is a pain to go through every dependent workspace package to update their build dependencies with the new transitive dependencies that are necessary for the build plugins it wants to use. There should be no need to have to update transitive dependencies.
These are some other workarounds I've started using that don't involve constantly updating workspace packages with transitive dependencies.
For uv build commands specifically, my workaround is using a build-constraints file like this:
echo "my-util @ file:///home/user/uv-testing/my-workspace/my-util" > my-constraints.txt
uv build --wheel --build-constraints my-constraints.txt --package my-tool
Essentially just have all the common util-like packages that build plugins depend on in a constraints file and use that constraints file for every package you build.
In cases where uv sync requires actually calling the backend of affected files, it also fails. For example, let's include my-tool into a dependency group so that it'll sync when we sync the root project.
my-workspace/pyproject.toml
[project]
name = "my-workspace"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
[tool.uv]
package = false
[tool.uv.workspace]
members = ["my-util", "my-backend", "my-tool"]
[dependency-groups]
tools = ["my-tool"]
[tool.uv.sources]
my-tool = { workspace = true }
my-backend = { workspace = true }
my-util = { workspace = true }
$ uv sync --group tools
Resolved 4 packages in 2ms
× Failed to build `my-tool @ file:///home/user/uv-testing/my-workspace/my-tool`
├─▶ Failed to resolve requirements from `build-system.requires`
├─▶ No solution found when resolving: `setuptools`, `my-backend @
│ file:///home/user/uv-testing/my-workspace/my-backend`
├─▶ Failed to resolve dependencies for package `my-backend==0.1.0`
╰─▶ Package `my-util` was included as a URL dependency. URL dependencies must be expressed as direct requirements or
constraints. Consider adding `my-util @ file:///home/user/uv-testing/my-workspace/my-util` to your dependencies
or constraints file.
help: `my-tool` was included because `my-workspace:tools` (v0.1.0) depends on `my-tool`
My workaround for this is using build-constraint-dependencies. It seems like this ends up resolving my-util prior to the build-system getting mad that my-backend is using a URL dependency.
[tool.uv]
build-constraint-dependencies = ["my-util"]
Platform
Linux
Version
uv 0.11.7 (x86_64-unknown-linux-gnu)
Python version
No response
Summary
In a project that only uses workspace packages, I get this error when building a workspace package which has a build-dependency on another workspace package which has a dependency on another workspace package. In my example,
my-toollistsmy-backendas a build-dependency.my-backenddepends onmy-util.I cannot tell whether this is a bug or something that is covered by https://docs.astral.sh/uv/reference/internals/resolver/#url-dependencies. To me, it seems as though depending on workspace packages doesn't count as an "index package" and isn't subject to the same issues that resolving index packages have (since we directly list
workspace = trueas the source of the package). Since neithermy-utilnormy-backendis included as a dependency inmy-tool's wheel, it seems wrong to enforce these resolution requirements onto building.The docs state,
In the error it seems as though my-backend is attempting to be resolved as a URL dependency (I guess something happens in uv which takes a workspace package and transforms it to a URL dependency) and hence it should be OK for it to include other url dependencies.
Reproducible Pyprojects
The pyproject's for the example are as follows:
my-workspace/pyproject.toml
my-workspace/my-util/pyproject.toml
my-workspace/my-backend/pyproject.toml
my-workspace/my-tool/pyproject.toml
Workarounds
The most succinct workaround is to have
my-tooldeclaremy-utilas a build-dependency. I.e.my-workspace/my-tool/pyproject.toml
In my circumstance, I have a really large workspace with 100s of workspace packages which all depend on
my-backendor equivalent sort of packages (i.e. plugins). These plugins can be changed often and their dependencies on other local workspace packages can also be changed. It is a pain to go through every dependent workspace package to update their build dependencies with the new transitive dependencies that are necessary for the build plugins it wants to use. There should be no need to have to update transitive dependencies.These are some other workarounds I've started using that don't involve constantly updating workspace packages with transitive dependencies.
For
uv buildcommands specifically, my workaround is using a build-constraints file like this:Essentially just have all the common util-like packages that build plugins depend on in a constraints file and use that constraints file for every package you build.
In cases where
uv syncrequires actually calling the backend of affected files, it also fails. For example, let's includemy-toolinto a dependency group so that it'll sync when we sync the root project.my-workspace/pyproject.toml
My workaround for this is using
build-constraint-dependencies. It seems like this ends up resolvingmy-utilprior to the build-system getting mad thatmy-backendis using a URL dependency.Platform
Linux
Version
uv 0.11.7 (x86_64-unknown-linux-gnu)
Python version
No response