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
6 changes: 6 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ bazel_dep(name = "bazel_skylib", version = "1.8.2")
bazel_dep(name = "package_metadata", version = "0.0.7")
bazel_dep(name = "platforms", version = "0.0.11")
bazel_dep(name = "rules_cc", version = "0.1.5")
bazel_dep(name = "toml.bzl", version = "0.4.0")

# Those are loaded only when using py_proto_library
# Use py_proto_library directly from protobuf repository
Expand Down Expand Up @@ -51,9 +52,13 @@ python.defaults(
python.toolchain(
python_version = "3.11",
)
python.toolchain(
python_version = "3.14",
)
use_repo(
python,
"python_3_11",
"python_3_14",
"pythons_hub",
python = "python_versions",
)
Expand Down Expand Up @@ -182,6 +187,7 @@ dev_pip = use_extension(
"{os}_{arch}",
"{os}_{arch}_freethreaded",
],
uv_lock = "//docs:uv.lock",
)
for python_version in [
"3.9",
Expand Down
10 changes: 10 additions & 0 deletions docs/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,13 @@ lock(
python_version = "3.9",
visibility = ["//:__subpackages__"],
)

# Run bazel run //docs:uv_lock.update
lock(
name = "uv_lock",
srcs = ["pyproject.toml"],
out = "uv.lock",
lock_format = "uv_lock",
python_version = "3.9",
visibility = ["//:__subpackages__"],
)
1,139 changes: 1,139 additions & 0 deletions docs/uv.lock

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
For all of the steps do small changes and ensure that each commit is compliant with:
* buildifier and buildifier-lint pre-commit hooks
* isort and black pre-commit hooks
* all of the bazel tests pass
* Spin a sub-agent with a different model and monitor code quality to ensure it is high.
* Ensure that comments are minimal and not too verbose.
* Once done with each section, squash the commit and have a description that is good for a PR.

Already done:
* Then implement support for `uv.lock` as follows:
1. Add support to generate `uv.lock` from the `lock` rule in rules_python.
2. Add unit/integration tests for this feature.
3. Ensure that both actions to update the lock file work.
4. Use the docs/pyproject.toml for integration testing.
5. Support updating requirements and the uv.lock file together.

* Look at the architecture in one of the last comments on https://github.com/bazel-contrib/rules_python/issues/2787.
1. Take the uv.lock to json converter from a PR that is associated with the linked issue
2. Include the integration and unit tests for the converter.

In progress bellow:

Then add the uv.lock support to pip.parse by accepting the lock file.
1. parse_requirements should accept an extra parameter called uv lock.
2. If both requirements and uv.lock is passed and we are running from inside rules_python, run in a
debug mode where we test that uv.lock and requirements are consistent. Ensure that we pick used
extras from the uv.lock file. If uv.lock is provided, use only the uv.lock file for inputs and
use requirements files as a test mechanism - ensure that parsing uv.lock and using it to return
the output of parse_requirements function results in the same as what we would have returned with
just using the requirements files.
3. Use uv.lock packages, extras and the file shas to get the equivalent information to what is in
requirements.txt.
4. Iterate until the packages read from uv.lock file are the same as the ones that we retrieve from
the requirements files.
5. Along the way add integration tests and keep running full `//tests/pypi/...` suite between the
changes.
6. Run `bazel build //docs:sphinx-build` as an extra verification.
7. Use `fail` if the invariants are broken.

Then look online on the uv-lock file repository and add test cases to ensure that we are correctly
handling in requirements and lock files the following cases:
1. Overrides of whl files where we need to fetch the wheel from a particular location/index
2. All of the cases where requirements files use direct URL references or VCS references.

Then implement a Starlark uv.lock file parser that would be good enough for parsing any file in the
test suite of uv.lock file. Use the `python` version that we have introduced earlier for the
expected result.
1. Base the parser on https://github.com/jvolkman/toml.bzl
2. Copy the all of the sample uv.lock files from the upstream `astral-sh/uv` test-suite.
3. Ensure that we are passing test-suite in there.

Then implement a wrapper around the `get_index` function to get the wheel sources from the uv.lock
file instead of calling the SimpleAPI.
1. If there are no URLs in the uv.lock file, fallback to the SimpleAPI code.

For all of this work, the output signature of the parse_requirements should stay the same.

## PR Review Cycle Instructions (for AI agents)

When addressing PR review comments:
1. Read all review comments (inline and summary) using `gh api` and `gh pr view`
2. Address each issue with appropriate code fixes
3. Add tests matching existing coverage patterns
4. Run all tests (`bazel test --config=fast-tests //tests/...`), build docs (`bazel build //docs:sphinx-build`)
5. Commit, push, request gemini review
6. Check Buildkite CI results (except RBE failures); fix any failures
7. Cycle until all green

## Done
1. First review cycle:
- tomllib: try/except fallback to tomli for Python <3.11
- json_serializer: add datetime.date and datetime.time support
- all_platforms: use sorted(platforms.keys()) fallback
- $PWD/ path: check if python_path is absolute
- provides-extras: comment explaining inclusion rationale
- extra_pip_args: pass through to _parse_uv_lock_json
- Added uv_lock test coverage: multiple packages, extra_pip_args, multi-os
2. Python 3.14 toolchain in MODULE.bazel for toml.bzl usage
3. Integration test in tests/integration/uv_lock/
4. Dead code removed (uv_pypi test data), tomli fallback restored
5. toml2json removed in favor of pure Starlark toml.bzl decoder
6. uv_lock.bzl and toml2json tool deleted
10 changes: 9 additions & 1 deletion python/private/pypi/extension.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
load("@pythons_hub//:interpreters.bzl", "INTERPRETER_LABELS")
load("@pythons_hub//:versions.bzl", "MINOR_MAPPING")
load("@rules_python_internal//:rules_python_config.bzl", rp_config = "config")
load("@toml.bzl", "toml")
load("//python/private:auth.bzl", "AUTH_ATTRS")
load("//python/private:normalize_name.bzl", "normalize_name")
load("//python/private:repo_utils.bzl", "repo_utils")
Expand All @@ -34,7 +35,7 @@ def _whl_mods_impl(whl_mods_dict):
"""Implementation of the pip.whl_mods tag class.

This creates the JSON files used to modify the creation of different wheels.
"""
"""
for hub_name, whl_maps in whl_mods_dict.items():
whl_mods = {}

Expand Down Expand Up @@ -251,6 +252,7 @@ def build_config(
for name, values in defaults["platforms"].items()
},
enable_pipstar_extract = enable_pipstar_extract,
toml_decode = toml.decode,
)

def parse_modules(
Expand Down Expand Up @@ -818,6 +820,12 @@ a string `"{os}_{arch}"` as the value here. You could also use `"{os}_{arch}_fre

:::{versionadded} 1.8.0
:::
""",
),
"uv_lock": attr.label(
doc = """\
(label, optional): A label pointing to the uv.lock file. If provided,
the uv.lock file will be used as the primary source for package metadata.
""",
),
"whl_modifications": attr.label_keyed_string_dict(
Expand Down
2 changes: 2 additions & 0 deletions python/private/pypi/hub_builder.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -506,10 +506,12 @@ def _create_whl_repos(
),
logger = logger,
),
uv_lock = pip_attr.uv_lock,
platforms = platforms,
extra_pip_args = pip_attr.extra_pip_args,
get_index_urls = self._get_index_urls.get(pip_attr.python_version),
evaluate_markers = _evaluate_markers(self, pip_attr),
toml_decode = getattr(self._config, "toml_decode", None),
logger = logger,
)

Expand Down
Loading