-
Hey folks, I found myself in a file descriptor hell. I have a code that needs to take everything that would otherwise go to stdout and do something else with it. Basically like contextlib.redirect_stdout except I need it to work on file descriptor level, because the thing within might print to stdout in a way that is beyond the Python interpreter. I came up with this (heavily inspired by pytest's own capdf fixture): import contextlib, io, os, sys, tempfile
@contextlib.contextmanager
def hook_call():
"""Context manager that records all stdout content (on FD level)
and prints it to stderr at the end, with a 'HOOK STDOUT: ' prefix."""
tmpfile = io.TextIOWrapper(
tempfile.TemporaryFile(buffering=0),
encoding='utf-8',
errors='replace',
write_through=True,
)
# stdout FD is 1 in reality, but might be replaced by pytest's capfd
stdout_fd = sys.stdout.fileno()
stdout_fd_dup = os.dup(stdout_fd)
# begin capture
sys.stdout = tmpfile
os.dup2(tmpfile.fileno(), stdout_fd)
try:
yield
finally:
# end capture
sys.stdout = sys.__stdout__
os.dup2(stdout_fd_dup, stdout_fd)
tmpfile.seek(0) # rewind
for line in tmpfile.read().splitlines():
print('HOOK STDOUT:', line, file=sys.stderr)
tmpfile.close() When I use it from a script: def make_a_hook_call():
with hook_call():
print('from Python to stdout')
os.system('echo from echo to stdout')
print('from Python to stderr', file=sys.stderr)
os.system('echo from echo to stderr >&2')
print('from Python to stdout outside of hook_call()')
if __name__ == '__main__':
make_a_hook_call() It works as expected:
However, if I try to test this using the capfd fixture: def test_make_a_hook_call(capfd):
make_a_hook_call()
out, err = capfd.readouterr()
assert out == 'from Python to stdout outside of hook_call()\n' It breaks:
My assumption is that trying to write my own capfd and use that in a test with the pytest's capfd is a bad idea... But maybe I'm just missing something? Notes:
|
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 5 replies
-
OK, thanks for the rubber 🦆 I've put this initially to workaround something failing: # stdout FD is 1 in reality, but might be replaced by pytest's capfd
stdout_fd = sys.stdout.fileno() But apparently |
Beta Was this translation helpful? Give feedback.
OK, thanks for the rubber 🦆
I've put this initially to workaround something failing:
But apparently
stdout_fd = 1
makes it work as desired 😆