Remove support for implicit py2to3, which never worked in Bazel

See https://github.com/bazelbuild/bazel/issues/1393.

This treats py_*.srcs_version="PY2" the same as "PY2ONLY". Targets which are PY3-compatible should use srcs_version="PY2AND3" instead.

Fixes #1393.

RELNOTES: Treat py_*.srcs_version="PY2" the same as "PY2ONLY".
PiperOrigin-RevId: 407116957
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyRuleClasses.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyRuleClasses.java
index e002946..859784f 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPyRuleClasses.java
@@ -26,7 +26,6 @@
 import com.google.devtools.build.lib.analysis.BaseRuleClasses;
 import com.google.devtools.build.lib.analysis.RuleDefinition;
 import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
-import com.google.devtools.build.lib.analysis.config.HostTransition;
 import com.google.devtools.build.lib.bazel.rules.cpp.BazelCppRuleClasses.CcToolchainRequiringRule;
 import com.google.devtools.build.lib.packages.Attribute.AllowedValueSet;
 import com.google.devtools.build.lib.packages.Attribute.LabelLateBoundDefault;
@@ -129,13 +128,6 @@
               attr("srcs_version", STRING)
                   .value(PythonVersion.DEFAULT_SRCS_VALUE.toString())
                   .allowedValues(new AllowedValueSet(PythonVersion.SRCS_STRINGS)))
-          // do not depend on lib2to3:2to3 rule, because it creates circular dependencies
-          // 2to3 is itself written in Python and depends on many libraries.
-          .add(
-              attr("$python2to3", LABEL)
-                  .cfg(HostTransition.createFactory())
-                  .exec()
-                  .value(env.getToolsLabel("//tools/python:2to3")))
           .setPreferredDependencyPredicate(PyRuleClasses.PYTHON_SOURCE)
           .build();
     }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/BUILD b/src/main/java/com/google/devtools/build/lib/rules/python/BUILD
index 9c044c1..501fcc8 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/BUILD
@@ -18,7 +18,6 @@
         "//src/main/java/com/google/devtools/build/lib:build-request-options",
         "//src/main/java/com/google/devtools/build/lib/actions",
         "//src/main/java/com/google/devtools/build/lib/actions:artifacts",
-        "//src/main/java/com/google/devtools/build/lib/analysis:actions/custom_command_line",
         "//src/main/java/com/google/devtools/build/lib/analysis:analysis_cluster",
         "//src/main/java/com/google/devtools/build/lib/analysis:config/build_configuration",
         "//src/main/java/com/google/devtools/build/lib/analysis:config/build_options",
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 e61b4e0..fd2c41f 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
@@ -58,7 +58,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 import javax.annotation.Nullable;
 import net.starlark.java.eval.EvalException;
@@ -170,13 +169,6 @@
    */
   @Nullable private final PyRuntimeInfo runtimeFromToolchain;
 
-  /**
-   * Symlink map from root-relative paths to 2to3 converted source artifacts.
-   *
-   * <p>Null if no 2to3 conversion is required.
-   */
-  @Nullable private final Map<PathFragment, Artifact> convertedFiles;
-
   // Null if requiresMainFile is false, or the main artifact couldn't be determined.
   @Nullable private Artifact mainArtifact = null;
 
@@ -213,7 +205,6 @@
     this.hasPy2OnlySources = initHasPy2OnlySources(ruleContext, this.sourcesVersion);
     this.hasPy3OnlySources = initHasPy3OnlySources(ruleContext, this.sourcesVersion);
     this.runtimeFromToolchain = initRuntimeFromToolchain(ruleContext, this.version);
-    this.convertedFiles = makeAndInitConvertedFiles(ruleContext, version, this.sourcesVersion);
     validatePythonVersionAttr();
     validateLegacyProviderNotUsedIfDisabled();
   }
@@ -351,12 +342,11 @@
 
   /**
    * Returns true if any of {@code deps} has a py provider with {@code has_py2_only_sources} set, or
-   * this target has a {@code srcs_version} of {@code PY2ONLY}.
+   * this target has a {@code srcs_version} of {@code PY2} or {@code PY2ONLY}.
    */
