Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Repair colorama wrapping on non-Windows platforms #1670

Merged
merged 3 commits into from Sep 27, 2020
Merged

Conversation

jpgrayson
Copy link
Contributor

The wrap_stream_for_windows() function calls
colorama.initialise.wrap_stream() function to apply colorama's magic to
wrapper to the output stream. Except this wrapper is only applied on
Windows platforms that need it, otherwise the original stream is
returned as-is.

The colorama wrapped stream lacks a detach() method, so a no-op lambda
was being assigned to the wrapped stream.

The problem is that the no-op lambda was being assigned unconditionally
whether or not colorama actually returns a wrapped stream, thus
replacing the original TextIOWrapper's detach() method. Replacing the
detach() method with a no-op lambda is the root cause of the problem
observed in #1664.

The solution is to only assign the no-op detach() method if the stream
lacks its own detach() method.

Repairs #1664

@ichard26
Copy link
Collaborator

ichard26 commented Sep 2, 2020

Now looking at the colorama code, none of this "let's fake the detach method" is even required in the first place as <class 'colorama.ansitowin32.StreamWrapper'> (the type of object colorama.initialise.wrap_stream returns) hands off attribute search to its underlying wrapped stream when normal attribute search fails. https://github.com/tartley/colorama/blob/master/colorama/ansitowin32.py#L28

io.TextIOWrapper will always have the detach method so since it's the only stream type being wrapped by colorama.ansitowin32.StreamWrapper, the now wrapped stream will still have the detach method.

@jpgrayson
Copy link
Contributor Author

Saw that colorama's StreamWrapper implemented the pass-through __getattr__(), but since I don't have a Windows environment at-hand, I went with the minimal safe change. The concern would be that the no-op detach() is somehow required for this colorama wrapper to work properly on Windows.

Perhaps this PR could go in as-is and then someone with a Windows environment could confirm that ripping-out the detach() no-op lambda works in that environment?

@ichard26
Copy link
Collaborator

ichard26 commented Sep 2, 2020

Perhaps this PR could go in as-is and then someone with a Windows environment could confirm that ripping-out the detach() no-op lambda works in that environment?

Perfect, I have a Windows machine right in front of me :-) I'll test the other idea now.

@ichard26
Copy link
Collaborator

ichard26 commented Sep 3, 2020

Never faking the detach method doesn't seem to cause any problems on Windows as far as I can tell. Although I just have a single Windows machine which of course isn't representative of all of the Windows machines out there. So it's your choice whether to leave the noop method fallback. It seems highly unlikely that io.TextIOWrapper wouldn't have the detach method , but then again it doesn't hurt to include the fallback ¯\_(ツ)_/¯.

Regardless, it would be nice if you could update the nearby comments as some will become outdated with your patch or are already incorrect (like # wrap_stream returns a colorama.AnsiToWin32.AnsiToWin32 [snip]).

Oh, and if you'd like, feel free to add yourself to the authors list in the README!

@ichard26
Copy link
Collaborator

ichard26 commented Sep 7, 2020

Sorry for forgetting about this but could you please add an entry in the change log (CHANGES.md)? I would go with something like "fixed a bug where Black would sometimes fail to print diff output emitting I/O errors when formatting many files (PR #1670)".

Requesting review from @zsol as he reviewed the original PR that introduced coloured diffs... and the bug you're fixing.

CHANGES.md Outdated Show resolved Hide resolved
The wrap_stream_for_windows() function calls
colorama.initialise.wrap_stream() function to apply colorama's magic to
wrapper to the output stream. Except this wrapper is only applied on
Windows platforms that need it, otherwise the original stream is
returned as-is.

The colorama wrapped stream lacks a detach() method, so a no-op lambda
was being assigned to the wrapped stream.

The problem is that the no-op lambda was being assigned unconditionally
whether or not colorama actually returns a wrapped stream, thus
replacing the original TextIOWrapper's detach() method. Replacing the
detach() method with a no-op lambda is the root cause of the problem
observed in psf#1664.

The solution is to only assign the no-op detach() method if the stream
lacks its own detach() method.

Repairs psf#1664
- Clarify doc string and comments
- Do not assign a no-op detach() method to AnsiToWin32 wrapper--that
  wrapper will pass the call through to the wrapped stream's detach()
  method.
- Improve the control flow.
- Correct type annotation for colorama.AnsiToWin32
Copy link
Collaborator

@zsol zsol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for the contribution @jpgrayson and the thorough review @ichard26

@zsol zsol merged commit 4d71d74 into psf:master Sep 27, 2020
noxan pushed a commit to noxan/black that referenced this pull request Jun 6, 2021
* Repair colorama wrapping on non-Windows platforms

The wrap_stream_for_windows() function calls
colorama.initialise.wrap_stream() function to apply colorama's magic to
wrapper to the output stream. Except this wrapper is only applied on
Windows platforms that need it, otherwise the original stream is
returned as-is.

The colorama wrapped stream lacks a detach() method, so a no-op lambda
was being assigned to the wrapped stream.

The problem is that the no-op lambda was being assigned unconditionally
whether or not colorama actually returns a wrapped stream, thus
replacing the original TextIOWrapper's detach() method. Replacing the
detach() method with a no-op lambda is the root cause of the problem
observed in psf#1664.

The solution is to only assign the no-op detach() method if the stream
lacks its own detach() method.

Repairs psf#1664
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants