diff --git a/recipes/README.recipes.md b/recipes/README.recipes.md
index 3ddd2db5ce..81834c74e6 100644
--- a/recipes/README.recipes.md
+++ b/recipes/README.recipes.md
@@ -162,7 +162,7 @@ PYTHON_VERSION_COMPATIBILITY: PY2+3
 
 #### **class [GclientApi](/recipes/recipe_modules/gclient/api.py#77)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
 
-  **@property**
— **def [DepsDiffException](/recipes/recipe_modules/gclient/api.py#440)(self):**
+  **@property**
— **def [DepsDiffException](/recipes/recipe_modules/gclient/api.py#424)(self):**
 
 — **def [\_\_call\_\_](/recipes/recipe_modules/gclient/api.py#87)(self, name, cmd, infra_step=True, \*\*kwargs):**
 
@@ -179,11 +179,11 @@ Return a step generator function for gclient checkouts.
 
   **@staticmethod**
— **def [config\_to\_pythonish](/recipes/recipe_modules/gclient/api.py#139)(cfg):**
 
-— **def [diff\_deps](/recipes/recipe_modules/gclient/api.py#383)(self, cwd):**
+— **def [diff\_deps](/recipes/recipe_modules/gclient/api.py#367)(self, cwd):**
 
 — **def [get\_config\_defaults](/recipes/recipe_modules/gclient/api.py#133)(self):**
 
-— **def [get\_gerrit\_patch\_root](/recipes/recipe_modules/gclient/api.py#314)(self, gclient_config=None):**
+— **def [get\_gerrit\_patch\_root](/recipes/recipe_modules/gclient/api.py#298)(self, gclient_config=None):**
 
 Returns local path to the repo where gerrit patch will be applied.
 
@@ -196,7 +196,7 @@ Instead, properly map a repository to a local path using repo_path_map.
 TODO(nodir): remove this. Update all recipe tests to specify a git_repo
 matching the recipe.
 
-— **def [get\_repo\_path](/recipes/recipe_modules/gclient/api.py#341)(self, repo_url, gclient_config=None):**
+— **def [get\_repo\_path](/recipes/recipe_modules/gclient/api.py#325)(self, repo_url, gclient_config=None):**
 
 Returns local path to the repo checkout given its url.
 
@@ -226,7 +226,7 @@ Args:
 
 — **def [runhooks](/recipes/recipe_modules/gclient/api.py#285)(self, args=None, name='runhooks', \*\*kwargs):**
 
-— **def [set\_patch\_repo\_revision](/recipes/recipe_modules/gclient/api.py#371)(self, gclient_config=None):**
+— **def [set\_patch\_repo\_revision](/recipes/recipe_modules/gclient/api.py#355)(self, gclient_config=None):**
 
 Updates config revision corresponding to patched project.
 
diff --git a/recipes/recipe_modules/gclient/api.py b/recipes/recipe_modules/gclient/api.py
index 758ed0e7c2..3f630565e1 100644
--- a/recipes/recipe_modules/gclient/api.py
+++ b/recipes/recipe_modules/gclient/api.py
@@ -292,24 +292,8 @@ class GclientApi(recipe_api.RecipeApi):
     """Remove all index.lock files. If a previous run of git crashed, bot was
     reset, etc... we might end up with leftover index.lock files.
     """
-    self.m.python.inline(
-      'cleanup index.lock',
-      """
-        from __future__ import print_function
-        import os, sys
-
-        build_path = sys.argv[1]
-        if os.path.exists(build_path):
-          for (path, dir, files) in os.walk(build_path):
-            for cur_file in files:
-              if cur_file.endswith('index.lock'):
-                path_to_file = os.path.join(path, cur_file)
-                print('deleting %s' % path_to_file)
-                os.remove(path_to_file)
-      """,
-      args=[self.m.path['start_dir']],
-      infra_step=True,
-    )
+    cmd = ['python3', '-u', self.resource('cleanup.py'), self.m.path['start_dir']]
+    return self.m.step('cleanup index.lock', cmd)
 
   def get_gerrit_patch_root(self, gclient_config=None):
     """Returns local path to the repo where gerrit patch will be applied.
@@ -398,7 +382,7 @@ class GclientApi(recipe_api.RecipeApi):
 
         step_result = self(
             'recursively git diff all DEPS',
-            ['recurse', 'python', self.resource('diff_deps.py')],
+            ['recurse', 'python3', self.resource('diff_deps.py')],
             stdout=self.m.raw_io.output_text(add_output_log=True),
         )
 
diff --git a/recipes/recipe_modules/gclient/cleanup.py b/recipes/recipe_modules/gclient/cleanup.py
new file mode 100644
index 0000000000..557baeda9a
--- /dev/null
+++ b/recipes/recipe_modules/gclient/cleanup.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python3
+
+from __future__ import print_function
+import os, sys
+
+build_path = sys.argv[1]
+if os.path.exists(build_path):
+  for (path, dir, files) in os.walk(build_path):
+    for cur_file in files:
+      if cur_file.endswith('index.lock'):
+        path_to_file = os.path.join(path, cur_file)
+        print('deleting %s' % path_to_file)
+        os.remove(path_to_file)
+
diff --git a/recipes/recipe_modules/gclient/examples/full.expected/basic.json b/recipes/recipe_modules/gclient/examples/full.expected/basic.json
index 74f6f075dd..ec99327aca 100644
--- a/recipes/recipe_modules/gclient/examples/full.expected/basic.json
+++ b/recipes/recipe_modules/gclient/examples/full.expected/basic.json
@@ -195,28 +195,12 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
-      "\nfrom __future__ import print_function\nimport os, sys\n\nbuild_path = sys.argv[1]\nif os.path.exists(build_path):\n  for (path, dir, files) in os.walk(build_path):\n    for cur_file in files:\n      if cur_file.endswith('index.lock'):\n        path_to_file = os.path.join(path, cur_file)\n        print('deleting %s' % path_to_file)\n        os.remove(path_to_file)\n",
+      "RECIPE_MODULE[depot_tools::gclient]/resources/cleanup.py",
       "[START_DIR]"
     ],
-    "infra_step": true,
-    "name": "cleanup index.lock",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@from __future__ import print_function@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@build_path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(build_path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for (path, dir, files) in os.walk(build_path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    for cur_file in files:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      if cur_file.endswith('index.lock'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@        path_to_file = os.path.join(path, cur_file)@@@",
-      "@@@STEP_LOG_LINE@python.inline@        print('deleting %s' % path_to_file)@@@",
-      "@@@STEP_LOG_LINE@python.inline@        os.remove(path_to_file)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
+    "name": "cleanup index.lock"
   },
   {
     "cmd": [
diff --git a/recipes/recipe_modules/gclient/examples/full.expected/revision.json b/recipes/recipe_modules/gclient/examples/full.expected/revision.json
index f9d42ee462..4aebaec294 100644
--- a/recipes/recipe_modules/gclient/examples/full.expected/revision.json
+++ b/recipes/recipe_modules/gclient/examples/full.expected/revision.json
@@ -317,12 +317,11 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
-      "\nfrom __future__ import print_function\nimport os, sys\n\nbuild_path = sys.argv[1]\nif os.path.exists(build_path):\n  for (path, dir, files) in os.walk(build_path):\n    for cur_file in files:\n      if cur_file.endswith('index.lock'):\n        path_to_file = os.path.join(path, cur_file)\n        print('deleting %s' % path_to_file)\n        os.remove(path_to_file)\n",
+      "RECIPE_MODULE[depot_tools::gclient]/resources/cleanup.py",
       "[START_DIR]"
     ],
-    "infra_step": true,
     "luci_context": {
       "realm": {
         "name": "project:ci"
@@ -335,22 +334,7 @@
         "hostname": "rdbhost"
       }
     },
-    "name": "cleanup index.lock",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@from __future__ import print_function@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@build_path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(build_path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for (path, dir, files) in os.walk(build_path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    for cur_file in files:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      if cur_file.endswith('index.lock'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@        path_to_file = os.path.join(path, cur_file)@@@",
-      "@@@STEP_LOG_LINE@python.inline@        print('deleting %s' % path_to_file)@@@",
-      "@@@STEP_LOG_LINE@python.inline@        os.remove(path_to_file)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
+    "name": "cleanup index.lock"
   },
   {
     "cmd": [
diff --git a/recipes/recipe_modules/gclient/examples/full.expected/tryserver.json b/recipes/recipe_modules/gclient/examples/full.expected/tryserver.json
index 4771fd068b..618a3537ad 100644
--- a/recipes/recipe_modules/gclient/examples/full.expected/tryserver.json
+++ b/recipes/recipe_modules/gclient/examples/full.expected/tryserver.json
@@ -315,12 +315,11 @@
   },
   {
     "cmd": [
-      "python",
+      "python3",
       "-u",
-      "\nfrom __future__ import print_function\nimport os, sys\n\nbuild_path = sys.argv[1]\nif os.path.exists(build_path):\n  for (path, dir, files) in os.walk(build_path):\n    for cur_file in files:\n      if cur_file.endswith('index.lock'):\n        path_to_file = os.path.join(path, cur_file)\n        print('deleting %s' % path_to_file)\n        os.remove(path_to_file)\n",
+      "RECIPE_MODULE[depot_tools::gclient]/resources/cleanup.py",
       "[START_DIR]"
     ],
-    "infra_step": true,
     "luci_context": {
       "realm": {
         "name": "project:try"
@@ -333,22 +332,7 @@
         "hostname": "rdbhost"
       }
     },
-    "name": "cleanup index.lock",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@from __future__ import print_function@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os, sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@build_path = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@if os.path.exists(build_path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for (path, dir, files) in os.walk(build_path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    for cur_file in files:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      if cur_file.endswith('index.lock'):@@@",
-      "@@@STEP_LOG_LINE@python.inline@        path_to_file = os.path.join(path, cur_file)@@@",
-      "@@@STEP_LOG_LINE@python.inline@        print('deleting %s' % path_to_file)@@@",
-      "@@@STEP_LOG_LINE@python.inline@        os.remove(path_to_file)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
+    "name": "cleanup index.lock"
   },
   {
     "cmd": [
diff --git a/recipes/recipe_modules/gclient/resources/diff_deps.py b/recipes/recipe_modules/gclient/resources/diff_deps.py
index 43f37b2595..1148d49aa3 100755
--- a/recipes/recipe_modules/gclient/resources/diff_deps.py
+++ b/recipes/recipe_modules/gclient/resources/diff_deps.py
@@ -1,4 +1,5 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
+
 from __future__ import print_function
 
 import os
diff --git a/recipes/recipe_modules/gclient/tests/diff_deps.expected/basic.json b/recipes/recipe_modules/gclient/tests/diff_deps.expected/basic.json
index 6e6c76dfa0..a9715ad9b0 100644
--- a/recipes/recipe_modules/gclient/tests/diff_deps.expected/basic.json
+++ b/recipes/recipe_modules/gclient/tests/diff_deps.expected/basic.json
@@ -31,7 +31,7 @@
       "-u",
       "RECIPE_REPO[depot_tools]/gclient.py",
       "recurse",
-      "python",
+      "python3",
       "RECIPE_MODULE[depot_tools::gclient]/resources/diff_deps.py"
     ],
     "cwd": "[CACHE]",
diff --git a/recipes/recipe_modules/gclient/tests/diff_deps.expected/dont have revision yet.json b/recipes/recipe_modules/gclient/tests/diff_deps.expected/dont have revision yet.json
index 04e47e3948..65bbbae26f 100644
--- a/recipes/recipe_modules/gclient/tests/diff_deps.expected/dont have revision yet.json	
+++ b/recipes/recipe_modules/gclient/tests/diff_deps.expected/dont have revision yet.json	
@@ -31,7 +31,7 @@
       "-u",
       "RECIPE_REPO[depot_tools]/gclient.py",
       "recurse",
-      "python",
+      "python3",
       "RECIPE_MODULE[depot_tools::gclient]/resources/diff_deps.py"
     ],
     "cwd": "[CACHE]",
@@ -100,7 +100,7 @@
       "Traceback (most recent call last):",
       "  File \"RECIPE_REPO[depot_tools]/recipes/recipe_modules/gclient/tests/diff_deps.py\", line 35, in RunSteps",
       "    affected_files = api.gclient.diff_deps(api.path['cache'])",
-      "  File \"RECIPE_REPO[depot_tools]/recipes/recipe_modules/gclient/api.py\", line 412, in diff_deps",
+      "  File \"RECIPE_REPO[depot_tools]/recipes/recipe_modules/gclient/api.py\", line 396, in diff_deps",
       "    raise self.DepsDiffException(msg)",
       "DepsDiffException('Couldn't checkout previous ref: fatal: bad object abcdef1234567890')"
     ]
diff --git a/recipes/recipe_modules/gclient/tests/diff_deps.expected/no change, exception.json b/recipes/recipe_modules/gclient/tests/diff_deps.expected/no change, exception.json
index e70c9f0fe3..2c94f48872 100644
--- a/recipes/recipe_modules/gclient/tests/diff_deps.expected/no change, exception.json	
+++ b/recipes/recipe_modules/gclient/tests/diff_deps.expected/no change, exception.json	
@@ -31,7 +31,7 @@
       "-u",
       "RECIPE_REPO[depot_tools]/gclient.py",
       "recurse",
-      "python",
+      "python3",
       "RECIPE_MODULE[depot_tools::gclient]/resources/diff_deps.py"
     ],
     "cwd": "[CACHE]",
@@ -99,7 +99,7 @@
       "Traceback (most recent call last):",
       "  File \"RECIPE_REPO[depot_tools]/recipes/recipe_modules/gclient/tests/diff_deps.py\", line 35, in RunSteps",
       "    affected_files = api.gclient.diff_deps(api.path['cache'])",
-      "  File \"RECIPE_REPO[depot_tools]/recipes/recipe_modules/gclient/api.py\", line 428, in diff_deps",
+      "  File \"RECIPE_REPO[depot_tools]/recipes/recipe_modules/gclient/api.py\", line 412, in diff_deps",
       "    raise self.DepsDiffException(msg)",
       "DepsDiffException('Unexpected result: autoroll diff found 0 files changed')"
     ]
diff --git a/recipes/recipe_modules/gclient/tests/diff_deps.expected/windows.json b/recipes/recipe_modules/gclient/tests/diff_deps.expected/windows.json
index a4079ff534..05cab1a3f2 100644
--- a/recipes/recipe_modules/gclient/tests/diff_deps.expected/windows.json
+++ b/recipes/recipe_modules/gclient/tests/diff_deps.expected/windows.json
@@ -31,7 +31,7 @@
       "-u",
       "RECIPE_REPO[depot_tools]\\gclient.py",
       "recurse",
-      "python",
+      "python3",
       "RECIPE_MODULE[depot_tools::gclient]\\resources\\diff_deps.py"
     ],
     "cwd": "[CACHE]",