[python3] Many changes

Explicitly mark all recipes and modules as compatible only with
Python 2. Change 'PY2' to 'PY2+3' when './recipes.py test train' should
block on Python 3 failures.

Replaced xrange() with range() and iteritems()/iterkeys()/itertools()
with items()/keys()/values(). Also sorting results of these in a few
places and using OrderedDict() in others so expectation files are more
likely to match between Python 2 and Python 3.

Change-Id: I752dd0d42869c419b09ce02c55c5957d57821bcf
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/18465
Reviewed-by: Godofredo Contreras <godofredoc@google.com>
Commit-Queue: Rob Mohr <mohrr@google.com>
diff --git a/recipe_modules/adhoc_validation/__init__.py b/recipe_modules/adhoc_validation/__init__.py
index 84cdf6e..6dea97c 100644
--- a/recipe_modules/adhoc_validation/__init__.py
+++ b/recipe_modules/adhoc_validation/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/osx_sdk',
     'flutter/firebase',
diff --git a/recipe_modules/adhoc_validation/examples/full.expected/invalid_validation.json b/recipe_modules/adhoc_validation/examples/full.expected/invalid_validation.json
index 62087a5..a3bb828 100644
--- a/recipe_modules/adhoc_validation/examples/full.expected/invalid_validation.json
+++ b/recipe_modules/adhoc_validation/examples/full.expected/invalid_validation.json
@@ -7,7 +7,7 @@
       "The recipe has crashed at point 'Uncaught exception'!",
       "",
       "Traceback (most recent call last):",
-      "  File \"RECIPE_REPO[flutter]/recipe_modules/adhoc_validation/examples/full.py\", line 19, in RunSteps",
+      "  File \"RECIPE_REPO[flutter]/recipe_modules/adhoc_validation/examples/full.py\", line 21, in RunSteps",
       "    api.adhoc_validation.run('Docs', validation, {}, {})",
       "  File \"RECIPE_REPO[flutter]/recipe_modules/adhoc_validation/api.py\", line 36, in run",
       "    raise AssertionError(msg)",
diff --git a/recipe_modules/adhoc_validation/examples/full.py b/recipe_modules/adhoc_validation/examples/full.py
index 193f4a5..4f14a56 100644
--- a/recipe_modules/adhoc_validation/examples/full.py
+++ b/recipe_modules/adhoc_validation/examples/full.py
@@ -4,6 +4,8 @@
 
 from recipe_engine.post_process import DoesNotRun, Filter, StatusFailure
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/adhoc_validation', 'flutter/repo_util', 'recipe_engine/context',
     'recipe_engine/path', 'recipe_engine/platform', 'recipe_engine/properties'
diff --git a/recipe_modules/android_sdk/__init__.py b/recipe_modules/android_sdk/__init__.py
index c1c701f..01f1ee8 100644
--- a/recipe_modules/android_sdk/__init__.py
+++ b/recipe_modules/android_sdk/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'recipe_engine/cipd',
     'recipe_engine/context',
diff --git a/recipe_modules/android_sdk/examples/full.py b/recipe_modules/android_sdk/examples/full.py
index fa8ad7e..fa5c5bd 100644
--- a/recipe_modules/android_sdk/examples/full.py
+++ b/recipe_modules/android_sdk/examples/full.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'android_sdk',
     'recipe_engine/context',
diff --git a/recipe_modules/android_virtual_device/__init__.py b/recipe_modules/android_virtual_device/__init__.py
index 7641e6e..01973a3 100644
--- a/recipe_modules/android_virtual_device/__init__.py
+++ b/recipe_modules/android_virtual_device/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/depot_tools',
     'flutter/repo_util',
diff --git a/recipe_modules/android_virtual_device/examples/full.py b/recipe_modules/android_virtual_device/examples/full.py
index fd3522e..49ea364 100644
--- a/recipe_modules/android_virtual_device/examples/full.py
+++ b/recipe_modules/android_virtual_device/examples/full.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'android_virtual_device',
     'recipe_engine/context',
diff --git a/recipe_modules/bucket_util/__init__.py b/recipe_modules/bucket_util/__init__.py
index c7e7d23..f00deac 100644
--- a/recipe_modules/bucket_util/__init__.py
+++ b/recipe_modules/bucket_util/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/depot_tools',
     'depot_tools/gsutil',
diff --git a/recipe_modules/bucket_util/examples/full.expected/basic with fail.json b/recipe_modules/bucket_util/examples/full.expected/basic with fail.json
index 3b9fced..6819db3 100644
--- a/recipe_modules/bucket_util/examples/full.expected/basic with fail.json
+++ b/recipe_modules/bucket_util/examples/full.expected/basic with fail.json
@@ -106,7 +106,7 @@
       "The recipe has crashed at point 'Uncaught exception'!",
       "",
       "Traceback (most recent call last):",
-      "  File \"RECIPE_REPO[flutter]/recipe_modules/bucket_util/examples/full.py\", line 60, in RunSteps",
+      "  File \"RECIPE_REPO[flutter]/recipe_modules/bucket_util/examples/full.py\", line 62, in RunSteps",
       "    add_mock=False)",
       "  File \"RECIPE_REPO[flutter]/recipe_modules/bucket_util/api.py\", line 126, in safe_upload",
       "    raise AssertionError('File not found %s' % local_path)",
diff --git a/recipe_modules/bucket_util/examples/full.expected/upload_packages_tiggers_exception_and_package_exists.json b/recipe_modules/bucket_util/examples/full.expected/upload_packages_tiggers_exception_and_package_exists.json
index 9aadcab..181b905 100644
--- a/recipe_modules/bucket_util/examples/full.expected/upload_packages_tiggers_exception_and_package_exists.json
+++ b/recipe_modules/bucket_util/examples/full.expected/upload_packages_tiggers_exception_and_package_exists.json
@@ -38,7 +38,7 @@
       "The recipe has crashed at point 'Uncaught exception'!",
       "",
       "Traceback (most recent call last):",
