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.