How to set custom output handlers for argparse in Python?

Looking at the argparse.py source code there doesn't seem to be a way to configure this behaviour.

My suggestion(s) would be:

Override/patch:

  • print_* method(s)
  • error method.

The print_* method(s) seem to take an optional file argument which defaults to _sys.stdout.

Update: Alternatively you could do something like this whereby you redirect sys.stdout temporarily while you parse arguments:

from contextlib import contextmanager

@contextmanager
def redirect_stdout_stderr(stream):
    old_stdout = sys.stdout
    old_stderr = sys.stderr
    sys.stdout = stream
    sys.stderr = stream
    try:
        yield
    finally:
        sys.stdout = old_stdout
        sys.stderr = old_stderr


with redirct_stdout_stderr(logstream):
    args = parser.parse_args()

There seems to be no way to do this through the API.

However, you can do the following:

class LoggingArgumentParser(argparse.ArgumentParser):
    """Custom ArgumentPaarser that overrides _print_message"""

    def _print_message(self, message, file=None):
        if message:
            logger.write(message)

While the answer given by @James Mills is great and solves the issue, there is no need for a generator in this case. Hence, the yield is redundant. Another way of achieving the same (without the generator) would be to write your own context manager without using the inbuilt contextlib.contextmanager decorator. Like the following.

class redirect_stdout_stderr(object):
    def __init__(self, stream):
        # Save the old std streams
        self.old_stream = sys.stdout
        self.old_error_stream = sys.stderr
        self.fstream = stream

    def __enter__(self):
        # Change the std streams to your streams when entering
        sys.stdout = self.fstream
        sys.stderr = self.fstream

    def __exit__(self, exc_type, exc_value, exc_traceback):
        # Change the std streams back to the original streams while exiting
        sys.stdout = self.old_stream
        sys.stderr = self.old_error_stream

In your case you can do something as follows.

with redirect_stdout_stderr(logstream):
    # __enter__() is executed
    args = parser.parse_args()
    # __exit__() is executed

Hope this helps!