Skip to content

project sources shadow workspace sources, even when disabled by markers #14093

@oconnor663

Description

@oconnor663

Summary

Here's a minimal repro. pyproject.toml at the root of the workspace:

[tool.uv.workspace]
members = ["foo"]

[[tool.uv.sources.numpy]]
path = "/var/tmp/numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"

And foo/pyproject.toml:

[project]
name = "foo"
version = "0.1.0"
requires-python = ">=3.13"
dependencies = ["numpy"]

[[tool.uv.sources.numpy]]
path = "/var/tmp/numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
marker = "sys_platform == 'foobarbaz'"

So what we have is both a workspace-level and a project-level source for numpy. However, the project-level source is disabled by a marker that will never be true. Here's what I see when I uv sync:

$ rm -r .venv && uv sync
Using CPython 3.13.2
Creating virtual environment at: .venv
Resolved 3 packages in 60ms
Installed 1 package in 11ms
 + numpy==2.2.0

Contrast that with what I see if I remove the project-level source:

$ rm -r .venv && uv sync
Using CPython 3.13.2
Creating virtual environment at: .venv
Resolved 2 packages in 0.65ms
Installed 1 package in 11ms
 + numpy==2.2.0 (from file:///var/tmp/numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl)

In this case the workspace-level source is used. The bug(?) is that it should've been used in both cases. I think the cause is structure of the following if statement:

let (sources, origin) = if let Some(source) = project_sources.get(&requirement.name) {
(Some(source), RequirementOrigin::Project)
} else if let Some(source) = workspace.sources().get(&requirement.name) {
(Some(source), RequirementOrigin::Workspace)
} else {
(None, RequirementOrigin::Project)
};

In that code, if there's a project-level source with a matching name, then any workspace-level source is ignored. But this happens before markers are checked.

Platform

linux

Version

uv 0.7.13 (62ed17b 2025-06-12)

Python version

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions