Skip to content
Draft
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ bazel run //:gazelle

1. **Swap the rules**: Load `py_binary`, `py_library`, `py_test` from `@aspect_rules_py//py:defs.bzl` instead of
`@rules_python//python:defs.bzl`
2. **Migrate dependencies**: Replace `pip.parse` with `uv.hub` and generate a `uv.lock`
2. **Migrate dependencies**: Replace `pip.parse` with `uv.declare_hub` and generate a `uv.lock`
3. **Optionally migrate toolchains**: Replace `rules_python` interpreter provisioning with
the `aspect_rules_py` interpreter extension for fully independent hermetic interpreters

Expand Down
25 changes: 22 additions & 3 deletions docs/uv.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,10 @@ py_library(
## A mental model

```
@pypi # Your UV built hub repository
@pypi//requests:requests # The library for a requirement
@pypi//jinja2-cli/entrypoints:jinja2-cli # A requirement's declared entrypoint
@pypi # Your UV built hub repository
@pypi//requests:requests # The library for a requirement
@pypi//jinja2-cli/entrypoints:jinja2-cli # A requirement's declared entrypoint
@pypi//project/my_project:requests # Project-qualified label (see below)
```

This central hub wraps "spoke" internal dependency group repos. For instance if you have two
Expand All @@ -319,6 +320,24 @@ over the dependency group targets in which that requirement is defined.
Hub requirement targets are _incompatible_ with dependency group configurations in which the
requirement in question is not defined.

### Project-qualified labels

In a multi-project hub where two projects both provide the same package, the unqualified label
can produce a Bazel "multiple keys match" error when the active dep_group name is shared across
projects. The project-qualified form scopes the `select()` to a single project, eliminating
the ambiguity:

```python
# Unqualified — works unless two projects have the same package + same dep_group name
deps = ["@pypi//requests"]

# Project-qualified — always routes to exactly one project's resolution
deps = ["@pypi//project/my_project:requests"]
```

The `<stamp>` in `//project/<stamp>` is the [PEP 503-normalised](https://peps.python.org/pep-0503/#normalized-names)
form of the `[project].name` declared in that project's `pyproject.toml`.

### Conditional dependencies and the empty target

When a package is gated entirely behind an environment marker (e.g.
Expand Down
29 changes: 29 additions & 0 deletions e2e/cases/multi-project-hub/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,32 @@ py_test(
"@pypi_multi//cowsay",
],
)

# Project-qualified labels: each project exposes its packages under
# @hub//project/<stamp>:<pkg>, scoping the select to that project only.
py_test(
name = "alpha_qualified",
srcs = ["__test__.py"],
dep_group = "dev",
main = "__test__.py",
python_version = "3.11",
deps = ["@pypi_multi//project/alpha:cowsay"],
)

py_test(
name = "beta_qualified",
srcs = ["__test__.py"],
dep_group = "beta",
main = "__test__.py",
python_version = "3.11",
deps = ["@pypi_multi//project/beta:cowsay"],
)

py_test(
name = "gamma_qualified",
srcs = ["__test__.py"],
dep_group = "gamma",
main = "__test__.py",
python_version = "3.11",
deps = ["@pypi_multi//project/gamma:cowsay"],
)
12 changes: 12 additions & 0 deletions e2e/cases/single-project-hub/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,15 @@ py_test(
"@pypi_single//cowsay",
],
)

# Verifies @hub//project/<stamp>:<pkg> resolves correctly.
py_test(
name = "single_project_hub_qualified_label",
srcs = ["__test__.py"],
dep_group = "single_project_hub",
main = "__test__.py",
python_version = "3.11",
deps = [
"@pypi_single//project/single_project_hub:cowsay",
],
)
2 changes: 0 additions & 2 deletions e2e/cases/snapshots/pypi_multi.cowsay.BUILD.bazel

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

2 changes: 0 additions & 2 deletions e2e/cases/snapshots/pypi_single.cowsay.BUILD.bazel

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

Loading
Loading