If you have worked on some projects that requires API calls to the external parties or uses 3rd party libraries, you may sometimes run into the problem that you are able to get the correct return results but it also comes back with a lot of noises in the stdout and stderr. For instance, the developer may leave a lot of “for your info” messages in the standard output or some warning or error messages due to the version differences in some of the dependency libraries.
All these messages would flood your console and you have no control on the source code, hence you cannot change its behavior. To reduce these noises, one option is to suppress stdout and stderr messages during making the function call. In this article, we will discuss about some recipes to suppress the messages for such scenarios.
Unexpected messages from stdout and stderr
To further illustrate the issue, let’s take a look at the below example. Assuming we have below check_result function in a python file externallib.py, and this represents an external library.
import sys def check_result(): print("stdout message from externallib") print("stderr message from externallib", file=sys.stderr) return True
If you import the module and call the check_result function, you would be definitely getting the result as True, but you would see both the stdout and stderr messages from your console as well.
import externallib result = externallib.check_result()
Both stdout and stderr messages were printed out in the console:
suppress stdout and stderr with context manager
To stop these messages from printing out, we need to suppress stdout and stderr in the way that it redirects the output into a devnull file (similiar to /dev/null in Linux which is typically used for disposing of unwanted output streams) right before calling the function, and then redirect the outputs back after the call completed.
To do that, the best approach is to use a context manager, so that it is automatically directed/redirected upon the entry and exit of the context manager.
So let’s implement a context manager to perform the below:
- Use suppress_stdout and suppress_stderr flags to indicate which stream to be suppressed
- Save the state of the sys.stdout and sys.stderr in the __enter__ function, and redirect them to devnull based on the suppress_stdout and suppress_stderr flags
- Restore back the state for sys.stdout and sys.stderr in __exit__
Below is the code snippet:
import os, sys class suppress_output: def __init__(self, suppress_stdout=False, suppress_stderr=False): self.suppress_stdout = suppress_stdout self.suppress_stderr = suppress_stderr self._stdout = None self._stderr = None def __enter__(self): devnull = open(os.devnull, "w") if self.suppress_stdout: self._stdout = sys.stdout sys.stdout = devnull if self.suppress_stderr: self._stderr = sys.stderr sys.stderr = devnull def __exit__(self, *args): if self.suppress_stdout: sys.stdout = self._stdout if self.suppress_stderr: sys.stderr = self._stderr
And if you call the check_result again within this context manager as per below:
with suppress_output(suppress_stdout=True, suppress_stderr=True): result = externallib.check_result() print(result)
You would not see any messages printed out from check_result function, and the return result would remain as True. This is exactly what we are expecting!
Since we are using context manager, you may wonder to use contextlib to make our code more concise. So let’s make use of the contextlib package, and re-implement the above context manager using decorator as per below:
from contextlib import contextmanager @contextmanager def nullify_output(suppress_stdout=True, suppress_stderr=True): stdout = sys.stdout stderr = sys.stderr devnull = open(os.devnull, "w") try: if suppress_stdout: sys.stdout = devnull if suppress_stderr: sys.stderr = devnull yield finally: if suppress_stdout: sys.stdout = stdout if suppress_stderr: sys.stderr = stderr
With the above decorator implementation, you shall be able to get the same result when you call the function:
with nullify_output(suppress_stdout=True, suppress_stderr=True): result = externallib.check_result() print(result)
Everything seems to be good as of now, are we going to conclude here? Wait, there is something else we can still improve – instead of totally discard the messages, can we collect them into logging file?
Suppress stdout and stderr with redirect_stdout and redirect_stderr
If you scroll down the Python contextlib documentation further, you will notice there are two methods related to stdout and stderr: redirect_stdout and redirect_stderr . They are quite self-explanatory by their names, and also accept a file-like object as the redirect target.
With these two functions, we shall be able to make our code even more concise, meanwhile we can easily collect back the output message into our log file.
from contextlib import redirect_stdout, redirect_stderr import io, logging logging.basicConfig(filename='error.log', level=logging.DEBUG) f = io.StringIO() with redirect_stdout(f), redirect_stderr(f): result = externallib.check_result() logging.info(f.getvalue()) print(result)
If you check the log file, you shall see the stdout and stderr messages were collected correctly.
Of course, if you wish to continue disposing these messages, you can still specify the target file as devnull, so that nothing will be collected.
With all the above examples and explanations, hopefully you are able to use the code snippets and customize it to meet the objective in your own project. Directly disposing the stderr sometimes may not be a good idea in case there are some useful information for your later troubleshooting, so I would recommend to collect it into a log file as much as possible and do proper housekeeping to ensure the logs are not growing too fast.
If you are looking for solution to suppress certain known python exceptions, you may check out the suppress function from contextlib package.