-  // TODO(#1393): For Bazel, deprecate 2to3 support and treat PY2 the same as PY2ONLY.
   private static boolean initHasPy2OnlySources(
       RuleContext ruleContext, PythonVersion sourcesVersion) {
-    if (sourcesVersion == PythonVersion.PY2ONLY) {
+    if (sourcesVersion == PythonVersion.PY2 || sourcesVersion == PythonVersion.PY2ONLY) {
       return true;
     }
     for (TransitiveInfoCollection dep : ruleContext.getPrerequisites("deps")) {
@@ -511,27 +501,6 @@
   }
 
   /**
-   * If 2to3 conversion is to be done, creates the 2to3 actions and returns the map of converted
-   * files; otherwise returns null.
-   *
-   * <p>May also return null and report a rule error if there is a problem creating an output file
-   * for 2to3 conversion.
-   */
-  // TODO(#1393): 2to3 conversion doesn't work in Bazel and the attempt to invoke it for Bazel
-  // should be removed / factored away into PythonSemantics.
-  @Nullable
-  private static Map<PathFragment, Artifact> makeAndInitConvertedFiles(
-      RuleContext ruleContext, PythonVersion version, PythonVersion sourcesVersion) {
-    if (sourcesVersion == PythonVersion.PY2 && version == PythonVersion.PY3) {
-      Iterable<Artifact> artifacts =
-          ruleContext.getPrerequisiteArtifacts("srcs").filter(PyRuleClasses.PYTHON_SOURCE).list();
-      return PythonUtils.generate2to3Actions(ruleContext, artifacts);
-    } else {
-      return null;
-    }
-  }
-
-  /**
    * Reports an attribute error if {@code python_version} cannot be parsed as {@code PY2}, {@code
    * PY3}, or the sentinel value.
    *
@@ -752,10 +721,6 @@
     return runtimeFromToolchain;
   }
 
-  public Map<PathFragment, Artifact> getConvertedFiles() {
-    return convertedFiles;
-  }
-
   public void initBinary(List<Artifact> srcs) {
     Preconditions.checkNotNull(version);
 
@@ -864,9 +829,7 @@
 
   /** Returns a list of the source artifacts */
   public List<Artifact> getPythonSources() {
-    return convertedFiles != null
-        ? ImmutableList.copyOf(convertedFiles.values())
-        : directPythonSources;
+    return directPythonSources;
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/PyExecutable.java b/src/main/java/com/google/devtools/build/lib/rules/python/PyExecutable.java
index 246cb9d..159f499 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/PyExecutable.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/PyExecutable.java
@@ -149,11 +149,7 @@
             ruleContext.getWorkspaceName(),
             ruleContext.getConfiguration().legacyExternalRunfiles());
     builder.addArtifact(common.getExecutable());
-    if (common.getConvertedFiles() != null) {
-      builder.addSymlinks(common.getConvertedFiles());
-    } else {
-      builder.addTransitiveArtifacts(common.getFilesToBuild());
-    }
+    builder.addTransitiveArtifacts(common.getFilesToBuild());
     semantics.collectDefaultRunfiles(ruleContext, builder);
     builder.add(ruleContext, PythonRunfilesProvider.TO_RUNFILES);
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/PyLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/python/PyLibrary.java
index 8446d4c..3bdaa0a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/PyLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/PyLibrary.java
@@ -58,11 +58,7 @@
 
     Runfiles.Builder runfilesBuilder = new Runfiles.Builder(
         ruleContext.getWorkspaceName(), ruleContext.getConfiguration().legacyExternalRunfiles());
-    if (common.getConvertedFiles() != null) {
-      runfilesBuilder.addSymlinks(common.getConvertedFiles());
-    } else {
-      runfilesBuilder.addTransitiveArtifacts(filesToBuild);
-    }
+    runfilesBuilder.addTransitiveArtifacts(filesToBuild);
     runfilesBuilder.add(ruleContext, PythonRunfilesProvider.TO_RUNFILES);
     runfilesBuilder.addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES);
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/PythonUtils.java b/src/main/java/com/google/devtools/build/lib/rules/python/PythonUtils.java
index 85e8367..1956dbc 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/PythonUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/PythonUtils.java
@@ -14,21 +14,12 @@
 package com.google.devtools.build.lib.rules.python;
 
 import com.google.common.collect.ImmutableSet;
-import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.actions.ArtifactRoot;
-import com.google.devtools.build.lib.analysis.FilesToRunProvider;
-import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.Runfiles;
-import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
-import com.google.devtools.build.lib.analysis.actions.SpawnAction;
 import com.google.devtools.build.lib.util.FileType;
 import com.google.devtools.build.lib.vfs.PathFragment;
-import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Map;
 import java.util.Set;
 import java.util.function.Predicate;
-import javax.annotation.Nullable;
 
 /** Various utility methods for Python support. */
 public final class PythonUtils {
@@ -108,85 +99,4 @@
   private PythonUtils() {
     // This is a utility class, not to be instantiated.
   }
-
-  /**
-   * Get the artifact generated by the 2to3 action.
-   *
-   * <p>There might be conflicts eg. when the input file is generated, but that case is unsupported
-   * because 2to3 is obsolete.
-   *
-   * <p>Returns null and reports a rule error if the output file cannot be created because it is not
-   * underneath the target's package.
-   */
-  @Nullable
-  private static Artifact get2to3OutputArtifact(RuleContext ruleContext, Artifact input) {
-    PathFragment rootRelativePath =
-        input.getOutputDirRelativePath(ruleContext.getConfiguration().isSiblingRepositoryLayout());
-    if (!rootRelativePath.startsWith(ruleContext.getPackageDirectory())) {
-      ruleContext.ruleError(
-          String.format(
-              "cannot perform 2to3 conversion on source file %s from another package",
-              rootRelativePath));
-      return null;
-    }
-    ArtifactRoot root = ruleContext.getGenfilesDirectory();
-    return ruleContext.getDerivedArtifact(rootRelativePath, root);
-  }
-
-  /**
-   * Creates an action for each Python 2 file to convert to Python 3.
-   *
-   * <p>Returns null and reports a rule error if an output file cannot be created.
-   */
-  @Nullable
-  public static Map<PathFragment, Artifact> generate2to3Actions(
-      RuleContext ruleContext, Iterable<Artifact> inputs) {
-    // This creates many actions, but this is fine. Creating one action per library leads
-    // to some problems (when the same file is generated by two different actions), with
-    // little benefits and negligible memory improvement.
-
-    Map<PathFragment, Artifact> symlinks = new HashMap<>();
-    for (Artifact input : inputs) {
-      Artifact output = generate2to3Action(ruleContext, input);
-      if (output == null) {
-        return null;
-      }
-      symlinks.put(input.getRootRelativePath(), output);
-    }
-    return symlinks;
-  }
-
-  /**
-   * Generates and registers one 2to3 action.
-   *
-   * <p>Returns null and reports an error if the output file cannot be created.
-   */
-  @Nullable
-  private static Artifact generate2to3Action(RuleContext ruleContext, Artifact input) {
-    FilesToRunProvider py2to3converter = ruleContext.getExecutablePrerequisite("$python2to3");
-    Artifact output = get2to3OutputArtifact(ruleContext, input);
-    if (output == null) {
-      return null;
-    }
-
-    CustomCommandLine.Builder commandLine =
-        CustomCommandLine.builder()
-            .add("--no-diffs")
-            .add("--nobackups")
-            .add("--write")
-            .addPath("--output-dir", output.getExecPath().getParentDirectory())
-            .add("--write-unchanged-files")
-            .addExecPath(input);
-
-    ruleContext.registerAction(
-        new SpawnAction.Builder()
-            .addInput(input)
-            .addOutput(output)
-            .setExecutable(py2to3converter)
-            .setProgressMessage("Converting to Python 3: %s", input.prettyPrint())
-            .setMnemonic("2to3")
-            .addCommandLine(commandLine.build())
-            .build(ruleContext));
-    return output;
-  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockPythonSupport.java b/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockPythonSupport.java
index 39e91db..a95f246 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockPythonSupport.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockPythonSupport.java
@@ -80,8 +80,7 @@
         "    toolchain = ':default_py_runtime_pair',",
         "    toolchain_type = ':toolchain_type',",
         ")",
-        "exports_files(['precompile.py'])",
-        "sh_binary(name='2to3', srcs=['2to3.sh'])");
+        "exports_files(['precompile.py'])");
   }
 
   @Override
