Make --incompatible_allow_python_version_transitions a no-op

This flag enabled the new flexible Python version semantics, from the old --force_python way of doing things. It's been flipped for quite some time. This change deletes its remaining uses in tests and makes it a no-op.

Work toward #7797.

RELNOTES: None
PiperOrigin-RevId: 303172288
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 f7dcf00..36a1d9e 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
@@ -105,14 +105,13 @@
           reasons, but they are essentially the same as <code>"PY2"</code> and <code>"PY3"</code>
           and should be avoided.
 
-          <p>Under the old semantics
-          (<code>--incompatible_allow_python_version_transitions=false</code>), it is an error to
-          build any Python target for a version disallowed by its <code>srcs_version</code>
-          attribute. Under the new semantics
-          (<code>--incompatible_allow_python_version_transitions=true</code>), this check is
-          deferred to the executable rule: You can build a <code>srcs_version = "PY3"</code>
-          <code>py_library</code> target for Python 2, but you cannot actually depend on it via
-          <code>deps</code> from a Python 3 <code>py_binary</code>.
+          <p>Note that only the executable rules ({@code py_binary} and {@code py_library}) actually
+          verify the current Python version against the value of this attribute. (This is a feature;
+          since {@code py_library} does not change the current Python version, if it did the
+          validation, it'd be impossible to build both {@code PY2ONLY} and {@code PY3ONLY} libraries
+          in the same invocation.) Furthermore, if there is a version mismatch, the error is only
+          reported in the execution phase. In particular, the error will not appear in a {@code
+          bazel build --nobuild} invocation.)
 
           <p>To get diagnostic information about which dependencies introduce version requirements,
           you can run the <code>find_requirements</code> aspect on your target:
@@ -181,17 +180,9 @@
           Whether to build this target (and its transitive <code>deps</code>) for Python 2 or Python
           3. Valid values are <code>"PY2"</code> and <code>"PY3"</code> (the default).
 
-          <p>Under the old semantics
-          (<code>--incompatible_allow_python_version_transitions=false</code>), the Python version
-          generally cannot be changed once set. This means that the <code>--python_version</code>
-          flag overrides this attribute, and other Python binaries in the <code>data</code> deps of
-          this target are forced to use the same version as this target.
-
-          <p>Under the new semantics
-          (<code>--incompatible_allow_python_version_transitions=true</code>), the Python version
-          is always set (possibly by default) to whatever version is specified by this attribute,
-          regardless of the version specified on the command line or by other targets that depend on
-          this one.
+          <p>The Python version is always reset (possibly by default) to whatever version is
+          specified by this attribute, regardless of the version specified on the command line or by
+          other higher targets that depend on this one.
 
           <p>If you want to <code>select()</code> on the current Python version, you can inspect the
           value of <code>@rules_python//python:python_version</code>. See
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 3c8968f..ff5ecd0 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
@@ -216,7 +216,6 @@
     this.hasPy3OnlySources = initHasPy3OnlySources(ruleContext, this.sourcesVersion);
     this.runtimeFromToolchain = initRuntimeFromToolchain(ruleContext, this.version);
     this.convertedFiles = makeAndInitConvertedFiles(ruleContext, version, this.sourcesVersion);
-    maybeValidateVersionCompatibleWithOwnSourcesAttr();
     validateTargetPythonVersionAttr(DEFAULT_PYTHON_VERSION_ATTRIBUTE);
     validateTargetPythonVersionAttr(PYTHON_VERSION_ATTRIBUTE);
     validateOldVersionAttrNotUsedIfDisabled();
@@ -512,39 +511,6 @@
   }
 
   /**
-   * Under the old version semantics ({@code
-   * --incompatible_allow_python_version_transitions=false}), checks that the {@code srcs_version}
-   * attribute is compatible with the Python version as determined by the configuration.
-   *
-   * <p>A failure is reported as a rule error.
-   *
-   * <p>This check is local to the current target and intended to be enforced by each {@code
-   * py_library} up the dependency chain.
-   *
-   * <p>No-op under the new version semantics.
-   */
-  private void maybeValidateVersionCompatibleWithOwnSourcesAttr() {
-    if (ruleContext.getFragment(PythonConfiguration.class).useNewPyVersionSemantics()) {
-      return;
-    }
-    // Treat PY3 as PY3ONLY: we'll never implement 3to2.
-    if ((version == PythonVersion.PY2 || version == PythonVersion.PY2AND3)
-        && (sourcesVersion == PythonVersion.PY3 || sourcesVersion == PythonVersion.PY3ONLY)) {
-      ruleContext.ruleError(
-          "Rule '"
-              + ruleContext.getLabel()
-              + "' can only be used with Python 3, and cannot be converted to Python 2");
-    }
-    if ((version == PythonVersion.PY3 || version == PythonVersion.PY2AND3)
-        && sourcesVersion == PythonVersion.PY2ONLY) {
-      ruleContext.ruleError(
-          "Rule '"
-              + ruleContext.getLabel()
-              + "' can only be used with Python 2, and cannot be converted to Python 3");
-    }
-  }
-
-  /**
    * Reports an attribute error if the given target Python version attribute ({@code
    * default_python_version} or {@code python_version}) cannot be parsed as {@code PY2}, {@code
    * PY3}, or the sentinel value.
@@ -645,25 +611,18 @@
   }
 
   /**
-   * Under the new version semantics ({@code --incompatible_allow_python_version_transitions=true}),
-   * if the Python version (as determined by the configuration) is inconsistent with {@link
+   * If the Python version (as determined by the configuration) is inconsistent with {@link
    * #hasPy2OnlySources} or {@link #hasPy3OnlySources}, emits a {@link FailAction} that "generates"
    * the executable.
    *
-   * <p>If the version is consistent, or if we are using the old semantics, no such action is
-   * emitted.
-   *
    * <p>We use a {@code FailAction} rather than a rule error because we want to defer the error
    * until the execution phase. This way, we still get a configured target that the user can query
    * over with an aspect to find the exact transitive dependency that introduced the offending
-   * version constraint.
+   * version constraint. (See {@code <tools repo>//tools/python/srcs_version.bzl%find_requirements})
    *
    * @return true if a {@link FailAction} was created
    */
   private boolean maybeCreateFailActionDueToTransitiveSourcesVersion() {
-    if (!ruleContext.getFragment(PythonConfiguration.class).useNewPyVersionSemantics()) {
-      return false;
-    }
     String errorTemplate =
         ruleContext.getLabel()
             + ": "
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/PythonConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/python/PythonConfiguration.java
index 9acc4cf..ab03ab7 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/PythonConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/PythonConfiguration.java
@@ -17,10 +17,7 @@
 import com.google.common.base.Preconditions;
 import com.google.common.base.Verify;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
-import com.google.devtools.build.lib.analysis.config.BuildOptions;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
-import com.google.devtools.build.lib.events.Event;
-import com.google.devtools.build.lib.events.EventHandler;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
 import com.google.devtools.build.lib.syntax.StarlarkValue;
@@ -43,9 +40,8 @@
   private final TriState buildPythonZip;
   private final boolean buildTransitiveRunfilesTrees;
 
-  // TODO(brandjon): Remove these once migration to the new version API is complete (#6583).
+  // TODO(brandjon): Remove this (#7797).
   private final boolean oldPyVersionApiAllowed;
-  private final boolean useNewPyVersionSemantics;
 
   // TODO(brandjon): Remove this once migration to PY3-as-default is complete.
   private final boolean py2OutputsAreSuffixed;
@@ -67,7 +63,6 @@
       TriState buildPythonZip,
       boolean buildTransitiveRunfilesTrees,
       boolean oldPyVersionApiAllowed,
-      boolean useNewPyVersionSemantics,
       boolean py2OutputsAreSuffixed,
       boolean disallowLegacyPyProvider,
       boolean useToolchains,
@@ -78,7 +73,6 @@
     this.buildPythonZip = buildPythonZip;
     this.buildTransitiveRunfilesTrees = buildTransitiveRunfilesTrees;
     this.oldPyVersionApiAllowed = oldPyVersionApiAllowed;
-    this.useNewPyVersionSemantics = useNewPyVersionSemantics;
     this.py2OutputsAreSuffixed = py2OutputsAreSuffixed;
     this.disallowLegacyPyProvider = disallowLegacyPyProvider;
     this.useToolchains = useToolchains;
@@ -131,17 +125,6 @@
     }
   }
 
-  @Override
-  public void reportInvalidOptions(EventHandler reporter, BuildOptions buildOptions) {
-    PythonOptions opts = buildOptions.get(PythonOptions.class);
-    if (opts.incompatiblePy3IsDefault && !opts.incompatibleAllowPythonVersionTransitions) {
-      reporter.handle(
-          Event.error(
-              "cannot enable `--incompatible_py3_is_default` without also enabling "
-                  + "`--incompatible_allow_python_version_transitions`"));
-    }
-  }
-
   /** Returns whether to build the executable zip file for Python binaries. */
   public boolean buildPythonZip() {
     switch (buildPythonZip) {
@@ -167,11 +150,6 @@
     return oldPyVersionApiAllowed;
   }
 
-  /** Returns true if the new semantics should be used for transitions on the Python version. */
-  public boolean useNewPyVersionSemantics() {
-    return useNewPyVersionSemantics;
-  }
-
   /**
    * Returns true if Python rules should omit the legacy "py" provider and fail-fast when given this
    * provider from their {@code deps}.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/PythonConfigurationLoader.java b/src/main/java/com/google/devtools/build/lib/rules/python/PythonConfigurationLoader.java
index a898e2c..805e333 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/PythonConfigurationLoader.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/PythonConfigurationLoader.java
@@ -40,7 +40,6 @@
         pythonOptions.buildPythonZip,
         pythonOptions.buildTransitiveRunfilesTrees,
         /*oldPyVersionApiAllowed=*/ !pythonOptions.incompatibleRemoveOldPythonVersionApi,
-        /*useNewPyVersionSemantics=*/ pythonOptions.incompatibleAllowPythonVersionTransitions,
         /*py2OutputsAreSuffixed=*/ pythonOptions.incompatiblePy2OutputsAreSuffixed,
         /*disallowLegacyPyProvider=*/ pythonOptions.incompatibleDisallowLegacyPyProvider,
         /*useToolchains=*/ pythonOptions.incompatibleUsePythonToolchains,
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/PythonOptions.java b/src/main/java/com/google/devtools/build/lib/rules/python/PythonOptions.java
index 44b835c4..02846d6 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/PythonOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/PythonOptions.java
@@ -85,18 +85,22 @@
               + "flag also disables `select()`-ing over `--host_force_python`.")
   public boolean incompatibleRemoveOldPythonVersionApi;
 
+  /**
+   * Deprecated machinery for setting the Python version; will be removed soon.
+   *
+   * <p>Not GraveyardOptions'd because we'll delete this alongside other soon-to-be-removed options
+   * in this file.
+   */
   @Option(
       name = "incompatible_allow_python_version_transitions",
       defaultValue = "true",
-      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
+      documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
       effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
       metadataTags = {
         OptionMetadataTag.INCOMPATIBLE_CHANGE,
         OptionMetadataTag.TRIGGERED_BY_ALL_INCOMPATIBLE_CHANGES
       },
-      help =
-          "If true, Python rules use the new PY2/PY3 version semantics. For more information, see "
-              + "the documentation for `py_binary`'s `python_version` attribute.")
+      help = "No-op, will be removed soon.")
   public boolean incompatibleAllowPythonVersionTransitions;
 
   /**
@@ -117,10 +121,9 @@
       },
       help =
           "If true, `py_binary` and `py_test` targets that do not set their `python_version` (or "
-              + "`default_python_version`) attribute will default to PY3 rather than to PY2. It is "
-              + "an error to set this flag without also enabling "
-              + "`--incompatible_allow_python_version_transitions`. If you set this flag it is "
-              + "also recommended to set `--incompatible_py2_outputs_are_suffixed`.")
+              + "`default_python_version`) attribute will default to PY3 rather than to PY2. If "
+              + "you set this flag it is also recommended to set "
+              + "`--incompatible_py2_outputs_are_suffixed`.")
   public boolean incompatiblePy3IsDefault;
 
   @Option(
@@ -159,9 +162,8 @@
         OptionEffectTag.AFFECTS_OUTPUTS // because of "-py2"/"-py3" output root
       },
       help =
-          "The Python major version mode, either `PY2` or `PY3`. Note that under the new version "
-              + "semantics (`--incompatible_allow_python_version_transitions`) this is overridden "
-              + "by `py_binary` and `py_test` targets (even if they don't explicitly specify a "
+          "The Python major version mode, either `PY2` or `PY3`. Note that this is overridden by "
+              + "`py_binary` and `py_test` targets (even if they don't explicitly specify a "
               + "version) so there is usually not much reason to supply this flag.")
   public PythonVersion pythonVersion;
 
@@ -326,44 +328,24 @@
   }
 
   /**
-   * Returns whether a Python version transition to {@code version} is allowed and not a no-op.
-   *
-   * <p>Under the new semantics ({@link #incompatibleAllowPythonVersionTransitions} is true),
-   * version transitions are always allowed, so this essentially returns whether the new version is
-   * different from the existing one.
-   *
-   * <p>Under the old semantics ({@link #incompatibleAllowPythonVersionTransitions} is false),
-   * version transitions are not allowed once the version has already been set ({@link
-   * #pythonVersion} is non-null). Due to a historical bug, it is also not allowed to transition the
-   * version to the hard-coded default value. Under these constraints, there is only one transition
-   * possible, from null to the non-default value, and it is never a no-op.
+   * Returns whether a Python version transition to {@code version} is not a no-op.
    *
    * @throws IllegalArgumentException if {@code version} is not {@code PY2} or {@code PY3}
    */
   public boolean canTransitionPythonVersion(PythonVersion version) {
     Preconditions.checkArgument(version.isTargetValue());
-    if (incompatibleAllowPythonVersionTransitions) {
-      return !version.equals(getPythonVersion());
-    } else {
-      boolean currentlyUnset = pythonVersion == null;
-      boolean transitioningToNonDefault = !version.equals(getDefaultPythonVersion());
-      return currentlyUnset && transitioningToNonDefault;
-    }
+    return !version.equals(getPythonVersion());
   }
 
   /**
-   * Manipulates the Python version fields so that {@link #getPythonVersion()} returns {@code
-   * version}.
+   * Sets the Python version to {@code version}.
    *
-   * <p>This method is a mutation on the current instance, so it should only be invoked on a newly
-   * constructed instance. The mutation does not depend on whether or not {@link
-   * #canTransitionPythonVersion} would return true.
-   *
-   * <p>If the old semantics are in effect ({@link #incompatibleAllowPythonVersionTransitions} is
-   * false), after this method is called {@link #canTransitionPythonVersion} will return false.
+   * <p>Since this is a mutation, it should only be called on a newly constructed instance.
    *
    * @throws IllegalArgumentException if {@code version} is not {@code PY2} or {@code PY3}
    */
+  // TODO(brandjon): Consider removing this mutator now that the various flags and semantics it
+  // used to consider are gone. We'd revert to just setting the public option field directly.
   public void setPythonVersion(PythonVersion version) {
     Preconditions.checkArgument(version.isTargetValue());
     this.pythonVersion = version;
@@ -373,8 +355,6 @@
   public FragmentOptions getHost() {
     PythonOptions hostPythonOptions = (PythonOptions) getDefault();
     hostPythonOptions.incompatibleRemoveOldPythonVersionApi = incompatibleRemoveOldPythonVersionApi;
-    hostPythonOptions.incompatibleAllowPythonVersionTransitions =
-        incompatibleAllowPythonVersionTransitions;
     PythonVersion hostVersion =
         (hostForcePython != null) ? hostForcePython : getDefaultPythonVersion();
     hostPythonOptions.setPythonVersion(hostVersion);
@@ -393,13 +373,10 @@
 
   @Override
   public FragmentOptions getNormalized() {
-    // Under the new version semantics, we want to ensure that options with "null" physical default
-    // values are normalized, to avoid #7808. We don't want to normalize with the old version
-    // semantics because that breaks backwards compatibility.
+    // We want to ensure that options with "null" physical default values are normalized, to avoid
+    // #7808.
     PythonOptions newOptions = (PythonOptions) clone();
-    if (incompatibleAllowPythonVersionTransitions) {
-      newOptions.setPythonVersion(newOptions.getPythonVersion());
-    }
+    newOptions.setPythonVersion(newOptions.getPythonVersion());
     return newOptions;
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/rules/python/PyBaseConfiguredTargetTestBase.java b/src/test/java/com/google/devtools/build/lib/rules/python/PyBaseConfiguredTargetTestBase.java
index d3767f6..d65e07a 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/python/PyBaseConfiguredTargetTestBase.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/python/PyBaseConfiguredTargetTestBase.java
@@ -69,27 +69,6 @@
   }
 
   @Test
-  public void srcsVersionClashesWithVersionFlagUnderOldSemantics() throws Exception {
-    // Under the old version semantics, we fail on any Python target the moment a conflict between
-    // srcs_version and the configuration is detected. Under the new semantics, py_binary and
-    // py_test care if there's a conflict but py_library does not. This test case checks the old
-    // semantics; the new semantics are checked in PyLibraryConfiguredTargetTest and
-    // PyExecutableConfiguredTargetTestBase. Note that under the new semantics py_binary and
-    // py_library ignore the version flag, so those tests use the attribute to set the version
-    // instead.
-    useConfiguration(
-        "--incompatible_allow_python_version_transitions=false", "--python_version=PY3");
-    checkError("pkg", "foo",
-        // error:
-        "'//pkg:foo' can only be used with Python 2",
-        // build file:
-        ruleName + "(",
-        "    name = 'foo',",
-        "    srcs = [':foo.py'],",
-        "    srcs_version = 'PY2ONLY')");
-  }
-
-  @Test
   public void versionIs2IfUnspecified() throws Exception {
     assumesDefaultIsPY2();
     scratch.file(
@@ -101,24 +80,6 @@
   }
 
   @Test
-  public void versionIs3IfForcedByFlagUnderOldSemantics() throws Exception {
-    // Under the old version semantics, --python_version takes precedence over the rule's own
-    // default_python_version attribute, so this test case applies equally well to py_library,
-    // py_binary, and py_test. Under the new semantics the rule attribute takes precedence, so this
-    // would only make sense for py_library; see PyLibraryConfiguredTargetTest for the analogous
-    // test.
-    assumesDefaultIsPY2();
-    useConfiguration(
-        "--incompatible_allow_python_version_transitions=false", "--python_version=PY3");
-    scratch.file(
-        "pkg/BUILD", //
-        ruleName + "(",
-        "    name = 'foo',",
-        "    srcs = ['foo.py'])");
-    assertThat(getPythonVersion(getConfiguredTarget("//pkg:foo"))).isEqualTo(PythonVersion.PY3);
-  }
-
-  @Test
   public void packageNameCannotHaveHyphen() throws Exception {
     checkError("pkg-hyphenated", "foo",
         // error:
diff --git a/src/test/java/com/google/devtools/build/lib/rules/python/PyBinaryConfiguredTargetTest.java b/src/test/java/com/google/devtools/build/lib/rules/python/PyBinaryConfiguredTargetTest.java
index c826117..7afa8e1 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/python/PyBinaryConfiguredTargetTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/python/PyBinaryConfiguredTargetTest.java
@@ -49,59 +49,23 @@
   }
 
   @Test
-  public void python2WithPy3SrcsVersionDependency_OldSemantics() throws Exception {
-    reporter.removeHandler(failFastHandler); // expect errors
-    useConfiguration("--incompatible_allow_python_version_transitions=false");
-    declareBinDependingOnLibWithVersions("PY2", "PY3");
-    assertThat(view.hasErrors(getConfiguredTarget("//pkg:bin"))).isTrue();
-    assertContainsEvent("//pkg:lib: Rule '//pkg:lib' can only be used with Python 3");
-  }
-
-  @Test
-  public void python2WithPy3SrcsVersionDependency_NewSemantics() throws Exception {
-    useConfiguration("--incompatible_allow_python_version_transitions=true");
+  public void python2WithPy3SrcsVersionDependency() throws Exception {
     declareBinDependingOnLibWithVersions("PY2", "PY3");
     assertThat(getPyExecutableDeferredError("//pkg:bin"))
-        .contains("being built for Python 2 but (transitively) includes Python 3-only sources");
+        .startsWith(
+            "//pkg:bin: This target is being built for Python 2 but (transitively) "
+                + "includes Python 3-only sources");
   }
 
   @Test
-  public void pythonWithIncompatibleSrcsNewSemanticsErrorMessageContainsLabel() throws Exception {
-    useConfiguration("--incompatible_allow_python_version_transitions=true");
-    declareBinDependingOnLibWithVersions("PY2", "PY3");
-    assertThat(getPyExecutableDeferredError("//pkg:bin"))
-        .startsWith("//pkg:bin: This target is being built for Python 2 but");
-  }
-
-  @Test
-  public void python2WithPy3OnlySrcsVersionDependency_OldSemantics() throws Exception {
-    reporter.removeHandler(failFastHandler); // expect errors
-    useConfiguration("--incompatible_allow_python_version_transitions=false");
-    declareBinDependingOnLibWithVersions("PY2", "PY3ONLY");
-    assertThat(view.hasErrors(getConfiguredTarget("//pkg:bin"))).isTrue();
-    assertContainsEvent("//pkg:lib: Rule '//pkg:lib' can only be used with Python 3");
-  }
-
-  @Test
-  public void python2WithPy3OnlySrcsVersionDependency_NewSemantics() throws Exception {
-    useConfiguration("--incompatible_allow_python_version_transitions=true");
+  public void python2WithPy3OnlySrcsVersionDependency() throws Exception {
     declareBinDependingOnLibWithVersions("PY2", "PY3ONLY");
     assertThat(getPyExecutableDeferredError("//pkg:bin"))
         .contains("being built for Python 2 but (transitively) includes Python 3-only sources");
   }
 
   @Test
-  public void python3WithPy2OnlySrcsVersionDependency_OldSemantics() throws Exception {
-    reporter.removeHandler(failFastHandler); // expect errors
-    useConfiguration("--incompatible_allow_python_version_transitions=false");
-    declareBinDependingOnLibWithVersions("PY3", "PY2ONLY");
-    assertThat(view.hasErrors(getConfiguredTarget("//pkg:bin"))).isTrue();
-    assertContainsEvent("//pkg:lib: Rule '//pkg:lib' can only be used with Python 2");
-  }
-
-  @Test
   public void python3WithPy2OnlySrcsVersionDependency_NewSemantics() throws Exception {
-    useConfiguration("--incompatible_allow_python_version_transitions=true");
     declareBinDependingOnLibWithVersions("PY3", "PY2ONLY");
     assertThat(getPyExecutableDeferredError("//pkg:bin"))
         .contains("being built for Python 3 but (transitively) includes Python 2-only sources");
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 5cd8e8b..23fd678 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
@@ -79,6 +79,11 @@
     return ((FailAction) action).getErrorMessage();
   }
 
+  /** Asserts that a configured target has the given Python version. */
+  protected void assertPythonVersionIs(String targetName, PythonVersion version) throws Exception {
+    assertThat(getPythonVersion(getOkPyTarget(targetName))).isEqualTo(version);
+  }
+
   /**
    * Sets the configuration, then asserts that a configured target has the given Python version.
    *
@@ -87,7 +92,7 @@
   protected void assertPythonVersionIs_UnderNewConfig(
       String targetName, PythonVersion version, String... flags) throws Exception {
     useConfiguration(flags);
-    assertThat(getPythonVersion(getOkPyTarget(targetName))).isEqualTo(version);
+    assertPythonVersionIs(targetName, version);
   }
 
   /**
@@ -256,22 +261,19 @@
 
   @Test
   public void py3IsDefaultFlag_SetsDefaultPythonVersion() throws Exception {
-    scratch.file( //
+    scratch.file(
         "pkg/BUILD", //
-        ruleName + "(", //
-        "    name = 'foo',", //
-        "    srcs = ['foo.py'],", //
+        ruleName + "(",
+        "    name = 'foo',",
+        "    srcs = ['foo.py'],",
         ")");
-    // --incompatible_py3_is_default requires --incompatible_allow_python_version_transitions
     assertPythonVersionIs_UnderNewConfig(
         "//pkg:foo",
         PythonVersion.PY2,
-        "--incompatible_allow_python_version_transitions=true",
         "--incompatible_py3_is_default=false");
     assertPythonVersionIs_UnderNewConfig(
         "//pkg:foo",
         PythonVersion.PY3,
-        "--incompatible_allow_python_version_transitions=true",
         "--incompatible_py3_is_default=true",
         // Keep the host Python as PY2, because we don't want to drag any implicit dependencies on
         // tools into PY3 for this test. (Doing so may require setting extra options to get it to
@@ -282,11 +284,9 @@
   @Test
   public void py3IsDefaultFlag_DoesntOverrideExplicitVersion() throws Exception {
     scratch.file("pkg/BUILD", ruleDeclWithPyVersionAttr("foo", "PY2"));
-    // --incompatible_py3_is_default requires --incompatible_allow_python_version_transitions
     assertPythonVersionIs_UnderNewConfig(
         "//pkg:foo",
         PythonVersion.PY2,
-        "--incompatible_allow_python_version_transitions=true",
         "--incompatible_py3_is_default=true",
         // Keep the host Python as PY2, because we don't want to drag any implicit dependencies on
         // tools into PY3 for this test. (Doing so may require setting extra options to get it to
@@ -309,156 +309,51 @@
   }
 
   @Test
-  public void versionAttrWorksUnderOldAndNewSemantics_WhenNotDefaultValue() throws Exception {
+  public void versionAttrWorks_WhenNotDefaultValue() throws Exception {
     assumesDefaultIsPY2();
     scratch.file("pkg/BUILD", ruleDeclWithPyVersionAttr("foo", "PY3"));
 
-    assertPythonVersionIs_UnderNewConfigs(
-        "//pkg:foo",
-        PythonVersion.PY3,
-        new String[] {"--incompatible_allow_python_version_transitions=false"},
-        new String[] {"--incompatible_allow_python_version_transitions=true"});
+    assertPythonVersionIs("//pkg:foo", PythonVersion.PY3);
   }
 
   @Test
-  public void versionAttrWorksUnderOldAndNewSemantics_WhenSameAsDefaultValue() throws Exception {
+  public void versionAttrWorks_WhenSameAsDefaultValue() throws Exception {
     assumesDefaultIsPY2();
     scratch.file("pkg/BUILD", ruleDeclWithPyVersionAttr("foo", "PY2"));
 
-    assertPythonVersionIs_UnderNewConfigs(
-        "//pkg:foo",
-        PythonVersion.PY2,
-        new String[] {"--incompatible_allow_python_version_transitions=false"},
-        new String[] {"--incompatible_allow_python_version_transitions=true"});
+    assertPythonVersionIs("//pkg:foo", PythonVersion.PY2);
   }
 
   @Test
-  public void flagTakesPrecedenceUnderOldSemantics_NonDefaultValue() throws Exception {
-    assumesDefaultIsPY2();
-    scratch.file("pkg/BUILD", ruleDeclWithPyVersionAttr("foo", "PY2"));
-    assertPythonVersionIs_UnderNewConfig(
-        "//pkg:foo",
-        PythonVersion.PY3,
-        "--incompatible_allow_python_version_transitions=false",
-        "--python_version=PY3");
-  }
-
-  @Test
-  public void flagTakesPrecedenceUnderOldSemantics_DefaultValue() throws Exception {
-    assumesDefaultIsPY2();
-    scratch.file("pkg/BUILD", ruleDeclWithPyVersionAttr("foo", "PY3"));
-    assertPythonVersionIs_UnderNewConfig(
-        "//pkg:foo",
-        PythonVersion.PY2,
-        "--incompatible_allow_python_version_transitions=false",
-        "--python_version=PY2");
-  }
-
-  @Test
-  public void versionAttrTakesPrecedenceUnderNewSemantics_NonDefaultValue() throws Exception {
+  public void versionAttrTakesPrecedence_NonDefaultValue() throws Exception {
     assumesDefaultIsPY2();
     scratch.file("pkg/BUILD", ruleDeclWithPyVersionAttr("foo", "PY3"));
 
-    assertPythonVersionIs_UnderNewConfig(
-        "//pkg:foo",
-        PythonVersion.PY3,
-        "--incompatible_allow_python_version_transitions=true",
-        "--python_version=PY2");
+    assertPythonVersionIs_UnderNewConfig("//pkg:foo", PythonVersion.PY3, "--python_version=PY2");
   }
 
   @Test
-  public void versionAttrTakesPrecedenceUnderNewSemantics_DefaultValue() throws Exception {
+  public void versionAttrTakesPrecedence_DefaultValue() throws Exception {
     assumesDefaultIsPY2();
     scratch.file("pkg/BUILD", ruleDeclWithPyVersionAttr("foo", "PY2"));
 
-    assertPythonVersionIs_UnderNewConfig(
-        "//pkg:foo",
-        PythonVersion.PY2,
-        "--incompatible_allow_python_version_transitions=true",
-        "--python_version=PY3");
+    assertPythonVersionIs_UnderNewConfig("//pkg:foo", PythonVersion.PY2, "--python_version=PY3");
   }
 
   @Test
-  public void canBuildWithDifferentVersionAttrs_UnderOldAndNewSemantics() throws Exception {
+  public void canBuildWithDifferentVersionAttrs() throws Exception {
     scratch.file(
         "pkg/BUILD",
         ruleDeclWithPyVersionAttr("foo_v2", "PY2"),
         ruleDeclWithPyVersionAttr("foo_v3", "PY3"));
 
-    assertPythonVersionIs_UnderNewConfigs(
-        "//pkg:foo_v2",
-        PythonVersion.PY2,
-        new String[] {"--incompatible_allow_python_version_transitions=false"},
-        new String[] {"--incompatible_allow_python_version_transitions=true"});
-    assertPythonVersionIs_UnderNewConfigs(
-        "//pkg:foo_v3",
-        PythonVersion.PY3,
-        new String[] {"--incompatible_allow_python_version_transitions=false"},
-        new String[] {"--incompatible_allow_python_version_transitions=true"});
+    assertPythonVersionIs("//pkg:foo_v2", PythonVersion.PY2);
+    assertPythonVersionIs("//pkg:foo_v3", PythonVersion.PY3);
   }
 
   @Test
-  public void canBuildWithDifferentVersionAttrs_UnderOldSemantics_FlagSetToDefault()
-      throws Exception {
-    assumesDefaultIsPY2();
-    scratch.file(
-        "pkg/BUILD",
-        ruleDeclWithPyVersionAttr("foo_v2", "PY2"),
-        ruleDeclWithPyVersionAttr("foo_v3", "PY3"));
-
-    assertPythonVersionIs_UnderNewConfig(
-        "//pkg:foo_v2",
-        PythonVersion.PY2,
-        "--incompatible_allow_python_version_transitions=false",
-        "--python_version=PY2");
-    assertPythonVersionIs_UnderNewConfig(
-        "//pkg:foo_v3",
-        PythonVersion.PY2,
-        "--incompatible_allow_python_version_transitions=false",
-        "--python_version=PY2");
-  }
-
-  @Test
-  public void canBuildWithDifferentVersionAttrs_UnderOldSemantics_FlagSetToNonDefault()
-      throws Exception {
-    assumesDefaultIsPY2();
-    scratch.file(
-        "pkg/BUILD",
-        ruleDeclWithPyVersionAttr("foo_v2", "PY2"),
-        ruleDeclWithPyVersionAttr("foo_v3", "PY3"));
-
-    assertPythonVersionIs_UnderNewConfig(
-        "//pkg:foo_v2",
-        PythonVersion.PY3,
-        "--incompatible_allow_python_version_transitions=false",
-        "--python_version=PY3");
-    assertPythonVersionIs_UnderNewConfig(
-        "//pkg:foo_v3",
-        PythonVersion.PY3,
-        "--incompatible_allow_python_version_transitions=false",
-        "--python_version=PY3");
-  }
-
-  @Test
-  public void incompatibleSrcsVersion_OldSemantics() throws Exception {
-    useConfiguration("--incompatible_allow_python_version_transitions=false");
-    checkError(
-        "pkg",
-        "foo",
-        // error:
-        "'//pkg:foo' can only be used with Python 2",
-        // build file:
-        ruleName + "(",
-        "    name = 'foo',",
-        "    srcs = [':foo.py'],",
-        "    srcs_version = 'PY2ONLY',",
-        "    default_python_version = 'PY3')");
-  }
-
-  @Test
-  public void incompatibleSrcsVersion_NewSemantics() throws Exception {
+  public void incompatibleSrcsVersion() throws Exception {
     reporter.removeHandler(failFastHandler); // We assert below that we don't fail at analysis.
-    useConfiguration("--incompatible_allow_python_version_transitions=true");
     scratch.file(
         "pkg/BUILD",
         // build file:
@@ -467,29 +362,10 @@
         "    srcs = [':foo.py'],",
         "    srcs_version = 'PY2ONLY',",
         "    python_version = 'PY3')");
-    // Under the new semantics, this is an execution-time error, not an analysis-time one. We fail
-    // by setting the generating action to FailAction.
-    assertNoEvents();
     assertThat(getPyExecutableDeferredError("//pkg:foo"))
         .contains("being built for Python 3 but (transitively) includes Python 2-only sources");
-  }
-
-  @Test
-  public void incompatibleSrcsVersion_DueToVersionAttrDefault() throws Exception {
-    assumesDefaultIsPY2(); // When changed to PY3, flip srcs_version below to be PY2ONLY.
-
-    // This test doesn't care whether we use old and new semantics, but it affects how we assert.
-    useConfiguration("--incompatible_allow_python_version_transitions=false");
-
-    // Fails because default_python_version is PY2 by default, so the config is set to PY2
-    // regardless of srcs_version.
-    checkError("pkg", "foo",
-        // error:
-        "'//pkg:foo' can only be used with Python 3",
-        // build file:
-        ruleName + "(",
-        "    name = 'foo',",
-        "    srcs = [':foo.py'],",
-        "    srcs_version = 'PY3ONLY')");
+    // This is an execution-time error, not an analysis-time one. We fail by setting the generating
+    // action to FailAction.
+    assertNoEvents();
   }
 }
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 045eda8..6a93eea 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
@@ -46,34 +46,29 @@
   }
 
   @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.
-    useConfiguration(
-        "--incompatible_allow_python_version_transitions=true", "--python_version=PY3");
+  public void canBuildWithIncompatibleSrcsVersion() throws Exception {
+    useConfiguration("--python_version=PY3");
     scratch.file(
         "pkg/BUILD",
         "py_library(",
         "    name = 'foo',",
         "    srcs = [':foo.py'],",
         "    srcs_version = 'PY2ONLY')");
-    // Under the new semantics, errors are only reported at the binary target, not the library, and
-    // even then they'd be deferred to execution time, so there should be nothing wrong here.
+    // Errors are only reported at the binary target, not the library, and even then they'd be
+    // deferred to execution time, so there should be nothing wrong here.
     assertThat(view.hasErrors(getConfiguredTarget("//pkg:foo"))).isFalse();
   }
 
   @Test
-  public void versionIs3IfSetByFlagUnderNewSemantics() 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.
+  public void versionIs3IfSetByFlag() throws Exception {
     assumesDefaultIsPY2();
-    useConfiguration(
-        "--incompatible_allow_python_version_transitions=true", "--python_version=PY3");
+    useConfiguration("--python_version=PY3");
     scratch.file(
-        "pkg/BUILD",
+        "pkg/BUILD", //
         "py_library(",
         "    name = 'foo',",
-        "    srcs = ['foo.py'])");
+        "    srcs = ['foo.py'],",
+        ")");
     assertThat(getPythonVersion(getConfiguredTarget("//pkg:foo"))).isEqualTo(PythonVersion.PY3);
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/rules/python/PythonConfigurationTest.java b/src/test/java/com/google/devtools/build/lib/rules/python/PythonConfigurationTest.java
index 998fc0c..267142fd 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/python/PythonConfigurationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/python/PythonConfigurationTest.java
@@ -50,25 +50,10 @@
   }
 
   @Test
-  public void py3IsDefaultFlagRequiresNewSemanticsFlag() throws Exception {
-    checkError(
-        "cannot enable `--incompatible_py3_is_default` without also enabling "
-            + "`--incompatible_allow_python_version_transitions`",
-        "--incompatible_allow_python_version_transitions=false",
-        "--incompatible_py3_is_default=true");
-  }
-
-  @Test
   public void getDefaultPythonVersion() throws Exception {
-    // --incompatible_py3_is_default requires --incompatible_allow_python_version_transitions
     PythonOptions withoutPy3IsDefaultOpts =
-        parsePythonOptions(
-            "--incompatible_allow_python_version_transitions=true",
-            "--incompatible_py3_is_default=false");
-    PythonOptions withPy3IsDefaultOpts =
-        parsePythonOptions(
-            "--incompatible_allow_python_version_transitions=true",
-            "--incompatible_py3_is_default=true");
+        parsePythonOptions("--incompatible_py3_is_default=false");
+    PythonOptions withPy3IsDefaultOpts = parsePythonOptions("--incompatible_py3_is_default=true");
     assertThat(withoutPy3IsDefaultOpts.getDefaultPythonVersion()).isEqualTo(PythonVersion.PY2);
     assertThat(withPy3IsDefaultOpts.getDefaultPythonVersion()).isEqualTo(PythonVersion.PY3);
   }
@@ -76,61 +61,22 @@
   @Test
   public void getPythonVersion_FallBackOnDefaultPythonVersion() throws Exception {
     // Run it twice with two different values for the incompatible flag to confirm it's actually
-    // reading getDefaultPythonVersion() and not some other source of default values. Note that
-    // --incompatible_py3_is_default requires --incompatible_allow_python_version_transitions.
-    PythonOptions py2Opts =
-        parsePythonOptions(
-            "--incompatible_allow_python_version_transitions=true",
-            "--incompatible_py3_is_default=false");
-    PythonOptions py3Opts =
-        parsePythonOptions(
-            "--incompatible_allow_python_version_transitions=true",
-            "--incompatible_py3_is_default=true");
+    // reading getDefaultPythonVersion() and not some other source of default values.
+    PythonOptions py2Opts = parsePythonOptions("--incompatible_py3_is_default=false");
+    PythonOptions py3Opts = parsePythonOptions("--incompatible_py3_is_default=true");
     assertThat(py2Opts.getPythonVersion()).isEqualTo(PythonVersion.PY2);
     assertThat(py3Opts.getPythonVersion()).isEqualTo(PythonVersion.PY3);
   }
 
   @Test
-  public void canTransitionPythonVersion_OldSemantics_Yes() throws Exception {
-    assumesDefaultIsPY2();
-    PythonOptions opts =
-        parsePythonOptions("--incompatible_allow_python_version_transitions=false");
-    assertThat(opts.canTransitionPythonVersion(PythonVersion.PY3)).isTrue();
-  }
-
-  @Test
-  public void canTransitionPythonVersion_OldSemantics_NoBecauseAlreadySet() throws Exception {
-    assumesDefaultIsPY2();
-    PythonOptions opts =
-        parsePythonOptions(
-            "--incompatible_allow_python_version_transitions=false", "--python_version=PY2");
-    assertThat(opts.canTransitionPythonVersion(PythonVersion.PY3)).isFalse();
-  }
-
-  @Test
-  public void canTransitionPythonVersion_OldSemantics_NoBecauseNewValueSameAsDefault()
-      throws Exception {
-    assumesDefaultIsPY2();
-    PythonOptions opts =
-        parsePythonOptions("--incompatible_allow_python_version_transitions=false");
-    assertThat(opts.canTransitionPythonVersion(PythonVersion.PY2)).isFalse();
-  }
-
-  @Test
-  public void canTransitionPythonVersion_NewSemantics_Yes() throws Exception {
-    PythonOptions opts =
-        parsePythonOptions(
-            "--incompatible_allow_python_version_transitions=true", "--python_version=PY3");
+  public void canTransitionPythonVersion_Yes() throws Exception {
+    PythonOptions opts = parsePythonOptions("--python_version=PY3");
     assertThat(opts.canTransitionPythonVersion(PythonVersion.PY2)).isTrue();
   }
 
   @Test
-  public void canTransitionPythonVersion_NewSemantics_NoBecauseSameAsCurrent() throws Exception {
-    PythonOptions opts =
-        parsePythonOptions(
-            "--incompatible_allow_python_version_transitions=true",
-            "--incompatible_remove_old_python_version_api=false",
-            "--python_version=PY3");
+  public void canTransitionPythonVersion_NoBecauseSameAsCurrent() throws Exception {
+    PythonOptions opts = parsePythonOptions("--python_version=PY3");
     assertThat(opts.canTransitionPythonVersion(PythonVersion.PY3)).isFalse();
   }
 
@@ -157,7 +103,6 @@
   public void getHost_CopiesMostValues() throws Exception {
     PythonOptions opts =
         parsePythonOptions(
-            "--incompatible_allow_python_version_transitions=true",
             "--incompatible_remove_old_python_version_api=true",
             "--incompatible_py3_is_default=true",
             "--incompatible_py2_outputs_are_suffixed=true",
@@ -165,7 +110,6 @@
             "--incompatible_disallow_legacy_py_provider=true",
             "--incompatible_use_python_toolchains=true");
     PythonOptions hostOpts = (PythonOptions) opts.getHost();
-    assertThat(hostOpts.incompatibleAllowPythonVersionTransitions).isTrue();
     assertThat(hostOpts.incompatibleRemoveOldPythonVersionApi).isTrue();
     assertThat(hostOpts.incompatiblePy3IsDefault).isTrue();
     assertThat(hostOpts.incompatiblePy2OutputsAreSuffixed).isTrue();
@@ -180,9 +124,7 @@
     PythonOptions optsWithPythonVersionFlag =
         parsePythonOptions("--python_version=PY2", "--host_force_python=PY3");
     PythonOptions optsWithPy3IsDefaultFlag =
-        // --incompatible_py3_is_default requires --incompatible_allow_python_version_transitions
         parsePythonOptions(
-            "--incompatible_allow_python_version_transitions=true",
             "--incompatible_py3_is_default=true",
             // It's more interesting to set the incompatible flag true and force host to PY2, than
             // it is to set the flag false and force host to PY3.
@@ -197,27 +139,15 @@
   @Test
   public void getHost_Py3IsDefaultFlagChangesHost() throws Exception {
     assumesDefaultIsPY2();
-    PythonOptions opts =
-        // --incompatible_py3_is_default requires --incompatible_allow_python_version_transitions
-        parsePythonOptions(
-            "--incompatible_allow_python_version_transitions=true",
-            "--incompatible_py3_is_default=true");
+    PythonOptions opts = parsePythonOptions("--incompatible_py3_is_default=true");
     PythonOptions hostOpts = (PythonOptions) opts.getHost();
     assertThat(hostOpts.getPythonVersion()).isEqualTo(PythonVersion.PY3);
   }
 
   @Test
-  public void getNormalized_OldSemantics() throws Exception {
-    PythonOptions opts =
-        parsePythonOptions("--incompatible_allow_python_version_transitions=false");
-    PythonOptions normalizedOpts = (PythonOptions) opts.getNormalized();
-    assertThat(normalizedOpts.pythonVersion).isNull();
-  }
-
-  @Test
-  public void getNormalized_NewSemantics() throws Exception {
+  public void getNormalized() throws Exception {
     assumesDefaultIsPY2();
-    PythonOptions opts = parsePythonOptions("--incompatible_allow_python_version_transitions=true");
+    PythonOptions opts = parsePythonOptions();
     PythonOptions normalizedOpts = (PythonOptions) opts.getNormalized();
     assertThat(normalizedOpts.pythonVersion).isEqualTo(PythonVersion.PY2);
   }
diff --git a/src/test/java/com/google/devtools/build/lib/rules/python/PythonSrcsVersionAspectTest.java b/src/test/java/com/google/devtools/build/lib/rules/python/PythonSrcsVersionAspectTest.java
index a097323..a80db94 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/python/PythonSrcsVersionAspectTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/python/PythonSrcsVersionAspectTest.java
@@ -22,7 +22,6 @@
 import com.google.devtools.build.lib.analysis.actions.FileWriteAction;
 import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
 import com.google.devtools.build.lib.testutil.TestConstants;
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -31,11 +30,6 @@
 @RunWith(JUnit4.class)
 public class PythonSrcsVersionAspectTest extends BuildViewTestCase {
 
-  @Before
-  public void setUp() throws Exception {
-    useConfiguration("--incompatible_allow_python_version_transitions=true");
-  }
-
   private static String join(String... args) {
     return String.join("\n", args);
   }
diff --git a/src/test/java/com/google/devtools/build/lib/rules/python/PythonStarlarkApiTest.java b/src/test/java/com/google/devtools/build/lib/rules/python/PythonStarlarkApiTest.java
index 65bb12b..fc99eac 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/python/PythonStarlarkApiTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/python/PythonStarlarkApiTest.java
@@ -98,9 +98,7 @@
 
   private void doLibrarySandwichTest(boolean legacyProviderAllowed) throws Exception {
     useConfiguration(
-        "--incompatible_disallow_legacy_py_provider=" + (legacyProviderAllowed ? "false" : "true"),
-        // Use new version semantics so we don't validate source versions in py_library.
-        "--incompatible_allow_python_version_transitions=true");
+        "--incompatible_disallow_legacy_py_provider=" + (legacyProviderAllowed ? "false" : "true"));
     defineUserlibRule(legacyProviderAllowed);
     scratch.file(
         "pkg/BUILD",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/python/PythonVersionSelectTest.java b/src/test/java/com/google/devtools/build/lib/rules/python/PythonVersionSelectTest.java
index 6b39058..ee54d6e 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/python/PythonVersionSelectTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/python/PythonVersionSelectTest.java
@@ -123,12 +123,7 @@
 
     // No --python_version, use default value.
     doTestSelectOnPythonVersionTarget(py2, "--incompatible_py3_is_default=false");
-    doTestSelectOnPythonVersionTarget(
-        py3,
-        "--incompatible_py3_is_default=true",
-        // PythonConfiguration has a validation check requiring that the new transition semantics be
-        // enabled before we're allowed to set the default to PY3.
-        "--incompatible_allow_python_version_transitions=true");
+    doTestSelectOnPythonVersionTarget(py3, "--incompatible_py3_is_default=true");
 
     // --python_version is given, use it.
     doTestSelectOnPythonVersionTarget(py2, "--python_version=PY2");
diff --git a/src/test/shell/bazel/python_version_test.sh b/src/test/shell/bazel/python_version_test.sh
index 21828d7..251e83e 100755
--- a/src/test/shell/bazel/python_version_test.sh
+++ b/src/test/shell/bazel/python_version_test.sh
@@ -271,8 +271,7 @@
 EOF
   sed s/py3bin/py2bin/ test/py2bin_calling_py3bin.py > test/py3bin_calling_py2bin.py
 
-  EXPFLAG="--incompatible_allow_python_version_transitions=true \
---incompatible_py3_is_default=false \
+  EXPFLAG="--incompatible_py3_is_default=false \
 --incompatible_py2_outputs_are_suffixed=false"
 
   bazel build $EXPFLAG //test:py2bin_calling_py3bin //test:py3bin_calling_py2bin \
@@ -357,8 +356,7 @@
 
   chmod u+x test/shbin_calling_py23bins.sh
 
-  EXPFLAG="--incompatible_allow_python_version_transitions=true \
---incompatible_py3_is_default=false \
+  EXPFLAG="--incompatible_py3_is_default=false \
 --incompatible_py2_outputs_are_suffixed=false"
 
   bazel build $EXPFLAG //test:shbin_calling_py23bins \
@@ -397,28 +395,22 @@
 EOF
   chmod u+x test/pybin.py
 
-  # Run under both old and new semantics.
-  for EXPFLAG in \
-      "--incompatible_allow_python_version_transitions=true \
---incompatible_py3_is_default=false \
---incompatible_py2_outputs_are_suffixed=false" \
-      "--incompatible_allow_python_version_transitions=false \
---incompatible_py3_is_default=false \
---incompatible_py2_outputs_are_suffixed=false"; do
-    echo "Using $EXPFLAG" > $TEST_log
-    bazel build $EXPFLAG --host_force_python=PY2 //test:genrule_calling_pybin \
-        || fail "bazel build failed"
-    ARTIFACT=$(bazel info bazel-genfiles $EXPFLAG)/test/out.txt
-    cat $ARTIFACT > $TEST_log
-    expect_log "pybin uses Python 2"
+  EXPFLAG="--incompatible_py3_is_default=false \
+--incompatible_py2_outputs_are_suffixed=false"
 
-    echo "Using $EXPFLAG" > $TEST_log
-    bazel build $EXPFLAG --host_force_python=PY3 //test:genrule_calling_pybin \
-          || fail "bazel build failed"
-      ARTIFACT=$(bazel info bazel-genfiles $EXPFLAG)/test/out.txt
-      cat $ARTIFACT > $TEST_log
-      expect_log "pybin uses Python 3"
-  done
+  echo "Using $EXPFLAG" > $TEST_log
+  bazel build $EXPFLAG --host_force_python=PY2 //test:genrule_calling_pybin \
+      || fail "bazel build failed"
+  ARTIFACT=$(bazel info bazel-genfiles $EXPFLAG)/test/out.txt
+  cat $ARTIFACT > $TEST_log
+  expect_log "pybin uses Python 2"
+
+  echo "Using $EXPFLAG" > $TEST_log
+  bazel build $EXPFLAG --host_force_python=PY3 //test:genrule_calling_pybin \
+      || fail "bazel build failed"
+  ARTIFACT=$(bazel info bazel-genfiles $EXPFLAG)/test/out.txt
+  cat $ARTIFACT > $TEST_log
+  expect_log "pybin uses Python 3"
 }
 
 function test_can_build_same_target_for_both_versions_in_one_build() {
@@ -498,8 +490,7 @@
 EOF
   chmod u+x test/shbin.sh
 
-  EXPFLAG="--incompatible_allow_python_version_transitions=true \
---incompatible_py3_is_default=false \
+  EXPFLAG="--incompatible_py3_is_default=false \
 --incompatible_py2_outputs_are_suffixed=false"
 
   bazel build $EXPFLAG //test:shbin \
diff --git a/src/test/shell/integration/python_stub_test.sh b/src/test/shell/integration/python_stub_test.sh
index ff957df..97ad092 100755
--- a/src/test/shell/integration/python_stub_test.sh
+++ b/src/test/shell/integration/python_stub_test.sh
@@ -68,12 +68,7 @@
 
 #### TESTS #############################################################
 
-# Tests that Python 2 or Python 3 is actually invoked, with and without flag
-# overrides.
-# TODO(brandjon): This test can probably be replaced by a test that simply
-# checks that Python 2 and 3 "interpreters" (the fake runtimes) can be invoked,
-# without checking the force_python behavior that's already covered in analysis
-# unit tests.
+# Tests that Python 2 or Python 3 is actually invoked.
 function test_python_version() {
   mkdir -p test
   touch test/main2.py test/main3.py
@@ -88,34 +83,12 @@
 )
 EOF
 
-  # No flag, use the default from the rule.
   bazel run //test:main2 \
       &> $TEST_log || fail "bazel run failed"
   expect_log "I am Python 2"
   bazel run //test:main3 \
       &> $TEST_log || fail "bazel run failed"
   expect_log "I am Python 3"
-
-  # These assertions try to override the version, which is legacy semantics.
-  FLAG="--incompatible_allow_python_version_transitions=false \
---incompatible_py3_is_default=false \
---incompatible_py2_outputs_are_suffixed=false"
-
-  # Force to Python 2.
-  bazel run //test:main2 $FLAG --python_version=PY2 \
-      &> $TEST_log || fail "bazel run failed"
-  expect_log "I am Python 2"
-  bazel run //test:main3 $FLAG --python_version=PY2 \
-      &> $TEST_log || fail "bazel run failed"
-  expect_log "I am Python 2"
-
-  # Force to Python 3.
-  bazel run //test:main2 $FLAG --python_version=PY3 \
-      &> $TEST_log || fail "bazel run failed"
-  expect_log "I am Python 3"
-  bazel run //test:main3 $FLAG --python_version=PY3 \
-      &> $TEST_log || fail "bazel run failed"
-  expect_log "I am Python 3"
 }
 
 function test_can_build_py_library_at_top_level_regardless_of_version() {
@@ -134,11 +107,9 @@
 EOF
   touch test/lib2.py test/lib3.py
 
-  EXPFLAG="--incompatible_allow_python_version_transitions=true"
-
-  bazel build --python_version=PY2 $EXPFLAG //test:* \
+  bazel build --python_version=PY2 //test:* \
       &> $TEST_log || fail "bazel build failed"
-  bazel build --python_version=PY3 $EXPFLAG //test:* \
+  bazel build --python_version=PY3 //test:* \
       &> $TEST_log || fail "bazel build failed"
 }
 
@@ -203,10 +174,8 @@
 EOF
 
   # Build cc at the top level, along with the outer halves of both paths to cc.
-  # Make sure to use the new version transition semantics.
   bazel build --nobuild //test:cc //test:path_A_outer //test:path_B_outer \
       --incompatible_remove_old_python_version_api=true \
-      --incompatible_allow_python_version_transitions=true \
       &> $TEST_log || fail "bazel run failed"
 }