-      "  File \"RECIPE_REPO[flutter]/recipe_modules/bucket_util/examples/full.py\", line 22, in RunSteps",
+      "  File \"RECIPE_REPO[flutter]/recipe_modules/bucket_util/examples/full.py\", line 24, in RunSteps",
       "    'test1.zip') # zip_name",
       "  File \"RECIPE_REPO[flutter]/recipe_modules/bucket_util/api.py\", line 47, in upload_folder",
       "    bucket_name=bucket_name",
diff --git a/recipe_modules/bucket_util/examples/full.py b/recipe_modules/bucket_util/examples/full.py
index 706d995..a2d934c 100644
--- a/recipe_modules/bucket_util/examples/full.py
+++ b/recipe_modules/bucket_util/examples/full.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'bucket_util',
     'recipe_engine/buildbucket',
diff --git a/recipe_modules/build_util/__init__.py b/recipe_modules/build_util/__init__.py
index 0add518..66bdc92 100644
--- a/recipe_modules/build_util/__init__.py
+++ b/recipe_modules/build_util/__init__.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/depot_tools',
     'fuchsia/goma',
diff --git a/recipe_modules/build_util/examples/full.py b/recipe_modules/build_util/examples/full.py
index 869614c..b426f00 100644
--- a/recipe_modules/build_util/examples/full.py
+++ b/recipe_modules/build_util/examples/full.py
@@ -4,6 +4,8 @@
 
 from recipe_engine.post_process import DoesNotRun, Filter, StatusFailure
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/build_util',
     'fuchsia/goma',
diff --git a/recipe_modules/devicelab_osx_sdk/__init__.py b/recipe_modules/devicelab_osx_sdk/__init__.py
index f6fd69f..dd76b12 100644
--- a/recipe_modules/devicelab_osx_sdk/__init__.py
+++ b/recipe_modules/devicelab_osx_sdk/__init__.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'recipe_engine/cipd',
     'recipe_engine/context',
diff --git a/recipe_modules/devicelab_osx_sdk/examples/full.py b/recipe_modules/devicelab_osx_sdk/examples/full.py
index 85240d7..953be7c 100644
--- a/recipe_modules/devicelab_osx_sdk/examples/full.py
+++ b/recipe_modules/devicelab_osx_sdk/examples/full.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
   'devicelab_osx_sdk',
   'recipe_engine/platform',
diff --git a/recipe_modules/display_util/__init__.py b/recipe_modules/display_util/__init__.py
index 3f3ffa4..df175fb 100644
--- a/recipe_modules/display_util/__init__.py
+++ b/recipe_modules/display_util/__init__.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     "recipe_engine/buildbucket",
     "recipe_engine/python",
diff --git a/recipe_modules/display_util/examples/display_builds.py b/recipe_modules/display_util/examples/display_builds.py
index dac0253..d22a608 100644
--- a/recipe_modules/display_util/examples/display_builds.py
+++ b/recipe_modules/display_util/examples/display_builds.py
@@ -5,6 +5,8 @@
 from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb2
 from recipe_engine.recipe_api import Property
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     "flutter/display_util",
     "fuchsia/status_check",
diff --git a/recipe_modules/display_util/examples/display_tasks.py b/recipe_modules/display_util/examples/display_tasks.py
index 5f55f6a..0b92537 100644
--- a/recipe_modules/display_util/examples/display_tasks.py
+++ b/recipe_modules/display_util/examples/display_tasks.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     "flutter/display_util",
     "fuchsia/status_check",
diff --git a/recipe_modules/firebase/__init__.py b/recipe_modules/firebase/__init__.py
index b2e6aef..b06b2aa 100644
--- a/recipe_modules/firebase/__init__.py
+++ b/recipe_modules/firebase/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'recipe_engine/context',
     'recipe_engine/path',
diff --git a/recipe_modules/firebase/examples/full.py b/recipe_modules/firebase/examples/full.py
index 78a0493..75b97a3 100644
--- a/recipe_modules/firebase/examples/full.py
+++ b/recipe_modules/firebase/examples/full.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/firebase',
     'recipe_engine/path',
diff --git a/recipe_modules/flutter_deps/__init__.py b/recipe_modules/flutter_deps/__init__.py
index 9c1ba9e..a384983 100644
--- a/recipe_modules/flutter_deps/__init__.py
+++ b/recipe_modules/flutter_deps/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/depot_tools',
     'flutter/android_sdk',
diff --git a/recipe_modules/flutter_deps/examples/full.py b/recipe_modules/flutter_deps/examples/full.py
index 767d269..a5b39b1 100644
--- a/recipe_modules/flutter_deps/examples/full.py
+++ b/recipe_modules/flutter_deps/examples/full.py
@@ -4,6 +4,8 @@
 
 from recipe_engine.post_process import DoesNotRun, Filter, StatusFailure
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/flutter_deps',
     'flutter/repo_util',
diff --git a/recipe_modules/fuchsia_util/__init__.py b/recipe_modules/fuchsia_util/__init__.py
index 61016c2..e090ace 100644
--- a/recipe_modules/fuchsia_util/__init__.py
+++ b/recipe_modules/fuchsia_util/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/gsutil',
     'flutter/repo_util',
