-
Notifications
You must be signed in to change notification settings - Fork 7
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
Exception when using capturer inside pytest with capsys #7
Comments
Hi @clara-avnet and thanks for the feedback. The exception you reported is indeed reproducible. This prompted me to play around a bit with the interactions between pytest's capturing behavior and capturer to see if it can be improved. The good news is that the exception can be avoided: diff --git a/capturer/__init__.py b/capturer/__init__.py
index 407ba26..442ca86 100644
--- a/capturer/__init__.py
+++ b/capturer/__init__.py
@@ -7,6 +7,7 @@
"""Easily capture stdout/stderr of the current process and subprocesses."""
# Standard library modules.
+import io
import multiprocessing
import os
import pty
@@ -264,12 +265,19 @@ class CaptureOutput(MultiProcessHelper):
For more details refer to `issue 2 on GitHub
<https://github.com/xolox/python-capturer/issues/2>`_.
"""
- real_fd = file_obj.fileno()
- stream_obj = Stream(real_fd)
- self.streams.append((expected_fd, stream_obj))
+ try:
+ real_fd = file_obj.fileno()
+ stream_obj = Stream(real_fd)
+ self.streams.append((expected_fd, stream_obj))
+ except io.UnsupportedOperation:
+ # This happens when the pytest capture fixture is being used.
+ real_fd = None
+ # Regardless of how the above went we're interested in what child
+ # processes have to report on the OS defined file descriptor.
if real_fd != expected_fd:
self.streams.append((expected_fd, Stream(expected_fd)))
- return stream_obj
+ # Get the first stream available (we know there will be at least one).
+ return next(stream for fd, stream in self.streams if fd == expected_fd)
def __enter__(self):
"""Automatically call :func:`start_capture()` when entering a :keyword:`with` block.""" Unfortunately this means output capturing by capturer is only done on the level of file descriptors and not inside Python, which is likely not the expected behavior (as your test nicely illustrates). This means I don't see a realistic way to make the test as you wrote it succeed, it would end up like this: def test_pytest_capture_fixture(capsys):
with CaptureOutput() as capturer:
# Generate some output from Python.
print("Output from Python")
# Generate output from a subprocess.
subprocess.call(["echo", "Output from a subprocess"])
# Get the output in each of the supported formats.
assert capturer.get_bytes() == b'Output from a subprocess\r\n'
assert capturer.get_lines() == [u'Output from a subprocess']
assert capturer.get_text() == u'Output from a subprocess' However this seems like counterintuitive behavior to me, so I'm not convinced about enabling this. Maybe instead the documentation should just explicitly state not to mix capturer and the pytest output capturing fixtures? After all they serve very similar purposes. I'm open to discussion about whether there's a right choice here 🙂. |
I think your suggestion to simply warn users not mix usages of capturer and the capsys fixture, sounds like the best option. |
Using capturer inside a pytest test that uses the pytest fixture
capsys
leads to the following error in the call to CaptureOutput():I suspect, this has to do with nested capturing, as the function's documentation helpfully states (#2).
Reproduction
python 3.7.3
pytest 5.3.5
capturer 2.4
File test_file.py, called with
pytest test_file.py
:The text was updated successfully, but these errors were encountered: