Reputation: 11
After looking through other stackoverflow posts, I can't seem to figure this redirection problem out. What I would like to do is suppress both stdout
and stderr
, and then restore them after the error is caught. The suppression works fine, but restoring them only half works.
If I try to suppress and restore stderr
and stdout
, the suppression works but not the restoring. If I only try to suppress/restore stdout
it works (but then I get all the stderr
text I don't want).
I can't reason out why there would be any difference causing stderr
to block stdout
from being restored, hoping for input on why that might be (or if I'm doing something weird/stupid)
Here's the code that I want to work but only suppresses out/err and doesn't restore ('Restored stdout'
never prints):
sys.stdout = None
sys.stderr = None
try:
op_args = op_parse.parse_args(selection.split(' '))
except SystemExit:
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
print("Restored stdout")
Here's the code that suppresses stdout
AND restores it (at the cost of printing the pesky stderr
):
sys.stdout = None
#sys.stderr = None
try:
op_args = op_parse.parse_args(selection.split(' '))
except SystemExit:
sys.stdout = sys.__stdout__
#sys.stderr = sys.__stderr__
print("Restored stdout")
Edit: I figured out a workaround, but I'm still interested in why the above problem occurs. My workaround is to reassign stdout
/stderr
to = open("/dev/null", "w")
which does produce the behavior I want. Again I still would like input on the original problem.
Upvotes: 1
Views: 1079
Reputation: 123423
You can do this a little more elegantly by creating a context manager:
import os
from contextlib import contextmanager
@contextmanager
def nullout():
save_stdout = sys.stdout
save_stderr = sys.stderr
sys.stdout = open(os.devnull, 'w')
sys.stderr = open(os.devnull, 'w')
try:
yield
finally:
sys.stdout = save_stdout
sys.stderr = save_stderr
with nullout():
op_args = op_parse.parse_args(selection.split(' '))
A nice thing about context managers is that the code after the yield
will get executed regardless of whether an exception occurs or not.
The reason you can't just set sys.stdout
and sys.stderr
to None
is probably because None
doesn't have a write()
or close()
method and generally doesn't behave like an output stream.
Upvotes: 4
Reputation: 4427
As a high level comment, this technique is error prone and usually means something else is going wrong. For example - restoring in the except
above (instead of in a finally
) means you restore stdout and stderr only if you fail.
I suspect that your observed problem may be due to your op_parse
invocation. Consider the below code
from __future__ import print_function
import sys
sys.stdout = None
sys.stderr = None
try:
print('before', file=sys.stdout)
print('before', file=sys.stderr)
# sys.stderr.write('hi')
assert False
except AssertionError:
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
print("Restored stdout", file=sys.stdout)
When I run it, I see: Restored stdout
.
If I uncomment the sys.stderr.write
line, I see: lost sys.stderr
.
A safer approach would be to use a context to redirect stdout/err to /dev/null
or an IOStream
during your operation - or better yet, find the validation function being used internally and call that directly instead of looking for a system exit on a high level function (is op_parse == opt_parse
?)
Upvotes: 0