diff --git a/recipe_modules/fuchsia_util/examples/full.py b/recipe_modules/fuchsia_util/examples/full.py
index 2781013..a0cc00f 100644
--- a/recipe_modules/fuchsia_util/examples/full.py
+++ b/recipe_modules/fuchsia_util/examples/full.py
@@ -4,6 +4,8 @@
 
 from recipe_engine.recipe_api import Property
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/fuchsia_util',
     'flutter/repo_util',
diff --git a/recipe_modules/job/__init__.py b/recipe_modules/job/__init__.py
index 5f3ac86..df3316f 100644
--- a/recipe_modules/job/__init__.py
+++ b/recipe_modules/job/__init__.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     "recipe_engine/buildbucket",
     "recipe_engine/file",
diff --git a/recipe_modules/job/api.py b/recipe_modules/job/api.py
index c5a382a..5312b5c 100644
--- a/recipe_modules/job/api.py
+++ b/recipe_modules/job/api.py
@@ -79,9 +79,9 @@
         "luci.%s.%s:%s" % (current.project, current.bucket, current.builder),
     )
     edit_args = []
-    for k, v in job.properties.iteritems():
+    for k, v in sorted(job.properties.items()):
       edit_args.extend(["-p", "%s=%s" % (k, self.m.json.dumps(v))])
-    for k, v in job.dimensions.iteritems():
+    for k, v in sorted(job.dimensions.items()):
       edit_args.extend(["-d", "%s=%s" % (k, v)])
     led_data = led_data.then("edit", *edit_args)
     led_data = self.m.led.inject_input_recipes(led_data)
diff --git a/recipe_modules/job/examples/full.expected/launch.json b/recipe_modules/job/examples/full.expected/launch.json
index 33e31fd..8cb83d6 100644
--- a/recipe_modules/job/examples/full.expected/launch.json
+++ b/recipe_modules/job/examples/full.expected/launch.json
@@ -43,9 +43,9 @@
       "-p",
       "foo=[\"a\", \"b\"]",
       "-p",
-      "recipe=\"fake_recipe\"",
-      "-p",
       "name=\"fake_job0\"",
+      "-p",
+      "recipe=\"fake_recipe\"",
       "-d",
       "id=fake_bot_id",
       "-d",
diff --git a/recipe_modules/job/examples/full.py b/recipe_modules/job/examples/full.py
index 33a21ae..a5c2082 100644
--- a/recipe_modules/job/examples/full.py
+++ b/recipe_modules/job/examples/full.py
@@ -4,6 +4,8 @@
 
 from recipe_engine.recipe_api import Property
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     "flutter/job",
     "recipe_engine/properties",
diff --git a/recipe_modules/kms/__init__.py b/recipe_modules/kms/__init__.py
index af64233..d742e27 100644
--- a/recipe_modules/kms/__init__.py
+++ b/recipe_modules/kms/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/gsutil',
     'recipe_engine/cipd',
diff --git a/recipe_modules/kms/examples/full.py b/recipe_modules/kms/examples/full.py
index 475389d..6e6f14f 100644
--- a/recipe_modules/kms/examples/full.py
+++ b/recipe_modules/kms/examples/full.py
@@ -4,6 +4,8 @@
 
 from recipe_engine.post_process import (Filter)
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'kms',
     'recipe_engine/path',
diff --git a/recipe_modules/logs_util/__init__.py b/recipe_modules/logs_util/__init__.py
index 87d5767..2f008d2 100644
--- a/recipe_modules/logs_util/__init__.py
+++ b/recipe_modules/logs_util/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/gsutil',
     'recipe_engine/buildbucket',
diff --git a/recipe_modules/logs_util/examples/full.py b/recipe_modules/logs_util/examples/full.py
index 573e2c6..9fc9ec0 100644
--- a/recipe_modules/logs_util/examples/full.py
+++ b/recipe_modules/logs_util/examples/full.py
@@ -5,6 +5,8 @@
 from PB.recipe_modules.recipe_engine.swarming import properties
 from recipe_engine.post_process import DoesNotRun, Filter, StatusFailure
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/logs_util',
 ]
diff --git a/recipe_modules/os_utils/__init__.py b/recipe_modules/os_utils/__init__.py
index 6c8086c..2281e53 100644
--- a/recipe_modules/os_utils/__init__.py
+++ b/recipe_modules/os_utils/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/repo_util',
     'recipe_engine/context',
diff --git a/recipe_modules/os_utils/examples/full.py b/recipe_modules/os_utils/examples/full.py
index 9581be6..7120482 100644
--- a/recipe_modules/os_utils/examples/full.py
+++ b/recipe_modules/os_utils/examples/full.py
@@ -5,6 +5,8 @@
 from PB.recipe_modules.recipe_engine.swarming import properties
 from recipe_engine.post_process import DoesNotRun, Filter, StatusFailure
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/os_utils',
     'recipe_engine/platform',
diff --git a/recipe_modules/osx_sdk/__init__.py b/recipe_modules/osx_sdk/__init__.py
index d6fa658..e90a9e0 100644
--- a/recipe_modules/osx_sdk/__init__.py
+++ b/recipe_modules/osx_sdk/__init__.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'recipe_engine/cipd',
     'recipe_engine/context',
diff --git a/recipe_modules/osx_sdk/examples/full.py b/recipe_modules/osx_sdk/examples/full.py
index 5fdc9be..f8c9f03 100644
--- a/recipe_modules/osx_sdk/examples/full.py
+++ b/recipe_modules/osx_sdk/examples/full.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
   'osx_sdk',
   'recipe_engine/platform',
diff --git a/recipe_modules/repo_util/__init__.py b/recipe_modules/repo_util/__init__.py
index 4b37fb1..a190e72 100644
--- a/recipe_modules/repo_util/__init__.py
+++ b/recipe_modules/repo_util/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/bot_update',
     'depot_tools/depot_tools',
diff --git a/recipe_modules/repo_util/examples/full.py b/recipe_modules/repo_util/examples/full.py
index 0739934..b914f10 100644
--- a/recipe_modules/repo_util/examples/full.py
+++ b/recipe_modules/repo_util/examples/full.py
@@ -4,6 +4,8 @@
 
 from recipe_engine.post_process import DoesNotRun, Filter, StatusFailure
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/repo_util',
     'recipe_engine/context',
diff --git a/recipe_modules/repo_util/examples/unsupported.expected/unsupported.json b/recipe_modules/repo_util/examples/unsupported.expected/unsupported.json
index 871711b..d50eb79 100644
--- a/recipe_modules/repo_util/examples/unsupported.expected/unsupported.json
+++ b/recipe_modules/repo_util/examples/unsupported.expected/unsupported.json
@@ -7,7 +7,7 @@
       "The recipe has crashed at point 'Uncaught exception'!",
       "",
       "Traceback (most recent call last):",
-      "  File \"RECIPE_REPO[flutter]/recipe_modules/repo_util/examples/unsupported.py\", line 15, in RunSteps",
+      "  File \"RECIPE_REPO[flutter]/recipe_modules/repo_util/examples/unsupported.py\", line 17, in RunSteps",
       "    api.path['start_dir'].join('unsupported_repo'))",
       "  File \"RECIPE_REPO[flutter]/recipe_modules/repo_util/api.py\", line 100, in checkout",
       "    raise ValueError('Unsupported repo: %s' % name)",
diff --git a/recipe_modules/repo_util/examples/unsupported.py b/recipe_modules/repo_util/examples/unsupported.py
index f812142..96ed4dc 100644
--- a/recipe_modules/repo_util/examples/unsupported.py
+++ b/recipe_modules/repo_util/examples/unsupported.py
@@ -4,6 +4,8 @@
 
 from recipe_engine.post_process import DoesNotRun, Filter, StatusFailure
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/repo_util',
     'recipe_engine/path',
diff --git a/recipe_modules/retry/__init__.py b/recipe_modules/retry/__init__.py
index 89cb20b..65ed86e 100644
--- a/recipe_modules/retry/__init__.py
+++ b/recipe_modules/retry/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/test_utils',
     'recipe_engine/raw_io',
diff --git a/recipe_modules/retry/examples/full.py b/recipe_modules/retry/examples/full.py
index 6cb73ac..e0aae8f 100644
--- a/recipe_modules/retry/examples/full.py
+++ b/recipe_modules/retry/examples/full.py
@@ -4,6 +4,8 @@
 
 from recipe_engine.recipe_api import Property
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/retry',
     'recipe_engine/properties',
diff --git a/recipe_modules/shard_util/__init__.py b/recipe_modules/shard_util/__init__.py
index 28fa90e..6ca9b38 100644
--- a/recipe_modules/shard_util/__init__.py
+++ b/recipe_modules/shard_util/__init__.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'recipe_engine/buildbucket',
     'recipe_engine/platform',
diff --git a/recipe_modules/shard_util/examples/full.py b/recipe_modules/shard_util/examples/full.py
index 509ef1a..2103227 100644
--- a/recipe_modules/shard_util/examples/full.py
+++ b/recipe_modules/shard_util/examples/full.py
@@ -4,6 +4,8 @@
 
 from recipe_engine.post_process import DoesNotRun, Filter, StatusFailure
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/shard_util',
     'recipe_engine/properties',
diff --git a/recipe_modules/shard_util_v2/__init__.py b/recipe_modules/shard_util_v2/__init__.py
index e534fb4..270f4cc 100644
--- a/recipe_modules/shard_util_v2/__init__.py
+++ b/recipe_modules/shard_util_v2/__init__.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'recipe_engine/buildbucket',
     'recipe_engine/file',
diff --git a/recipe_modules/shard_util_v2/api.py b/recipe_modules/shard_util_v2/api.py
index 91df744..98e6392 100644
--- a/recipe_modules/shard_util_v2/api.py
+++ b/recipe_modules/shard_util_v2/api.py
@@ -5,6 +5,7 @@
 DRONE_TIMEOUT_SECS = 3600 * 3  # 3 hours.
 
 import attr
+import collections
 
 from google.protobuf import json_format
 from recipe_engine import recipe_api
@@ -41,8 +42,8 @@
 
   def unfreeze_dict(self, dictionary):
     """Creates a mutable dictionary out of a FrozenDict."""
-    result = {}
-    for k, v in dictionary.items():
+    result = collections.OrderedDict()
+    for k, v in sorted(dictionary.items()):
       if isinstance(v, engine_types.FrozenDict):
         result[k] = self.unfreeze_dict(v)
       elif isinstance(v, (list, tuple)):
@@ -65,7 +66,7 @@
     This is because the proto structures can not be passed to the BuildBucket or led
     requests.
     """
-    return {k: v for k, v in struct.items()}
+    return collections.OrderedDict((k, v) for k, v in struct.items())
 
   def schedule_builds(self, builds, presentation):
     """Schedule builds using the builds configurations.
@@ -163,7 +164,7 @@
             "luci.%s.%s:%s" % (parent.project, parent.bucket, builder_name),
         )
         edit_args = []
-        for k, v in drone_properties.items():
+        for k, v in sorted(drone_properties.items()):
           edit_args.extend(["-p", "%s=%s" % (k, self.m.json.dumps(v))])
         # led reduces the priority of tasks by 10 from their values in
         # buildbucket which we do not want.
