Repository: pre-commit/pre-commit

Issue:
Flask style test failing health check in CI
Flask's style test in GitHub actions runs `tox -e style`, which runs `pre-commit run --all-files --show-diff-on-failure`, which is failing with "An unexpected error has occurred: AssertionError: BUG: expected environment for python to be healthy() immediately after install, please open an issue describing your environment".

Note, this does not reproduce locally.

AFAIK, this first appeared in pallets/flask#3739.

We're trying to debug this over in pallets/flask#3740 where I've temporarily added a `cat .../pre-commit.log` command. Output here: https://github.com/pallets/flask/pull/3740/checks?check_run_id=1018335736#step:9:14

Neither the error message nor the log actually say what specifically about the environment is unhealthy.
 
Any ideas?

Thanks!

Discussion:

Patch:
diff --git a/pre_commit/languages/python.py b/pre_commit/languages/python.py
index 6f7c900..7a68580 100644
--- a/pre_commit/languages/python.py
+++ b/pre_commit/languages/python.py
@@ -191,7 +191,8 @@ def healthy(prefix: Prefix, language_version: str) -> bool:
 
     return (
         'version_info' in cfg and
-        _version_info(py_exe) == cfg['version_info'] and (
+        # always use uncached lookup here in case we replaced an unhealthy env
+        _version_info.__wrapped__(py_exe) == cfg['version_info'] and (
             'base-executable' not in cfg or
             _version_info(cfg['base-executable']) == cfg['version_info']
         )


Test patch:
diff --git a/tests/languages/python_test.py b/tests/languages/python_test.py
index c419ad6..29c5a9b 100644
--- a/tests/languages/python_test.py
+++ b/tests/languages/python_test.py
@@ -8,6 +8,7 @@ import pre_commit.constants as C
 from pre_commit.envcontext import envcontext
 from pre_commit.languages import python
 from pre_commit.prefix import Prefix
+from pre_commit.util import make_executable
 
 
 def test_read_pyvenv_cfg(tmpdir):
@@ -141,3 +142,26 @@ def test_unhealthy_old_virtualenv(python_dir):
     os.remove(prefix.path('py_env-default/pyvenv.cfg'))
 
     assert python.healthy(prefix, C.DEFAULT) is False
+
+
+def test_unhealthy_then_replaced(python_dir):
+    prefix, tmpdir = python_dir
+
+    python.install_environment(prefix, C.DEFAULT, ())
+
+    # simulate an exe which returns an old version
+    exe_name = 'python.exe' if sys.platform == 'win32' else 'python'
+    py_exe = prefix.path(python.bin_dir('py_env-default'), exe_name)
+    os.rename(py_exe, f'{py_exe}.tmp')
+
+    with open(py_exe, 'w') as f:
+        f.write('#!/usr/bin/env bash\necho 1.2.3\n')
+    make_executable(py_exe)
+
+    # should be unhealthy due to version mismatch
+    assert python.healthy(prefix, C.DEFAULT) is False
+
+    # now put the exe back and it should be healthy again
+    os.replace(f'{py_exe}.tmp', py_exe)
+
+    assert python.healthy(prefix, C.DEFAULT) is True