Cleanly and optionally redirect stderr or stdout to file

女生的网名这么多〃 提交于 2019-12-12 04:59:40

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!