问题
I have a Python3 script and I want to optionally redirect stdout
and stderr
to a file. Something like this:
# variable declarations
if log_output:
output_file = open('output.txt', 'w')
sys.stdout = output_file
if log_errors:
errors_file = open('errors.txt', 'w')
sys.stderr = errors_file
# code that uses variables declared above but may exit suddenly
#at the end
if log_output:
output_file.close()
if log_errors:
errors_file.close()
This works, unless my code in the middle decides to quit. Then my files aren't guaranteed to be closed. How can I cleanly close these files no matter what happens in the code and only some of the time? (Normally, I would redirect through the shell, but I'm computing the file names in Python and I don't want to recompute them in various shells. Also, I don't want to put the logic for whether or not to redirect in a shell script. I want those branches in my main code if possible.)
Attempt 1
It seems like context managers would be the way to here, but, when I try to use them, I have to rewrite my code several times and it's not pretty code:
if log_output:
with open('output.txt', 'w') as output_file:
with contextlib.redirect_stdout(output_file):
if log_errors:
with open('errors.txt','w') as errors_file:
with contextlib.redirect_stderr(errors_file):
# log_output and log_errors
# code that uses variables declared above but may exit suddenly
else:
# log_output and not log_errors
# code that uses variables declared above but may exit suddenly
else:
if log_errors:
with open('errors.txt', 'w') as errors_file:
with contextlib.redirect_stderr(errors_file):
# not log_output and log_errors
# code that uses variables declared above but may exit suddenly
else:
# not log_output and not log_errors
# code that uses variables declared above but may exit suddenly
Attempt 2
I decided to make a context manager for it. I think it works, and Python's not yelling at me, but I still can't help but feel it's not too Pythonic and I'm not completely sure it's safe. I'm pushing the if
statements in odd directions. Is there a better way?
@contextlib.contextmanager
def opt_stream(stream, name = None):
if name:
file = open(name,'w')
yield file
file.close()
else:
yield stream
output_name, errors_name = None, None
if log_output:
output_name = 'outputs.txt'
if log_errors:
errors_name = 'errors.txt'
with opt_stream(sys.stdout, output_name) as output_file:
with opt_stream(sys.stderr, errors_name) as errors_file:
with contextlib.redirect_stdout(output_file):
with contextlib.redirect_stderr(errors_file):
# code that uses variables declared above but may exit suddenly
回答1:
The cleanest way to optionally redirect your program's stdout
or stderr
to a file is to not do so in your program at all. Instead, do it through your operating system's shell.
On Linux, if I wanted to redirect a Python program's stdout
to a file, I'd do this:
$ python something.py > stdout.log
$ python something_else.py 2> stderr.log
Note the 2>
for redirecting stderr
output.
As it happens, cmd and PowerShell on Windows use the same syntax.
The above, while true, isn't relevant given the OP's updated problem description.
Assuming you're using Python 3, the built-in print function actually has a named parameter "file
" that lets you decide where to print
to.
print(some_object, file=your_own_file_object)
file
can be any file-like object (which stdout
and stderr
are). You could just pass in the result of open(), or get crazy and screw around with the io module. Whatever the case, you just have to maintain a variable (whose value is possibly that of sys.stdout
) and always pass that in to print
calls, and then just set that variable whenever you decide where to output something to.
Otherwise, you might considering setting the value of sys.stdout and sys.stderr, if you don't mind getting funny looks from other Python programmers.
来源:https://stackoverflow.com/questions/33291792/cleanly-and-optionally-redirect-stderr-or-stdout-to-file