Don't require sys.stdout.buffer when writing formatted output or diffs - #5411
Merged
Merged
Conversation
Text streams aren't required to expose their underlying binary buffer (for example ipykernel's OutStream in Jupyter), so format_stdin_to_stdout and the diff output of format_file_in_place crashed with AttributeError. Fall back to writing text directly to sys.stdout, applying the newline translation that io.TextIOWrapper would otherwise do.
Collaborator
|
Disregard my previous comment, the patch wasn't properly downloading. Confirmed this IS working. Thanks for the fix! |
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
Fixes #2516
Bug: Calling
black.reformat_code()/black.format_stdin_to_stdout()from a Jupyter notebook fails withAttributeError: 'OutStream' object has no attribute 'buffer'. The diff output offormat_file_in_place()(WriteBack.DIFF/COLOR_DIFF) has the same problem.Root cause: Both functions wrap
sys.stdout.bufferinio.TextIOWrapperso they can control the encoding and newline translation.TextIOBase.bufferis not part of theTextIOBaseAPI and ipykernel'sOutStreamdoesn't have it.format_stdin_to_stdout()builds that wrapper in itsfinallyblock for everywrite_backmode, so even--check-style calls crash. The issue thread notes that just usinggetattr(sys.stdout, "buffer", sys.stdout)doesn't work either, becauseOutStream.write()only acceptsstr.Change: Both code paths now go through a small
_write_to_stdout()helper:sys.stdouthas abuffer, it does what the code did before: wraps it inio.TextIOWrapper(..., write_through=True), wraps it for Windows colors where needed, writes, then detaches.buffer, it writes text straight tosys.stdout. It does the\n->newlinetranslation itself (this is whatTextIOWrapperwould do, and the content has already been normalized to\nat that point) and leaves encoding to the stream. The stream is never detached or closed.format_stdin_to_stdout()now only touches stdout whenwrite_backisYES/DIFF/COLOR_DIFF.wrap_stream_for_windows()'s annotation is widened fromio.TextIOWrappertoTextIOso it acceptssys.stdout. At runtime it already accepted any stream.Tests: I added three tests that patch
sys.stdoutwith anio.StringIO, which has nobufferand only acceptsstr(likeOutStream). They coverformat_stdin_to_stdoutwithYES/CHECK/NO/DIFF/COLOR_DIFF,reformat_code, and theformat_file_in_placediff path with both\nand\r\nfiles. All three failed with theAttributeErrorbefore the fix and pass after it.Validation (macOS, Python 3.14):
pytest -n auto tests: 484 passed, 3 skippedpre-commit run -a: everything passes except mypy, which reports anaction/main.py:60tomlliberror that also shows up on unmodifiedmainin my environment. It has nothing to do with this change, and there are no mypy errors in the touched files.black --checkon the changed files: cleanChecklist - did you ...
--previewstyle, following the stability policy? (N/A, no style changes)CHANGES.mdif necessary?Generated with Claude Code on behalf of @ethanstoner.