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

Fix crash when printing while capsysbinary is active #6926

Merged
merged 1 commit into from Mar 17, 2020

Conversation

bluetech
Copy link
Member

@bluetech bluetech commented Mar 14, 2020

Closes #6880 by replacing that PR with some modifications as described in #6880 (review) (step 2).


Previously, writing to sys.stdout/stderr in text-mode (e.g.
print('foo')) while a capsysbinary fixture is active, would crash
with:

/usr/lib/python3.7/contextlib.py:119: in __exit__
    next(self.gen)
E   TypeError: write() argument must be str, not bytes

This is due to some confusion in the types. The relevant functions are
snap() and writeorg(). The function snap() returns what was
captured, and the return type should be bytes for the binary captures
and str for the regular ones. The snap() return value is eventually
passed to writeorg() to be written to the original file, so it's input
type should correspond to snap(). But this was incorrect for
SysCaptureBinary, which handled it like str.

To fix this, be explicit in the snap() and writeorg()
implementations, also of the other Capture types.

We can't add type annotations yet, because the current inheritance
scheme breaks Liskov Substitution and mypy would complain. To be
refactored later.

Fixes: #6871
Co-authored-by: Ran Benita (some modifications & commit message)

Copy link
Member

@Zac-HD Zac-HD left a comment

Choose a reason for hiding this comment

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

LGTM. @blueyed?

testing/test_capture.py Outdated Show resolved Hide resolved
@@ -591,6 +589,11 @@ def snap(self):
self.tmpfile.truncate()
return res

def writeorg(self, data):
""" write to original file descriptor. """
data = data.encode("utf-8") # XXX use encoding of original stream
Copy link
Member

Choose a reason for hiding this comment

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

Hmmm not related to this code, but curious how FDCaptureBinary still creates tmpfile with encoding="UTF-8"...

Copy link
Member Author

Choose a reason for hiding this comment

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

If you mean why FDCaptureBinary wraps the temporary file with EncodedFile (== TextIOWrapper), it's for two reasons:

  1. In FD capturing, in addition to the FD redirection itself, it also does sys capturing. And the sys capturing requires a file object which looks like sys.stdout and friends, which are text-mode.
  2. The FDCapture subclass uses it in its snap() implementation.

I have some plans to untangle all of this, but thought it'd be better not to mix refactorings with bug fixes.

Previously, writing to sys.stdout/stderr in text-mode (e.g.
`print('foo')`) while a `capsysbinary` fixture is active, would crash
with:

    /usr/lib/python3.7/contextlib.py:119: in __exit__
        next(self.gen)
    E   TypeError: write() argument must be str, not bytes

This is due to some confusion in the types. The relevant functions are
`snap()` and `writeorg()`. The function `snap()` returns what was
captured, and the return type should be `bytes` for the binary captures
and `str` for the regular ones. The `snap()` return value is eventually
passed to `writeorg()` to be written to the original file, so it's input
type should correspond to `snap()`. But this was incorrect for
`SysCaptureBinary`, which handled it like `str`.

To fix this, be explicit in the `snap()` and `writeorg()`
implementations, also of the other Capture types.

We can't add type annotations yet, because the current inheritance
scheme breaks Liskov Substitution and mypy would complain. To be
refactored later.

Fixes: pytest-dev#6871
Co-authored-by: Ran Benita (some modifications & commit message)
Copy link
Member

@nicoddemus nicoddemus left a comment

Choose a reason for hiding this comment

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

Great work @bluetech, thanks!

@bluetech bluetech merged commit ded3023 into pytest-dev:master Mar 17, 2020
@nicoddemus
Copy link
Member

Sorry, I just realized we have forgotten to backport this to 5.4.x. Could you take care of that @bluetech? Thanks!

@Feuermurmel
Copy link

Feuermurmel commented Apr 2, 2020

Thanks for the great work! :) Just tested this from the current master 2d9dac9 by switching from capsys to capsysbinary in the project I originally ran across this and it works like a charm!

@bluetech bluetech added the needs backport applied to PRs, indicates that it should be ported to the current bug-fix branch label Apr 2, 2020
@bluetech
Copy link
Member Author

bluetech commented Apr 2, 2020

Backport in #7002. Should be fixed in (upcoming) 5.4.2.

jktjkt added a commit to Telecominfraproject/oopt-gnpy that referenced this pull request Jun 11, 2020
I would like to avoid that extra fork to a child Python interpreter (it
looks like something that can be easily avoided). It's something that's
possible now that the code ships just some trivial wrappers (which are,
in turn, needed for setuptools' `console_scripts`).

This cannot use the `capsysbinary` fixture for wrapping of stdout/stderr
due to something in pytest which already got fixed, but has not been
released yet (May 2020). Let's use `capfdbinary` which works fine.

Change-Id: Ic4a124a5cbe2bd24c56e4565d27d313fe4da703f
See-also: pytest-dev/pytest#6926
@bluetech bluetech deleted the fix-capsysbin-print branch June 17, 2020 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs backport applied to PRs, indicates that it should be ported to the current bug-fix branch
Projects
None yet
Development

Successfully merging this pull request may close these issues.

capsysbinary fixture breaks print()
5 participants