Expose `PyRuntimeInfo` from `py_binary` and `py_test` rules
This allows targets that depend on an executable Python target to see what runtime was chosen from the toolchain. The returned `PyRuntimeInfo` provider is the same type of value as what's returned by the `py_runtime` rule.
For example, the path to the system interpreter (if not using an in-workspace interpreter) can be accessed from a dependency as `mydep[PyRuntimeInfo].interpreter_path`. The Python major version for a target can also indirectly be obtained from the version of its runtime, as `mydep[PyRuntimeInfo].python_version`.
Fixes #7805.
RELNOTES: None
PiperOrigin-RevId: 254212408
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java b/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java
index 0c31dbf..31f13ef 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java
@@ -825,6 +825,7 @@
public void addCommonTransitiveInfoProviders(
RuleConfiguredTargetBuilder builder, NestedSet<Artifact> filesToBuild) {
+ // Add PyInfo and/or legacy "py" struct provider.
boolean createLegacyPyProvider =
!ruleContext.getFragment(PythonConfiguration.class).disallowLegacyPyProvider();
PyProviderUtils.builder(createLegacyPyProvider)
@@ -835,6 +836,11 @@
.setHasPy3OnlySources(hasPy3OnlySources)
.buildAndAddToTarget(builder);
+ // Add PyRuntimeInfo if this is an executable rule.
+ if (runtimeFromToolchain != null) {
+ builder.addNativeDeclaredProvider(runtimeFromToolchain);
+ }
+
builder
.addNativeDeclaredProvider(
InstrumentedFilesCollector.collect(
diff --git a/src/test/java/com/google/devtools/build/lib/rules/python/PyExecutableConfiguredTargetTestBase.java b/src/test/java/com/google/devtools/build/lib/rules/python/PyExecutableConfiguredTargetTestBase.java
index bf14b24..a5d3092 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/python/PyExecutableConfiguredTargetTestBase.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/python/PyExecutableConfiguredTargetTestBase.java
@@ -130,6 +130,18 @@
}
@Test
+ public void pyRuntimeInfoIsPresent() throws Exception {
+ useConfiguration("--incompatible_use_python_toolchains=true");
+ scratch.file(
+ "pkg/BUILD", //
+ ruleName + "(",
+ " name = 'foo',",
+ " srcs = [':foo.py'],",
+ ")");
+ assertThat(getConfiguredTarget("//pkg:foo").get(PyRuntimeInfo.PROVIDER)).isNotNull();
+ }
+
+ @Test
public void oldVersionAttr_UnknownValue() throws Exception {
useConfiguration("--incompatible_remove_old_python_version_api=false");
checkError(
diff --git a/src/test/java/com/google/devtools/build/lib/rules/python/PyLibraryConfiguredTargetTest.java b/src/test/java/com/google/devtools/build/lib/rules/python/PyLibraryConfiguredTargetTest.java
index 358c9b3..c4b15e4 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/python/PyLibraryConfiguredTargetTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/python/PyLibraryConfiguredTargetTest.java
@@ -34,6 +34,18 @@
}
@Test
+ public void pyRuntimeInfoIsNotPresent() throws Exception {
+ useConfiguration("--incompatible_use_python_toolchains=true");
+ scratch.file(
+ "pkg/BUILD", //
+ "py_library(",
+ " name = 'foo',",
+ " srcs = [':foo.py'],",
+ ")");
+ assertThat(getConfiguredTarget("//pkg:foo").get(PyRuntimeInfo.PROVIDER)).isNull();
+ }
+
+ @Test
public void canBuildWithIncompatibleSrcsVersionUnderNewSemantics() throws Exception {
// See PyBaseConfiguredTargetTestBase for the analogous test under the old semantics, which
// applies not just to py_library but also to py_binary and py_test.