I have configured logger
to print both onto terminal stdout
and to a file so I can have an archive of logging messages that I can refer to.
That is easily accomplished by adding a FileHandler
to your logging
object. Easy peasy.
What I want to accomplish now is to make argparse
log also to the same file along with logs to stdout when it encounters parsing errors. So far it only prints to stdout
. I looked in the argparse
documentation but I can't find anything about setting a different output stream or pipe for argparse
.
Is it possible to do? How?
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With