Repository navigation
🐛 fix(discovery): tolerate unreadable .python-version files - #173
Merged
gaborbernat merged 3 commits intoOct 30, 2026
Merged
Conversation
…ting discovery Resolving a pyenv, mise or asdf shim reads .python-version from the working directory up to the filesystem root, then $PYENV_ROOT/version, and each file was opened as strict UTF-8 with no error handling. A file holding a byte that is not UTF-8, such as a Latin-1 comment or the UTF-16 that Windows PowerShell 5.1 writes for `"3.12" > .python-version`, raised UnicodeDecodeError, and one the user may not read raised PermissionError. Both escaped get_interpreter and iter_interpreters, so virtualenv and tox stopped with a traceback for anyone with a shim on PATH who asked for an interpreter other than the running one. pyenv reads the file as bytes, so it takes the version from the Latin-1 file without complaint. The file is now decoded with errors="replace", so a byte that does not decode no longer hides the versions around it, the same result pyenv's reader gives. A file that cannot be opened or read is treated like one that names no version: it is logged at debug level and the search moves on, as the rest of the module does with filesystem errors.
On Python 3.9 to 3.13, Path.is_dir and Path.is_file re-raise a PermissionError from stat, so a working directory under a folder without search permission still aborted the .python-version walk. os.path.isdir and os.path.isfile return False there. The shim test now pins with a Latin-1 comment and checks that the pinned interpreter is probed.
Exercise the unreadable file and unsearchable directory through get_interpreter with real file permissions instead of calling the private reader and patching Path.open, and drop the reader-level Latin-1 test that the shim test already covers. Relabel the docs flowchart: a file that exists but names no readable version now falls through to the parent directories.
gaborbernat
enabled auto-merge (squash)
October 30, 2026 18:19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

A
.python-versionthat isn't valid UTF-8, or that the user can't read, made discovery through a pyenv, mise or asdf shim raise instead of finding an interpreter. 🐛 With a Latin-1 comment such as# für das Projektabove3.11.9,get_interpreter("3.11")stopped withUnicodeDecodeError, and so did virtualenv, which passes-pto it without a guard. A file without read permission, or a working directory below a folder without search permission, raisedPermissionErrorthe same way. Once this merges, discovery reads3.11.9from the Latin-1 file, as pyenv does, and skips an unreadable file like a missing one.This only affects shim resolution with
PYENV_VERSIONunset. Discovery then walks from the working directory up to the filesystem root looking for a.python-versionthat names a version, and falls back to the pyenv globalversionfile. pyenv reads these files with bashread, which never decodes, so discovery now tolerates undecodable bytes. A file or directory it can't access counts as naming no version, and the walk moves on to the parent and the global file, the same path a comments-only file already takes. Read failures go to the debug log.A UTF-16 file, which Windows PowerShell 5.1 writes for
"3.12" > .python-version, no longer raises either. pyenv can't read that file and neither can discovery, so the shim is probed as is.