diff --git a/src/test/java/com/google/devtools/build/lib/query2/testutil/AbstractQueryTest.java b/src/test/java/com/google/devtools/build/lib/query2/testutil/AbstractQueryTest.java
index 8ac9cbf..cdb1e34 100644
--- a/src/test/java/com/google/devtools/build/lib/query2/testutil/AbstractQueryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/query2/testutil/AbstractQueryTest.java
@@ -1068,8 +1068,8 @@
     // Works for implicit edges too.  This is for consistency with --output
     // xml, which exposes them too.
     String toolsRepository = helper.getToolsRepository();
-    assertThat(eval("labels(\"$python2to3\", //k)"))
-        .isEqualTo(eval(toolsRepository + "//tools/python:2to3"));
+    assertThat(eval("labels(\"$py_toolchain_type\", //k)"))
+        .isEqualTo(eval(toolsRepository + "//tools/python:toolchain_type"));
 
     // Configurable deps:
     if (testConfigurableAttributes()) {
diff --git a/tools/python/BUILD.tools b/tools/python/BUILD.tools
index e289d95..5031ee9 100644
--- a/tools/python/BUILD.tools
+++ b/tools/python/BUILD.tools
@@ -19,30 +19,6 @@
 
 package(default_visibility = ["//visibility:public"])
 
-write_file(
-    name = "2to3",
-    out = select({
-        "//src/conditions:host_windows": "2to3.bat",
-        "//conditions:default": "2to3.sh",
-    }),
-    content = select({
-        "//src/conditions:host_windows": [
-            "@echo >&2 ERROR: 2to3 is not implemented.",
-            "@echo >&2 Please either build this target for PY2 or else set srcs_version to PY2ONLY instead of PY2.",
-            "@echo >&2 See https://github.com/bazelbuild/bazel/issues/1393#issuecomment-431110617",
-            "@exit /B 1",
-        ],
-        "//conditions:default": [
-            "#!/bin/bash",
-            "echo >&2 'ERROR: 2to3 is not implemented.'",
-            "echo >&2 'Please either build this target for PY2 or else set srcs_version to PY2ONLY instead of PY2.'",
-            "echo >&2 'See https://github.com/bazelbuild/bazel/issues/1393#issuecomment-431110617'",
-            "exit 1",
-        ],
-    }),
-    is_executable = True,
-)
-
 # These exports are needed for Starlark tooling to work on files that
 # transitive load these ones. Ideally they would be bzl_library targets, but we
 # don't have access to Skylib here. See