Skip to content

feat(pypi): add WORKSPACE uv_lock macro - #4374

Open
rickeylev wants to merge 10 commits into
bazel-contrib:mainfrom
rickeylev:use_uv_lock_rule
Open

rickeylev wants to merge 10 commits into
bazel-contrib:mainfrom
rickeylev:use_uv_lock_rule

Conversation

@rickeylev

@rickeylev rickeylev commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Bzlmod supports pip.parse(uv_lock = ...) to install PyPI dependencies
from a universal uv.lock file, but WORKSPACE setups still required
platform- or version-specific requirements.txt files for pip_parse.
Maintaining separate lockfile formats across MODULE.bazel and
WORKSPACE duplicated lockfile generation targets and complicated
dependency updates.

Add the uv_lock macro to //python:pip.bzl for WORKSPACE builds.
It decodes uv.lock with @toml.bzl, parses packages via
parse_requirements, and renders the PyPI hub (and multi-version spoke
repositories when python_interpreter_target is a dict) using
pip_repository with Bazel's downloader (whl_library urls,
filename, and integrity) for prebuilt wheels and pip fallback for
sdists and git sources.

Bzlmod supports `pip.parse(uv_lock = ...)` to install PyPI dependencies
from a universal `uv.lock` file, but `WORKSPACE` setups still required
platform- or version-specific `requirements.txt` files for `pip_parse`.
Maintaining separate lockfile formats across `MODULE.bazel` and
`WORKSPACE` duplicated lockfile generation targets and complicated
dependency updates.

Add the `uv_lock` macro to `//python:pip.bzl` for `WORKSPACE` builds.
It decodes `uv.lock` with `@toml.bzl`, converts it to requirements
format via `uv_lock_to_requirements`, and renders the PyPI hub (and
multi-version spoke repositories when `python_interpreter_target` is a
dict) using the shared `pip_repository` rendering logic. Migrate
repository, example, and test lockfiles that used `uv` from
`requirements.txt` to unified `uv.lock` files.
@rickeylev

Copy link
Copy Markdown
Collaborator Author

AI Disclosure

  • Oversight level: semi-autonomous — This PR was created by an AI coding agent under the direction and review of @rickeylev.
  • Comment handling: Review comments and questions on this PR are monitored by @rickeylev, who either responds directly or directs and reviews agent-assisted updates before they are posted.

This is an automatically generated agent comment.

@rickeylev

Copy link
Copy Markdown
Collaborator Author

@aignas is core of this implementation for a low-effort workspace macro to handle uv.lock look promising? Just look at the files under python/private/pypi, the uv_lock.bzl and uv_lock_repo.bzl. (everything else is migration noise).

The basic impl is to convert the uv.lock to requirements using the uv_lock_to_requirements helper. It basically just converts uv.lock to requirements, then feeds that into the pip rules and pip hub rules.

With just a year left for workspace, I didn't want to put a ton of effort into e.g. full API compatibility with pip_parse. But enough that we can move our own workspace off it

…on PR

Propagate transitive dependency-edge markers in uv_lock_to_requirements
and move the repository, example, and test lockfile migrations into a
stacked child PR.
@rickeylev rickeylev changed the title feat(pypi): add WORKSPACE uv_lock macro and migrate lockfiles to uv.lock feat(pypi): add WORKSPACE uv_lock macro Oct 29, 2026

@aignas aignas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having this in workspace could also mean that we could at some point implement an automatic cycle dependency algorythm for uv.lock asd friends.

Returns:
A dict of {package_name: [extra1, extra2, ...]} for packages with extras.
"""
extras_map = {}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already similar code in parse_requirements. Maybe these could be refactored into one? The only thing that is different is that in workspace case we are dropping the urls to wheels that we can use bazel downloader to download.

That said I think the proposed approach is good enough, if somebody wants, they can spend tokens to backport some of the pip.parse machinery to make it work in workspace.

@rickeylev rickeylev Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — unified WORKSPACE (extract_url_srcs = False) directly into _parse_uv_lock_json in parse_requirements.bzl and removed uv_lock_to_requirements.bzl in ac7eb904. PTAL!

This is an automatically generated agent comment.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I had it go do this and it went a bit...weird. I seems to have more-or-less copy/pasted uv_lock_to_requirements.bzl into parse_requirements.bzl and the body of it become basically two entirely different implementations. This appears to have all been just so that it could pass along requirements.txt style info to pip underneath.

I've asked it to just skip that and jump straight to using the rctx.download() stuff instead. Lets see if that goes better.

Teach `_parse_uv_lock_json` in `parse_requirements.bzl` to handle
`extract_url_srcs = False` directly so `uv_lock_repo` and
`render_pip_repository` pass `uv.lock` through `parse_requirements`
without round-tripping through a synthetic `requirements.txt` string.
Delete the now-unused `uv_lock_to_requirements.bzl` module and cover the
`WORKSPACE` `extract_url_srcs = False` behavior in
`parse_requirements_tests.bzl`.
@rickeylev
rickeylev marked this pull request as ready for review October 31, 2026 04:57
Extract wheel URLs, filenames, and SRI integrity digests in
render_pip_repository when uv_lock is set so whl_library fetches
prebuilt wheels directly with Bazel's downloader instead of invoking
pip, falling back to pip only for sdists and git sources.
…uv_lock

Resolve uv.lock wheel candidates across all target platforms rather
than only the host platform so generated requirements.bzl files are
byte-for-byte identical across Linux, macOS, and Windows while still
downloading prebuilt wheels via Bazel's downloader.
…uv_lock

Filter out packages that have no matching source for the repository host
platform (unless download_only = True) so all_requirements never
includes platform-specific packages on unsupported hosts.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants