[Bugfix][Python] Cache virtual environments by full python path

Prior to this commit, a cached virtualenv could be erroneously
re-used, causing failures in pre-commit hooks.  This is caused by
incorrect normalization in `get_default_version`, resulting in a cache
key of `"python3"`, regardless of the minor version used.

This commit updates the implementation of `get_default_version` for
python-based hooks to return `os.path.realpath(sys.executable)`.  This
ensures that the correct python version is used for running pre-commit
hooks.
This commit is contained in:
Eric Lunderberg 2024-07-17 17:02:13 +00:00
parent faa6f8c70c
commit f7371ebea4

View file

@ -75,46 +75,9 @@ def _find_by_py_launcher(
return None
def _find_by_sys_executable() -> str | None:
def _norm(path: str) -> str | None:
_, exe = os.path.split(path.lower())
exe, _, _ = exe.partition('.exe')
if exe not in {'python', 'pythonw'} and find_executable(exe):
return exe
return None
# On linux, I see these common sys.executables:
#
# system `python`: /usr/bin/python -> python2.7
# system `python2`: /usr/bin/python2 -> python2.7
# virtualenv v: v/bin/python (will not return from this loop)
# virtualenv v -ppython2: v/bin/python -> python2
# virtualenv v -ppython2.7: v/bin/python -> python2.7
# virtualenv v -ppypy: v/bin/python -> v/bin/pypy
for path in (sys.executable, os.path.realpath(sys.executable)):
exe = _norm(path)
if exe:
return exe
return None
@functools.lru_cache(maxsize=1)
def get_default_version() -> str: # pragma: no cover (platform dependent)
# First attempt from `sys.executable` (or the realpath)
exe = _find_by_sys_executable()
if exe:
return exe
# Next try the `pythonX.X` executable
exe = f'python{sys.version_info[0]}.{sys.version_info[1]}'
if find_executable(exe):
return exe
if _find_by_py_launcher(exe):
return exe
# We tried!
return C.DEFAULT
return os.path.realpath(sys.executable)
def _sys_executable_matches(version: str) -> bool: