fix(release): decode distribution smoke output as UTF-8 - #244
Merged
Zongwei9888 merged 1 commit intoSep 27, 2026
Merged
Conversation
`_run()` read child output with `text=True` and no `encoding`, so the locale codec decoded it. A byte the locale cannot decode raised inside subprocess's reader thread, and that exception never reached the caller: the thread died, the result stayed clean, and the verifier reported the failing command with no output at all -- exit code and diagnostics disagreeing is what makes a fail-closed release gate silently useless. Take the text kwargs and the child environment from `core.platform_compat` (the policy the rest of the runtime already uses), and configure UTF-8 stdio in `main()` so a report containing replacement characters cannot crash a legacy console on its way out.
Collaborator
|
Merged into |
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.
Description
While checking the other subprocess call sites for the locale-decoding failure that PR #237 fixed for
.git/info/exclude, I found the same pattern inscripts/verify_python_distribution.py, but with a worse failure mode than a crash._run()callssubprocess.run(..., capture_output=True, text=True)with noencoding, so the child's output is decoded with the locale codec. When the child emits a byte the locale cannot decode, the decode raises inside subprocess's_readerthread-- and that exception never reaches_run(). The thread dies, the process result stays clean, and the verifier reports the command with no output at all:The exit code is still 0. So a fail-closed gate (
python-ci.yml,pypi-publish.yml) can stay green while its diagnostics are silently thrown away, which is the part that made me file this rather than a plain robustness tweak. I reproduced both readings on this machine (Windows, cp936) in the before/after section.Three changes, reusing the subprocess policy the runtime already has instead of restating it:
_run()takes its text kwargs fromsubprocess_text_kwargs()(encoding="utf-8",errors="replace") and builds the child environment withsubprocess_env(...), which also exportsPYTHONUTF8=1andPYTHONIOENCODING=utf-8. Declaring UTF-8 on the read side is only correct if the child speaks it; without that, CJK output from pip or the CLI degrades into replacement characters instead of being readable.main()callsconfigure_utf8_stdio(). The failure path prints a report and then raisesSystemExit, and witherrors="replace"that report can contain U+FFFD -- which a cp936 console cannot encode, so the report would crash on the way out and the real failure would go unreported.sys.pathbefore importingcore.platform_compat, the wayscripts/verify_transparency_log.pyalready does. CI runs the script by path, sosys.path[0]isscripts/.On UTF-8 locales nothing changes. I kept the policy in one place because duplicating it here is how the verifier and the runtime it checks would drift apart.
Related Issues
None that I could find. This is the same bug class as #237 (
core/team/worktree.py) and cdd2681 (hooks loader), one layer down in the subprocess read path.Changes Made
scripts/verify_python_distribution.py:_run()usessubprocess_text_kwargs()andsubprocess_env();main()callsconfigure_utf8_stdio(); repository root is added tosys.pathsocore.platform_compatcan be imported when the file is run as a script.tests/test_python_distribution_release.py:test_smoke_failure_reports_output_that_the_reader_cannot_decoderuns a child that writesb'partial \x91 output\n'and exits non-zero, then asserts the raisedDistributionVerificationErrorstill carries the decoded text (and the replacement character).test_smoke_children_are_told_to_write_utf8pins the contract the other direction: the kwargs it passes and thePYTHONIOENCODINGthe child receives. Plus theimport subprocessboth use.Checklist
Additional Notes
Before and after, Windows 11, Python 3.14, cp936 locale,
python -m pytest tests/test_python_distribution_release.py:Unpatched script:
2 failed, 11 passed, 1 error. The two new tests fail, and the error is pytest escalating the dying reader thread (filterwarnings = error), which is the clearest statement of the bug:with
fh = <_io.TextIOWrapper name=11 encoding='cp936'>and the assertion showing the report that lost its diagnostics.Patched script:
13 passed.python scripts/verify_python_distribution.py --helpand a run against a missing--dist-dirboth behave (thesys.pathguard), andruff check/ruff format --checkare clean on both files.CI coverage:
scripts/verify_python_distribution.pyis not in the documentation allowlist inscripts/ci_scope.py, soruntime_changed=trueand the verifier step inpython-ci.ymlruns on this PR. The full smoke path (real wheel, pip install into a throwaway venv) only runs there -- locally I verified the decode and reporting path directly.