tests: move py_reconfig rules to their own file (#2900)

The py_reconfig code is pretty large, so move it to its own file. It's
also be easier
to find in its own file rather that part of something named after "shell
testing".
diff --git a/tests/bootstrap_impls/BUILD.bazel b/tests/bootstrap_impls/BUILD.bazel
index 28a0d21..b669da5 100644
--- a/tests/bootstrap_impls/BUILD.bazel
+++ b/tests/bootstrap_impls/BUILD.bazel
@@ -13,7 +13,8 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-load("//tests/support:sh_py_run_test.bzl", "py_reconfig_binary", "py_reconfig_test", "sh_py_run_test")
+load("//tests/support:py_reconfig.bzl", "py_reconfig_binary", "py_reconfig_test")
+load("//tests/support:sh_py_run_test.bzl", "sh_py_run_test")
 load("//tests/support:support.bzl", "SUPPORTS_BOOTSTRAP_SCRIPT")
 load(":venv_relative_path_tests.bzl", "relative_path_test_suite")
 
diff --git a/tests/bootstrap_impls/a/b/c/BUILD.bazel b/tests/bootstrap_impls/a/b/c/BUILD.bazel
index 8ffcbcd..1659ef2 100644
--- a/tests/bootstrap_impls/a/b/c/BUILD.bazel
+++ b/tests/bootstrap_impls/a/b/c/BUILD.bazel
@@ -1,5 +1,5 @@
 load("//python/private:util.bzl", "IS_BAZEL_7_OR_HIGHER")  # buildifier: disable=bzl-visibility
-load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test")
+load("//tests/support:py_reconfig.bzl", "py_reconfig_test")
 
 _SUPPORTS_BOOTSTRAP_SCRIPT = select({
     "@platforms//os:windows": ["@platforms//:incompatible"],
diff --git a/tests/interpreter/interpreter_tests.bzl b/tests/interpreter/interpreter_tests.bzl
index ad94f43..3c5882a 100644
--- a/tests/interpreter/interpreter_tests.bzl
+++ b/tests/interpreter/interpreter_tests.bzl
@@ -14,7 +14,7 @@
 
 """This file contains helpers for testing the interpreter rule."""
 
-load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test")
+load("//tests/support:py_reconfig.bzl", "py_reconfig_test")
 
 # The versions of Python that we want to run the interpreter tests against.
 PYTHON_VERSIONS_TO_TEST = (
diff --git a/tests/no_unsafe_paths/BUILD.bazel b/tests/no_unsafe_paths/BUILD.bazel
index f12d1c9..c9a681d 100644
--- a/tests/no_unsafe_paths/BUILD.bazel
+++ b/tests/no_unsafe_paths/BUILD.bazel
@@ -11,7 +11,7 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test")
+load("//tests/support:py_reconfig.bzl", "py_reconfig_test")
 load("//tests/support:support.bzl", "SUPPORTS_BOOTSTRAP_SCRIPT")
 
 py_reconfig_test(
diff --git a/tests/packaging/BUILD.bazel b/tests/packaging/BUILD.bazel
index bb12269..d88a593 100644
--- a/tests/packaging/BUILD.bazel
+++ b/tests/packaging/BUILD.bazel
@@ -14,7 +14,7 @@
 
 load("@bazel_skylib//rules:build_test.bzl", "build_test")
 load("@rules_pkg//pkg:tar.bzl", "pkg_tar")
-load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test")
+load("//tests/support:py_reconfig.bzl", "py_reconfig_test")
 load("//tests/support:support.bzl", "SUPPORTS_BOOTSTRAP_SCRIPT")
 
 build_test(
diff --git a/tests/repl/BUILD.bazel b/tests/repl/BUILD.bazel
index 62c7377..b3986cc 100644
--- a/tests/repl/BUILD.bazel
+++ b/tests/repl/BUILD.bazel
@@ -1,5 +1,5 @@
 load("//python:py_library.bzl", "py_library")
-load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test")
+load("//tests/support:py_reconfig.bzl", "py_reconfig_test")
 
 # A library that adds a special import path only when this is specified as a
 # dependency. This makes it easy for a dependency to have this import path
diff --git a/tests/runtime_env_toolchain/BUILD.bazel b/tests/runtime_env_toolchain/BUILD.bazel
index ad2bd4e..2f82d20 100644
--- a/tests/runtime_env_toolchain/BUILD.bazel
+++ b/tests/runtime_env_toolchain/BUILD.bazel
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 load("@rules_python_runtime_env_tc_info//:info.bzl", "PYTHON_VERSION")
-load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test")
+load("//tests/support:py_reconfig.bzl", "py_reconfig_test")
 load("//tests/support:support.bzl", "CC_TOOLCHAIN")
 load(":runtime_env_toolchain_tests.bzl", "runtime_env_toolchain_test_suite")
 
diff --git a/tests/support/py_reconfig.bzl b/tests/support/py_reconfig.bzl
new file mode 100644
index 0000000..b33f679
--- /dev/null
+++ b/tests/support/py_reconfig.bzl
@@ -0,0 +1,101 @@
+# Copyright 2024 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Run a py_binary/py_test with altered config settings.
+
+This facilitates verify running binaries with different configuration settings
+without the overhead of a bazel-in-bazel integration test.
+"""
+
+load("//python/private:attr_builders.bzl", "attrb")  # buildifier: disable=bzl-visibility
+load("//python/private:py_binary_macro.bzl", "py_binary_macro")  # buildifier: disable=bzl-visibility
+load("//python/private:py_binary_rule.bzl", "create_py_binary_rule_builder")  # buildifier: disable=bzl-visibility
+load("//python/private:py_test_macro.bzl", "py_test_macro")  # buildifier: disable=bzl-visibility
+load("//python/private:py_test_rule.bzl", "create_py_test_rule_builder")  # buildifier: disable=bzl-visibility
+load("//tests/support:support.bzl", "VISIBLE_FOR_TESTING")
+
+def _perform_transition_impl(input_settings, attr, base_impl):
+    settings = {k: input_settings[k] for k in _RECONFIG_INHERITED_OUTPUTS if k in input_settings}
+    settings.update(base_impl(input_settings, attr))
+
+    settings[VISIBLE_FOR_TESTING] = True
+    settings["//command_line_option:build_python_zip"] = attr.build_python_zip
+    if attr.bootstrap_impl:
+        settings["//python/config_settings:bootstrap_impl"] = attr.bootstrap_impl
+    if attr.extra_toolchains:
+        settings["//command_line_option:extra_toolchains"] = attr.extra_toolchains
+    if attr.python_src:
+        settings["//python/bin:python_src"] = attr.python_src
+    if attr.repl_dep:
+        settings["//python/bin:repl_dep"] = attr.repl_dep
+    if attr.venvs_use_declare_symlink:
+        settings["//python/config_settings:venvs_use_declare_symlink"] = attr.venvs_use_declare_symlink
+    if attr.venvs_site_packages:
+        settings["//python/config_settings:venvs_site_packages"] = attr.venvs_site_packages
+    return settings
+
+_RECONFIG_INPUTS = [
+    "//python/config_settings:bootstrap_impl",
+    "//python/bin:python_src",
+    "//python/bin:repl_dep",
+    "//command_line_option:extra_toolchains",
+    "//python/config_settings:venvs_use_declare_symlink",
+    "//python/config_settings:venvs_site_packages",
+]
+_RECONFIG_OUTPUTS = _RECONFIG_INPUTS + [
+    "//command_line_option:build_python_zip",
+    VISIBLE_FOR_TESTING,
+]
+_RECONFIG_INHERITED_OUTPUTS = [v for v in _RECONFIG_OUTPUTS if v in _RECONFIG_INPUTS]
+
+_RECONFIG_ATTRS = {
+    "bootstrap_impl": attrb.String(),
+    "build_python_zip": attrb.String(default = "auto"),
+    "extra_toolchains": attrb.StringList(
+        doc = """
+Value for the --extra_toolchains flag.
+
+NOTE: You'll likely have to also specify //tests/support/cc_toolchains:all (or some CC toolchain)
+to make the RBE presubmits happy, which disable auto-detection of a CC
+toolchain.
+""",
+    ),
+    "python_src": attrb.Label(),
+    "repl_dep": attrb.Label(),
+    "venvs_site_packages": attrb.String(),
+    "venvs_use_declare_symlink": attrb.String(),
+}
+
+def _create_reconfig_rule(builder):
+    builder.attrs.update(_RECONFIG_ATTRS)
+
+    base_cfg_impl = builder.cfg.implementation()
+    builder.cfg.set_implementation(lambda *args: _perform_transition_impl(base_impl = base_cfg_impl, *args))
+    builder.cfg.update_inputs(_RECONFIG_INPUTS)
+    builder.cfg.update_outputs(_RECONFIG_OUTPUTS)
+    return builder.build()
+
+_py_reconfig_binary = _create_reconfig_rule(create_py_binary_rule_builder())
+
+_py_reconfig_test = _create_reconfig_rule(create_py_test_rule_builder())
+
+def py_reconfig_test(**kwargs):
+    """Create a py_test with customized build settings for testing.
+
+    Args:
+        **kwargs: kwargs to pass along to _py_reconfig_test.
+    """
+    py_test_macro(_py_reconfig_test, **kwargs)
+
+def py_reconfig_binary(**kwargs):
+    py_binary_macro(_py_reconfig_binary, **kwargs)
diff --git a/tests/support/sh_py_run_test.bzl b/tests/support/sh_py_run_test.bzl
index f6ebc50..1a61de9 100644
--- a/tests/support/sh_py_run_test.bzl
+++ b/tests/support/sh_py_run_test.bzl
@@ -13,94 +13,14 @@
 # limitations under the License.
 """Run a py_binary with altered config settings in an sh_test.
 
-This facilitates verify running binaries with different configuration settings
-without the overhead of a bazel-in-bazel integration test.
+This facilitates verify running binaries with different outer environmental
+settings and verifying their output without the overhead of a bazel-in-bazel
+integration test.
 """
 
 load("@rules_shell//shell:sh_test.bzl", "sh_test")
-load("//python/private:attr_builders.bzl", "attrb")  # buildifier: disable=bzl-visibility
-load("//python/private:py_binary_macro.bzl", "py_binary_macro")  # buildifier: disable=bzl-visibility
-load("//python/private:py_binary_rule.bzl", "create_py_binary_rule_builder")  # buildifier: disable=bzl-visibility
-load("//python/private:py_test_macro.bzl", "py_test_macro")  # buildifier: disable=bzl-visibility
-load("//python/private:py_test_rule.bzl", "create_py_test_rule_builder")  # buildifier: disable=bzl-visibility
 load("//python/private:toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE")  # buildifier: disable=bzl-visibility
-load("//tests/support:support.bzl", "VISIBLE_FOR_TESTING")
-
-def _perform_transition_impl(input_settings, attr, base_impl):
-    settings = {k: input_settings[k] for k in _RECONFIG_INHERITED_OUTPUTS if k in input_settings}
-    settings.update(base_impl(input_settings, attr))
-
-    settings[VISIBLE_FOR_TESTING] = True
-    settings["//command_line_option:build_python_zip"] = attr.build_python_zip
-    if attr.bootstrap_impl:
-        settings["//python/config_settings:bootstrap_impl"] = attr.bootstrap_impl
-    if attr.extra_toolchains:
-        settings["//command_line_option:extra_toolchains"] = attr.extra_toolchains
-    if attr.python_src:
-        settings["//python/bin:python_src"] = attr.python_src
-    if attr.repl_dep:
-        settings["//python/bin:repl_dep"] = attr.repl_dep
-    if attr.venvs_use_declare_symlink:
-        settings["//python/config_settings:venvs_use_declare_symlink"] = attr.venvs_use_declare_symlink
-    if attr.venvs_site_packages:
-        settings["//python/config_settings:venvs_site_packages"] = attr.venvs_site_packages
-    return settings
-
-_RECONFIG_INPUTS = [
-    "//python/config_settings:bootstrap_impl",
-    "//python/bin:python_src",
-    "//python/bin:repl_dep",
-    "//command_line_option:extra_toolchains",
-    "//python/config_settings:venvs_use_declare_symlink",
-    "//python/config_settings:venvs_site_packages",
-]
-_RECONFIG_OUTPUTS = _RECONFIG_INPUTS + [
-    "//command_line_option:build_python_zip",
-    VISIBLE_FOR_TESTING,
-]
-_RECONFIG_INHERITED_OUTPUTS = [v for v in _RECONFIG_OUTPUTS if v in _RECONFIG_INPUTS]
-
-_RECONFIG_ATTRS = {
-    "bootstrap_impl": attrb.String(),
-    "build_python_zip": attrb.String(default = "auto"),
-    "extra_toolchains": attrb.StringList(
-        doc = """
-Value for the --extra_toolchains flag.
-
-NOTE: You'll likely have to also specify //tests/support/cc_toolchains:all (or some CC toolchain)
-to make the RBE presubmits happy, which disable auto-detection of a CC
-toolchain.
-""",
-    ),
-    "python_src": attrb.Label(),
-    "repl_dep": attrb.Label(),
-    "venvs_site_packages": attrb.String(),
-    "venvs_use_declare_symlink": attrb.String(),
-}
-
-def _create_reconfig_rule(builder):
-    builder.attrs.update(_RECONFIG_ATTRS)
-
-    base_cfg_impl = builder.cfg.implementation()
-    builder.cfg.set_implementation(lambda *args: _perform_transition_impl(base_impl = base_cfg_impl, *args))
-    builder.cfg.update_inputs(_RECONFIG_INPUTS)
-    builder.cfg.update_outputs(_RECONFIG_OUTPUTS)
-    return builder.build()
-
-_py_reconfig_binary = _create_reconfig_rule(create_py_binary_rule_builder())
-
-_py_reconfig_test = _create_reconfig_rule(create_py_test_rule_builder())
-
-def py_reconfig_test(**kwargs):
-    """Create a py_test with customized build settings for testing.
-
-    Args:
-        **kwargs: kwargs to pass along to _py_reconfig_test.
-    """
-    py_test_macro(_py_reconfig_test, **kwargs)
-
-def py_reconfig_binary(**kwargs):
-    py_binary_macro(_py_reconfig_binary, **kwargs)
+load(":py_reconfig.bzl", "py_reconfig_binary")
 
 def sh_py_run_test(*, name, sh_src, py_src, **kwargs):
     """Run a py_binary within a sh_test.
diff --git a/tests/toolchains/defs.bzl b/tests/toolchains/defs.bzl
index fbb7082..a883b0a 100644
--- a/tests/toolchains/defs.bzl
+++ b/tests/toolchains/defs.bzl
@@ -15,7 +15,7 @@
 ""
 
 load("//python:versions.bzl", "PLATFORMS", "TOOL_VERSIONS")
-load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test")
+load("//tests/support:py_reconfig.bzl", "py_reconfig_test")
 
 def define_toolchain_tests(name):
     """Define the toolchain tests.
diff --git a/tests/uv/lock/lock_tests.bzl b/tests/uv/lock/lock_tests.bzl
index 35c7c19..1eb5b1d 100644
--- a/tests/uv/lock/lock_tests.bzl
+++ b/tests/uv/lock/lock_tests.bzl
@@ -16,7 +16,7 @@
 
 load("@bazel_skylib//rules:native_binary.bzl", "native_test")
 load("//python/uv:lock.bzl", "lock")
-load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test")
+load("//tests/support:py_reconfig.bzl", "py_reconfig_test")
 
 def lock_test_suite(name):
     """The test suite with various lock-related integration tests
diff --git a/tests/venv_site_packages_libs/BUILD.bazel b/tests/venv_site_packages_libs/BUILD.bazel
index 5d02708..1f48331 100644
--- a/tests/venv_site_packages_libs/BUILD.bazel
+++ b/tests/venv_site_packages_libs/BUILD.bazel
@@ -1,4 +1,4 @@
-load("//tests/support:sh_py_run_test.bzl", "py_reconfig_test")
+load("//tests/support:py_reconfig.bzl", "py_reconfig_test")
 load("//tests/support:support.bzl", "SUPPORTS_BOOTSTRAP_SCRIPT")
 
 py_reconfig_test(