@@ -218,11 +219,11 @@
         task_dimensions.append(common_pb2.RequestedDimension(key=k, value=v))
       # Override recipe.
       drone_properties['recipe'] = recipe_name
-      properties = {
-          key: val
-          for key, val in drone_properties.items()
+      properties = collections.OrderedDict(
+          (key, val)
+          for key, val in sorted(drone_properties.items())
           if key not in PROPERTIES_TO_REMOVE
-      }
+      )
       task_names.append(task_name)
       req = self.m.buildbucket.schedule_request(
           swarming_parent_run_id=self.m.swarming.task_id,
@@ -315,7 +316,7 @@
     """
     build_ids = [build.build_id for build in tasks.values()]
     build_id_to_name = {
-        int(build.build_id): build.build_name for build in tasks.values()
+        int(build.build_id): build.build_name for build in sorted(tasks.values())
     }
     bb_fields = self.m.buildbucket.DEFAULT_FIELDS.union({
         "infra.swarming.task_id",
@@ -332,7 +333,7 @@
         fields=bb_fields,
     )
     failed_builds = [
-        b for b in builds.values() if b.status != common_pb2.SUCCESS
+        b for b in sorted(builds.values()) if b.status != common_pb2.SUCCESS
     ]
     if failed_builds:
       task_ids = [b.infra.swarming.task_id for b in failed_builds]
@@ -366,7 +367,7 @@
       self.m.swarming.collect(
           "wait for %s to complete" % pluralize("task", task_ids), task_ids
       )
-    for build_id, build in builds.iteritems():
+    for build_id, build in sorted(builds.items()):
       builds[build_id] = SubbuildResult(
           builder=build.builder.builder,
           build_id=build_id,
diff --git a/recipe_modules/shard_util_v2/examples/full.expected/presubmit_led.json b/recipe_modules/shard_util_v2/examples/full.expected/presubmit_led.json
index 25246dc..cc7add8 100644
--- a/recipe_modules/shard_util_v2/examples/full.expected/presubmit_led.json
+++ b/recipe_modules/shard_util_v2/examples/full.expected/presubmit_led.json
@@ -33,25 +33,25 @@
       "led",
       "edit",
       "-p",
-      "tests=[{\"dependencies\": [\"ios_debug\"], \"name\": \"felt_test\", \"parameters\": [\"test\"], \"scripts\": [\"out/script.sh\"]}]",
+      "$recipe_engine/buildbucket={\"build\": {\"builder\": {\"bucket\": \"try\", \"builder\": \"try-builder\", \"project\": \"proj\"}, \"createTime\": \"2018-05-25T23:50:17Z\", \"createdBy\": \"user:commit-bot@chromium.org\", \"id\": \"8945511751514863184\", \"infra\": {\"resultdb\": {\"invocation\": \"invocations/build:8945511751514863184\"}, \"swarming\": {\"priority\": 30}}, \"input\": {\"gerritChanges\": [{\"change\": \"123456\", \"host\": \"github.com\", \"patchset\": \"7\", \"project\": \"repo/a\"}], \"gitilesCommit\": {\"host\": \"github.com\", \"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \"project\": \"repo/a\", \"ref\": \"refs/heads/main\"}}, \"number\": 123, \"tags\": [{\"key\": \"cq_experimental\", \"value\": \"false\"}]}}",
       "-p",
       "$recipe_engine/led={\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \"rbe_cas_input\": {\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \"digest\": {\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \"size_bytes\": 91}}}",
       "-p",
+      "build={\"drone_dimensions\": [\"dimension1=abc\"], \"gn\": [], \"name\": \"ios_debug\", \"ninja\": [\"ios_debug\"]}",
+      "-p",
+      "dependencies=[{\"dependency\": \"android_sdk\"}, {\"dependency\": \"chrome_and_driver\"}]",
+      "-p",
+      "environment=\"Staging\"",
+      "-p",
+      "git_ref=\"refs/123/master\"",
+      "-p",
+      "git_url=\"http://abc\"",
+      "-p",
       "recipe=\"engine_v2/builder\"",
       "-p",
       "task_name=\"ios_debug\"",
       "-p",
-      "environment=\"Staging\"",
-      "-p",
-      "dependencies=[{\"dependency\": \"android_sdk\"}, {\"dependency\": \"chrome_and_driver\"}]",
-      "-p",
-      "git_url=\"http://abc\"",
-      "-p",
-      "$recipe_engine/buildbucket={\"build\": {\"builder\": {\"bucket\": \"try\", \"builder\": \"try-builder\", \"project\": \"proj\"}, \"createTime\": \"2018-05-25T23:50:17Z\", \"createdBy\": \"user:commit-bot@chromium.org\", \"id\": \"8945511751514863184\", \"infra\": {\"resultdb\": {\"invocation\": \"invocations/build:8945511751514863184\"}, \"swarming\": {\"priority\": 30}}, \"input\": {\"gerritChanges\": [{\"change\": \"123456\", \"host\": \"github.com\", \"patchset\": \"7\", \"project\": \"repo/a\"}], \"gitilesCommit\": {\"host\": \"github.com\", \"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \"project\": \"repo/a\", \"ref\": \"refs/heads/main\"}}, \"number\": 123, \"tags\": [{\"key\": \"cq_experimental\", \"value\": \"false\"}]}}",
-      "-p",
-      "git_ref=\"refs/123/master\"",
-      "-p",
-      "build={\"drone_dimensions\": [\"dimension1=abc\"], \"gn\": [], \"name\": \"ios_debug\", \"ninja\": [\"ios_debug\"]}"
+      "tests=[{\"dependencies\": [\"ios_debug\"], \"name\": \"felt_test\", \"parameters\": [\"test\"], \"scripts\": [\"out/script.sh\"]}]"
     ],
     "name": "launch builds.led edit",
     "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}\n}\n}\n}\n}",
@@ -1084,25 +1084,25 @@
       "led",
       "edit",
       "-p",
-      "tests=[{\"dependencies\": [\"ios_debug\"], \"name\": \"felt_test\", \"parameters\": [\"test\"], \"scripts\": [\"out/script.sh\"]}]",
+      "$recipe_engine/buildbucket={\"build\": {\"builder\": {\"bucket\": \"try\", \"builder\": \"try-builder\", \"project\": \"proj\"}, \"createTime\": \"2018-05-25T23:50:17Z\", \"createdBy\": \"user:commit-bot@chromium.org\", \"id\": \"8945511751514863184\", \"infra\": {\"resultdb\": {\"invocation\": \"invocations/build:8945511751514863184\"}, \"swarming\": {\"priority\": 30}}, \"input\": {\"gerritChanges\": [{\"change\": \"123456\", \"host\": \"github.com\", \"patchset\": \"7\", \"project\": \"repo/a\"}], \"gitilesCommit\": {\"host\": \"github.com\", \"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \"project\": \"repo/a\", \"ref\": \"refs/heads/main\"}}, \"number\": 123, \"tags\": [{\"key\": \"cq_experimental\", \"value\": \"false\"}]}}",
       "-p",
       "$recipe_engine/led={\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \"rbe_cas_input\": {\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \"digest\": {\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \"size_bytes\": 91}}}",
       "-p",
+      "build={\"dependencies\": [\"ios_debug\"], \"name\": \"felt_test\", \"parameters\": [\"test\"], \"resolved_deps\": [{\"ios_debug\": \"bcd\", \"web_tests\": \"abc\"}], \"scripts\": [\"out/script.sh\"]}",
+      "-p",
+      "dependencies=[{\"dependency\": \"android_sdk\"}, {\"dependency\": \"chrome_and_driver\"}]",
+      "-p",
+      "environment=\"Staging\"",
+      "-p",
+      "git_ref=\"refs/123/master\"",
+      "-p",
+      "git_url=\"http://abc\"",
+      "-p",
       "recipe=\"engine_v2/tester\"",
       "-p",
       "task_name=\"felt_test\"",
       "-p",
-      "environment=\"Staging\"",
-      "-p",
-      "dependencies=[{\"dependency\": \"android_sdk\"}, {\"dependency\": \"chrome_and_driver\"}]",
-      "-p",
-      "git_url=\"http://abc\"",
-      "-p",
-      "$recipe_engine/buildbucket={\"build\": {\"builder\": {\"bucket\": \"try\", \"builder\": \"try-builder\", \"project\": \"proj\"}, \"createTime\": \"2018-05-25T23:50:17Z\", \"createdBy\": \"user:commit-bot@chromium.org\", \"id\": \"8945511751514863184\", \"infra\": {\"resultdb\": {\"invocation\": \"invocations/build:8945511751514863184\"}, \"swarming\": {\"priority\": 30}}, \"input\": {\"gerritChanges\": [{\"change\": \"123456\", \"host\": \"github.com\", \"patchset\": \"7\", \"project\": \"repo/a\"}], \"gitilesCommit\": {\"host\": \"github.com\", \"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \"project\": \"repo/a\", \"ref\": \"refs/heads/main\"}}, \"number\": 123, \"tags\": [{\"key\": \"cq_experimental\", \"value\": \"false\"}]}}",
-      "-p",
-      "git_ref=\"refs/123/master\"",
-      "-p",
-      "build={\"dependencies\": [\"ios_debug\"], \"name\": \"felt_test\", \"parameters\": [\"test\"], \"resolved_deps\": [{\"ios_debug\": \"bcd\", \"web_tests\": \"abc\"}], \"scripts\": [\"out/script.sh\"]}"
+      "tests=[{\"dependencies\": [\"ios_debug\"], \"name\": \"felt_test\", \"parameters\": [\"test\"], \"scripts\": [\"out/script.sh\"]}]"
     ],
     "name": "launch builds (2).led edit",
     "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}\n}\n}\n}\n}",
diff --git a/recipe_modules/shard_util_v2/examples/full.py b/recipe_modules/shard_util_v2/examples/full.py
index a565a78..37551ad 100644
--- a/recipe_modules/shard_util_v2/examples/full.py
+++ b/recipe_modules/shard_util_v2/examples/full.py
@@ -10,6 +10,8 @@
 from recipe_engine.post_process import DoesNotRun, Filter, StatusFailure
 from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb2
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/shard_util_v2',
     'fuchsia/buildbucket_util',
@@ -27,7 +29,7 @@
     reqs = api.shard_util_v2.schedule_builds(build_configs, presentation)
   with api.step.nest("collect builds") as presentation:
     builds = api.shard_util_v2.collect(reqs, presentation)
-    for build in builds.itervalues():
+    for build in builds.values():
       if build.build_proto.status != common_pb2.SUCCESS:
         raise api.step.StepFailure("build %s failed" % build.build_id)
   with api.step.nest("launch builds") as presentation:
diff --git a/recipe_modules/test_utils/__init__.py b/recipe_modules/test_utils/__init__.py
index 8bff4d3..6c1396e 100644
--- a/recipe_modules/test_utils/__init__.py
+++ b/recipe_modules/test_utils/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'recipe_engine/platform',
     'recipe_engine/raw_io',
diff --git a/recipe_modules/test_utils/examples/full.py b/recipe_modules/test_utils/examples/full.py
index 49e849a..fab5840 100644
--- a/recipe_modules/test_utils/examples/full.py
+++ b/recipe_modules/test_utils/examples/full.py
@@ -5,6 +5,8 @@
 from PB.recipe_modules.recipe_engine.swarming import properties
 from recipe_engine.post_process import DoesNotRun, Filter, StatusFailure
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/test_utils',
     'recipe_engine/platform',
diff --git a/recipe_modules/token_util/__init__.py b/recipe_modules/token_util/__init__.py
index 79334ab..f15ffea 100644
--- a/recipe_modules/token_util/__init__.py
+++ b/recipe_modules/token_util/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'recipe_engine/file',
     'recipe_engine/path',
diff --git a/recipe_modules/token_util/examples/full.py b/recipe_modules/token_util/examples/full.py
index 26e7da1..0f23444 100644
--- a/recipe_modules/token_util/examples/full.py
+++ b/recipe_modules/token_util/examples/full.py
@@ -4,6 +4,8 @@
 
 from recipe_engine.recipe_api import Property
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'flutter/token_util',
 ]
@@ -16,4 +18,3 @@
 
 def GenTests(api):
   yield api.test('basic')
-
diff --git a/recipe_modules/web_util/__init__.py b/recipe_modules/web_util/__init__.py
index b60c78a..b1eb5c9 100644
--- a/recipe_modules/web_util/__init__.py
+++ b/recipe_modules/web_util/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/gsutil',
     'depot_tools/git',
diff --git a/recipe_modules/web_util/examples/full.expected/fail case.json b/recipe_modules/web_util/examples/full.expected/fail case.json
index eede892..f443e31 100644
--- a/recipe_modules/web_util/examples/full.expected/fail case.json
+++ b/recipe_modules/web_util/examples/full.expected/fail case.json
@@ -7,7 +7,7 @@
       "The recipe has crashed at point 'Uncaught exception'!",
       "",
       "Traceback (most recent call last):",
-      "  File \"RECIPE_REPO[flutter]/recipe_modules/web_util/examples/full.py\", line 23, in RunSteps",
+      "  File \"RECIPE_REPO[flutter]/recipe_modules/web_util/examples/full.py\", line 25, in RunSteps",
       "    api.web_util.prepare_dependencies(engine_checkout_path)",
       "  File \"RECIPE_REPO[flutter]/recipe_modules/web_util/api.py\", line 172, in prepare_dependencies",
       "    raise ValueError('Dependency %s not available.' % dep)",
diff --git a/recipe_modules/web_util/examples/full.py b/recipe_modules/web_util/examples/full.py
index da7f572..1fe7d4d 100644
--- a/recipe_modules/web_util/examples/full.py
+++ b/recipe_modules/web_util/examples/full.py
@@ -4,6 +4,8 @@
 
 from recipe_engine.post_process import DoesNotRun, Filter, StatusFailure
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/gsutil',
     'flutter/web_util',
diff --git a/recipe_modules/yaml/__init__.py b/recipe_modules/yaml/__init__.py
index 2f187ed..702dbd2 100644
--- a/recipe_modules/yaml/__init__.py
+++ b/recipe_modules/yaml/__init__.py
@@ -1,3 +1,5 @@
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'recipe_engine/json',
     'recipe_engine/python',
diff --git a/recipe_modules/yaml/examples/full.py b/recipe_modules/yaml/examples/full.py
index d5c44c3..104d34a 100644
--- a/recipe_modules/yaml/examples/full.py
+++ b/recipe_modules/yaml/examples/full.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'yaml',
     'recipe_engine/json',
diff --git a/recipe_modules/zip/__init__.py b/recipe_modules/zip/__init__.py
index be65e53..615c40d 100644
--- a/recipe_modules/zip/__init__.py
+++ b/recipe_modules/zip/__init__.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
   'recipe_engine/python',
   'recipe_engine/path',
diff --git a/recipe_modules/zip/examples/full.py b/recipe_modules/zip/examples/full.py
index 36b8211..4428fb5 100644
--- a/recipe_modules/zip/examples/full.py
+++ b/recipe_modules/zip/examples/full.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
   'recipe_engine/context',
   'recipe_engine/file',
diff --git a/recipes/engine.py b/recipes/engine.py
index 86eddbf..44ab91f 100644
--- a/recipes/engine.py
+++ b/recipes/engine.py
@@ -10,6 +10,8 @@
 from PB.go.chromium.org.luci.buildbucket.proto import build as build_pb2
 from google.protobuf import struct_pb2
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/bot_update',
     'depot_tools/depot_tools',
diff --git a/recipes/engine_2_5_0.py b/recipes/engine_2_5_0.py
index b740a71..9686c9d 100644
--- a/recipes/engine_2_5_0.py
+++ b/recipes/engine_2_5_0.py
@@ -10,6 +10,8 @@
 from PB.go.chromium.org.luci.buildbucket.proto import build as build_pb2
 from google.protobuf import struct_pb2
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/bot_update',
     'depot_tools/depot_tools',
diff --git a/recipes/engine_2_6_0.py b/recipes/engine_2_6_0.py
index 6089b7c..fb029f9 100644
--- a/recipes/engine_2_6_0.py
+++ b/recipes/engine_2_6_0.py
@@ -10,6 +10,8 @@
 from PB.go.chromium.org.luci.buildbucket.proto import build as build_pb2
 from google.protobuf import struct_pb2
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/bot_update',
     'depot_tools/depot_tools',
diff --git a/recipes/engine_builder.py b/recipes/engine_builder.py
index 45b6430..9959597 100644
--- a/recipes/engine_builder.py
+++ b/recipes/engine_builder.py
@@ -7,6 +7,8 @@
 
 from PB.recipes.flutter.engine_builder import InputProperties, EngineBuild
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
   'depot_tools/bot_update',
   'depot_tools/depot_tools',
diff --git a/recipes/engine_unopt.py b/recipes/engine_unopt.py
index b17c46f..f38832e 100644
--- a/recipes/engine_unopt.py
+++ b/recipes/engine_unopt.py
@@ -7,6 +7,8 @@
 from PB.recipes.flutter.engine_unopt import InputProperties
 from PB.recipes.flutter.engine_unopt import EnvProperties
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/depot_tools',
     'depot_tools/gclient',
diff --git a/recipes/engine_unopt_2_5_0.py b/recipes/engine_unopt_2_5_0.py
index bdb29a929..22edc93 100644
--- a/recipes/engine_unopt_2_5_0.py
+++ b/recipes/engine_unopt_2_5_0.py
@@ -7,6 +7,8 @@
 from PB.recipes.flutter.engine_unopt import InputProperties
 from PB.recipes.flutter.engine_unopt import EnvProperties
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/depot_tools',
     'depot_tools/gclient',
diff --git a/recipes/femu_test.py b/recipes/femu_test.py
index dbe002c..f011968 100644
--- a/recipes/femu_test.py
+++ b/recipes/femu_test.py
@@ -12,6 +12,8 @@
 
 import re
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/depot_tools',
     'flutter/repo_util',
diff --git a/recipes/femu_test_2_5_0.py b/recipes/femu_test_2_5_0.py
index 001462c..de41f49 100644
--- a/recipes/femu_test_2_5_0.py
+++ b/recipes/femu_test_2_5_0.py
@@ -12,6 +12,8 @@
 
 import re
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/depot_tools',
     'flutter/repo_util',
diff --git a/recipes/femu_test_2_6_0.py b/recipes/femu_test_2_6_0.py
index 371634c..00afcca 100644
--- a/recipes/femu_test_2_6_0.py
+++ b/recipes/femu_test_2_6_0.py
@@ -12,6 +12,8 @@
 
 import re
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/depot_tools',
     'flutter/repo_util',
diff --git a/recipes/flutter.py b/recipes/flutter.py
index 7074c85..9007024 100644
--- a/recipes/flutter.py
+++ b/recipes/flutter.py
@@ -5,6 +5,8 @@
 from contextlib import contextmanager
 import re
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/depot_tools',
     'depot_tools/git',
diff --git a/recipes/flutter_2_5_0.py b/recipes/flutter_2_5_0.py
index 7074c85..9007024 100644
--- a/recipes/flutter_2_5_0.py
+++ b/recipes/flutter_2_5_0.py
@@ -5,6 +5,8 @@
 from contextlib import contextmanager
 import re
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/depot_tools',
     'depot_tools/git',
diff --git a/recipes/flutter_2_6_0.py b/recipes/flutter_2_6_0.py
index 7074c85..9007024 100644
--- a/recipes/flutter_2_6_0.py
+++ b/recipes/flutter_2_6_0.py
@@ -5,6 +5,8 @@
 from contextlib import contextmanager
 import re
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/depot_tools',
     'depot_tools/git',
diff --git a/recipes/fuchsia_ctl.py b/recipes/fuchsia_ctl.py
index 96f3049..7e4a478 100644
--- a/recipes/fuchsia_ctl.py
+++ b/recipes/fuchsia_ctl.py
@@ -4,6 +4,8 @@
 
 from recipe_engine.recipe_api import Property
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/git',
     'fuchsia/display_util',
diff --git a/recipes/ios-usb-dependencies.py b/recipes/ios-usb-dependencies.py
index 281f5ac..7c748e5 100644
--- a/recipes/ios-usb-dependencies.py
+++ b/recipes/ios-usb-dependencies.py
@@ -4,6 +4,8 @@
 
 from contextlib import contextmanager
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/depot_tools',
     'depot_tools/git',
diff --git a/recipes/recipes.py b/recipes/recipes.py
index 9475073..9c47a3f 100644
--- a/recipes/recipes.py
+++ b/recipes/recipes.py
@@ -5,6 +5,9 @@
 """Recipe for testing recipes."""
 
 from recipe_engine.recipe_api import Property
+
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'fuchsia/git',
     'fuchsia/commit_queue',
diff --git a/recipes/web_engine.py b/recipes/web_engine.py
index aac785f..5110e0b 100644
--- a/recipes/web_engine.py
+++ b/recipes/web_engine.py
@@ -12,6 +12,8 @@
 from PB.recipes.flutter.engine import InputProperties
 from PB.recipes.flutter.engine import EnvProperties
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/bot_update',
     'depot_tools/depot_tools',
diff --git a/recipes/web_engine_2_5_0.py b/recipes/web_engine_2_5_0.py
index f41b841..90ac532 100644
--- a/recipes/web_engine_2_5_0.py
+++ b/recipes/web_engine_2_5_0.py
@@ -12,6 +12,8 @@
 from PB.recipes.flutter.engine import InputProperties
 from PB.recipes.flutter.engine import EnvProperties
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/bot_update',
     'depot_tools/depot_tools',
diff --git a/recipes/web_engine_2_6_0.py b/recipes/web_engine_2_6_0.py
index f41b841..90ac532 100644
--- a/recipes/web_engine_2_6_0.py
+++ b/recipes/web_engine_2_6_0.py
@@ -12,6 +12,8 @@
 from PB.recipes.flutter.engine import InputProperties
 from PB.recipes.flutter.engine import EnvProperties
 
+PYTHON_VERSION_COMPATIBILITY = 'PY2'
+
 DEPS = [
     'depot_tools/bot_update',
     'depot_tools/depot_tools',