Repository navigation
feat: make Python version project scoping opt-in #4377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Spikhalskiy
wants to merge
6
commits into
bazel-contrib:main
Choose a base branch
from
Spikhalskiy:codex/python-project-scope
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
0e377c5
feat: make Python version project scoping opt-in
Spikhalskiy c24af67
docs: explain Python version project alias
Spikhalskiy d727f16
test: drop redundant SCL dialect flag from project scope test
Spikhalskiy d4a32bc
docs: clarify Python version project scope requirements
Spikhalskiy 9f54ff7
fix: don't require a root PROJECT.scl outside project scope
Spikhalskiy 41e7294
Merge branch 'main' into codex/python-project-scope
aignas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| # Python version project scoping | ||
|
|
||
| A Python binding and its build tools need the selected interpreter version; a | ||
| C++ or Rust library underneath the binding usually does not. Bazel's project | ||
| scope lets the root module name the packages that own the | ||
| `//python/config_settings:python_version` flag. Targets outside them see the | ||
| version given on the command line, so binaries transitioned to different Python | ||
| versions share one configuration of their Python-independent native | ||
| dependencies. | ||
|
|
||
| Project scope is off by default, and only the root module can enable it. Tags in | ||
| dependency modules are ignored, so a library can use it in its own builds | ||
| without imposing a policy on its consumers. | ||
|
|
||
| ```starlark | ||
| # MODULE.bazel | ||
| config = use_extension("@rules_python//python/extensions:config.bzl", "config") | ||
| config.python_version_scope(scope = "project") | ||
| ``` | ||
|
|
||
| `scope` accepts: | ||
|
|
||
| * `"universal"` (the default): the version reaches every target, including exec | ||
| tools such as the precompiler. | ||
| * `"project"`: the root module's `//:PROJECT.scl` decides which targets keep the | ||
| version. | ||
|
|
||
| Project scope requires Bazel 9.2 or later. Older releases drop project-scoped | ||
| flags on exec transitions, so even owned exec tools would lose the version | ||
| ([bazelbuild/bazel#28320](https://github.com/bazelbuild/bazel/issues/28320)). | ||
| The extension fails on older versions. | ||
|
|
||
| ## Ownership | ||
|
|
||
| Under project scope, `//python/config_settings:python_version` is an alias to | ||
| a flag in rules_python's generated configuration repository. That repository's | ||
| `PROJECT.scl` forwards to the root module's `//:PROJECT.scl` with Bazel's | ||
| `actual` field, which may itself redirect with `actual`. Without the tag nothing | ||
| forwards there, so other builds don't need a root `PROJECT.scl`. List every | ||
| package that must see the selected version in `active_directories`: | ||
|
|
||
| ```starlark | ||
| # PROJECT.scl | ||
| project = { | ||
| "active_directories": { | ||
| "default": [ | ||
| "//bindings", | ||
| "@@rules_python+", | ||
| ], | ||
| }, | ||
|
Comment on lines
+45
to
+50
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about repositories that are created due to the |
||
| } | ||
| ``` | ||
|
|
||
| Repositories are listed by canonical name. `bazel mod dump_repo_mapping ""` | ||
| prints the canonical names visible to the root module. | ||
|
|
||
| The example owns only `//bindings` and rules_python itself. A real project must | ||
| also own every other Python-sensitive target: interpreter and header | ||
| repositories, toolchains, version-sensitive generators, and any alias or | ||
| wrapper in between. Every dependency path from a transitioned target must stay | ||
| inside the owned packages until it reaches Python-independent code. Owned exec | ||
| tools keep the selected version. Bazel does not infer ownership from | ||
| dependencies and cannot detect an omitted consumer: such a target sees the | ||
| command-line version and can select the wrong interpreter. | ||
|
|
||
| If you also use aspect_rules_py, its terminals set this flag as well as their | ||
| own `@aspect_rules_py//py:python_version`. Scope both flags and own both | ||
| rulesets' repositories, or native dependencies still get one configuration per | ||
| Python version. | ||
|
|
||
| ## Command-line baseline | ||
|
|
||
| Pass a fixed `--@rules_python//python/config_settings:python_version=<version>` | ||
| and let binaries select their versions with transitions, for example through | ||
| `python_version` attributes. Outside the owned packages, Bazel restores that | ||
| command-line value, not the flag's declared default. Invocations with different | ||
| command-line versions therefore still build native dependencies in different | ||
| configurations. | ||
|
|
||
| ## Limitations | ||
|
|
||
| Only the Python version flag is scoped. ABI, platform, free-threading, | ||
| build-script environment, and other settings are unaffected. | ||
|
|
||
| See Bazel's | ||
| [project-scoped flags example](https://github.com/gregestren/snippets/tree/master/project_scoped_flags) | ||
| and the [configurability roadmap](https://bazel.build/about/roadmap#configurability). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| The `config.python_version_scope(scope = "project")` extension tag lets the root module opt into Python version project scoping using its `//:PROJECT.scl`. The default remains universal. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| exports_files([ | ||
| "PROJECT.scl", | ||
| "probe.bzl", | ||
| ]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| module(name = "python_version_scope_test") | ||
|
|
||
| bazel_dep(name = "rules_python", version = "0.0.0") | ||
| local_path_override( | ||
| module_name = "rules_python", | ||
| path = "../../..", | ||
| ) | ||
|
|
||
| config = use_extension("@rules_python//python/extensions:config.bzl", "config") | ||
| config.python_version_scope(scope = "project") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| """Own bindings and the flag definition; leave the native core outside.""" | ||
|
|
||
| project = {"active_directories": {"default": ["//bindings", "@@rules_python+"]}} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Workspace marker required by the Bazel integration test harness. |
52 changes: 52 additions & 0 deletions
52
tests/integration/python_version_scope/bindings/BUILD.bazel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| load("//:probe.bzl", "check", "probe") | ||
|
|
||
| config_setting( | ||
| name = "py311", | ||
| flag_values = {"@rules_python//python/config_settings:python_version": "3.11"}, | ||
| ) | ||
|
|
||
| config_setting( | ||
| name = "py312", | ||
| flag_values = {"@rules_python//python/config_settings:python_version": "3.12"}, | ||
| ) | ||
|
|
||
| probe( | ||
| name = "tool", | ||
| value = select({ | ||
| ":py310": "3.10", | ||
| ":py311": "3.11", | ||
| ":py312": "3.12", | ||
| "//conditions:default": "unexpected", | ||
| }), | ||
| ) | ||
|
|
||
| probe( | ||
| name = "binding", | ||
| tool = ":tool", | ||
| value = select({ | ||
| ":py310": "3.10", | ||
| ":py311": "3.11", | ||
| ":py312": "3.12", | ||
| "//conditions:default": "unexpected", | ||
| }), | ||
| deps = ["//native:core"], | ||
| ) | ||
|
|
||
| check( | ||
| name = "check311", | ||
| native_version = "3.10", | ||
| target = ":binding", | ||
| version = "3.11", | ||
| ) | ||
|
|
||
| check( | ||
| name = "check312", | ||
| native_version = "3.10", | ||
| target = ":binding", | ||
| version = "3.12", | ||
| ) | ||
|
|
||
| config_setting( | ||
| name = "py310", | ||
| flag_values = {"@rules_python//python/config_settings:python_version": "3.10"}, | ||
| ) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"such as the precompiler" -- is the precompiler ending up in multiple configs one of the motivations for this PR?