Repository: pre-commit/pre-commit
Pull request #915 (2019-01-10 22:40:31)

Be more timid about choosing a shebang
see Homebrew/homebrew-core#35825


Patch:
diff --git a/pre_commit/commands/install_uninstall.py b/pre_commit/commands/install_uninstall.py
index 4ff2a41..a6d501a 100644
--- a/pre_commit/commands/install_uninstall.py
+++ b/pre_commit/commands/install_uninstall.py
@@ -2,15 +2,14 @@ from __future__ import print_function
 from __future__ import unicode_literals
 
 import io
+import itertools
 import logging
 import os.path
 import sys
 
-import pre_commit.constants as C
 from pre_commit import git
 from pre_commit import output
 from pre_commit.clientlib import load_config
-from pre_commit.languages import python
 from pre_commit.repository import all_hooks
 from pre_commit.repository import install_hook_envs
 from pre_commit.util import cmd_output
@@ -51,8 +50,19 @@ def shebang():
     if sys.platform == 'win32':
         py = 'python'
     else:
-        py = python.get_default_version()
-        if py == C.DEFAULT:
+        # Homebrew/homebrew-core#35825: be more timid about appropriate `PATH`
+        path_choices = [p for p in os.defpath.split(os.pathsep) if p]
+        exe_choices = [
+            'python{}'.format('.'.join(
+                str(v) for v in sys.version_info[:i]
+            ))
+            for i in range(3)
+        ]
+        for path, exe in itertools.product(path_choices, exe_choices):
+            if os.path.exists(os.path.join(path, exe)):
+                py = exe
+                break
+        else:
             py = 'python'
     return '#!/usr/bin/env {}'.format(py)
 


Test patch:
diff --git a/tests/commands/install_uninstall_test.py b/tests/commands/install_uninstall_test.py
index 608fe38..c19aaa4 100644
--- a/tests/commands/install_uninstall_test.py
+++ b/tests/commands/install_uninstall_test.py
@@ -18,7 +18,6 @@ from pre_commit.commands.install_uninstall import is_our_script
 from pre_commit.commands.install_uninstall import PRIOR_HASHES
 from pre_commit.commands.install_uninstall import shebang
 from pre_commit.commands.install_uninstall import uninstall
-from pre_commit.languages import python
 from pre_commit.util import cmd_output
 from pre_commit.util import make_executable
 from pre_commit.util import mkdirp
@@ -51,17 +50,19 @@ def test_shebang_windows():
         assert shebang() == '#!/usr/bin/env python'
 
 
-def test_shebang_otherwise():
+def test_shebang_posix_not_on_path():
     with mock.patch.object(sys, 'platform', 'posix'):
-        assert C.DEFAULT not in shebang()
+        with mock.patch.object(os, 'defpath', ''):
+            assert shebang() == '#!/usr/bin/env python'
+
 
+def test_shebang_posix_on_path(tmpdir):
+    tmpdir.join('python{}'.format(sys.version_info[0])).ensure()
 
-def test_shebang_returns_default():
     with mock.patch.object(sys, 'platform', 'posix'):
-        with mock.patch.object(
-            python, 'get_default_version', return_value=C.DEFAULT,
-        ):
-            assert shebang() == '#!/usr/bin/env python'
+        with mock.patch.object(os, 'defpath', tmpdir.strpath):
+            expected = '#!/usr/bin/env python{}'.format(sys.version_info[0])
+            assert shebang() == expected
 
 
 def test_install_pre_commit(in_git_dir, store):