Redirecting stdout to something most developers will need to do at some point or other. It can be useful to redirect stdout to a file or to a file-like object. I have also redirected stdout to a text control in some of my desktop GUI projects. In this article we will look at the following:
- Redirecting stdout to a file (simple)
- The Shell redirection method
- Redirecting stdout using a custom context manager
- Python 3’s contextlib.redirect_stdout()
- Redirect stdout to a wxPython text control
The easiest way to redirect stdout in Python is to just assign it an open file object. Let’s take a look at a simple example:
import sys def redirect_to_file(text): original = sys.stdout sys.stdout = open('/path/to/redirect.txt', 'w') print('This is your redirected text:') print(text) sys.stdout = original print('This string goes to stdout, NOT the file!') if __name__ == '__main__':Redirecting stdout / stderr redirect_to_file('Python rocks!')
Here we just import Python’s sys module and create a function that we can pass strings that we want to have redirected to a file. We save off a reference to sys.stdout so we can restore it at the end of the function. This can be useful if you intend to use stdout for other things. Before you run this code, be sure to update the path to something that will work on your system. When you run it, you should see the following in your file:
This is your redirected text: Python rocks!
That last print statement will go to stdout, not the file.
Shell redirection is also pretty common, especially in Linux, although Windows also works the same way in most cases. Let’s create a silly example of a noisy function that we will call noisy.py:
# noisy.py def noisy(text): print('The noisy function prints a lot') print('Here is the string you passed in:') print('*' * 40) print(text) print('*' * 40) print('Thank you for calling me!') if __name__ == '__main__': noisy('This is a test of Python!')
You will notice that we didn’t import the sys module this time around. The reason is that we don’t need it since we will be using shell redirection. To do shell redirection, open a terminal (or command prompt) and navigate to the folder where you saved the code above. Then execute the following command:
python noisy.py > redirected.txt
The greater than character (i.e. >) tells your operating system to redirect stdout to the filename you specified. At this point you should have a file named “redirected.txt” in the same folder as your Python script. If you open it up, the file should have the following contents:
The noisy function prints a lot Here is the string you passed in: **************************************** This is a test of Python! **************************************** Thank you for calling me!
Now wasn’t that pretty cool?
Redirect stdout with a context manager
Another fun way to redirect stdout is by using a context manager. Let’s create a custom context manager that accepts a file object to redirect stdout to:
import sys from contextlib import contextmanager @contextmanager def custom_redirection(fileobj): old = sys.stdout sys.stdout = fileobj try: yield fileobj finally: sys.stdout = old if __name__ == '__main__': with open('/path/to/custom_redir.txt', 'w') as out: with custom_redirection(out): print('This text is redirected to file') print('So is this string') print('This text is printed to stdout')
When you run this code, it will write out two lines of text to your file and one to stdout. As usual, we reset stdout at the end of the function.
Python 3.4 added the redirect_stdout function to their contextlib module. Let’s try using that to create a context manager to redirect stdout:
import sys from contextlib import redirect_stdout def redirected(text, path): with open(path, 'w') as out: with redirect_stdout(out): print('Here is the string you passed in:') print('*' * 40) print(text) print('*' * 40) if __name__ == '__main__': path = '/path/to/red.txt' text = 'My test to redirect' redirected(text, path)
This code is a little simpler because the built-in function does all the yielding and resetting of stdout automatically for you. Otherwise, it works in pretty much the same way as our custom context manager.
Redirecting stdout in wxPython
I have written about redirecting stdout in wxPython on several occasions. The following code is actually from an article I wrote in 2009 and updated in 2015:
import sys import wx class MyForm(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, title="wxPython Redirect Tutorial") # Add a panel so it looks the correct on all platforms panel = wx.Panel(self, wx.ID_ANY) style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL log = wx.TextCtrl(panel, wx.ID_ANY, size=(300,100), style=style) btn = wx.Button(panel, wx.ID_ANY, 'Push me!') self.Bind(wx.EVT_BUTTON, self.onButton, btn) # Add widgets to a sizer sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(log, 1, wx.ALL|wx.EXPAND, 5) sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5) panel.SetSizer(sizer) # redirect text here sys.stdout = log def onButton(self, event): print "You pressed the button!" # Run the program if __name__ == "__main__": app = wx.App(False) frame = MyForm().Show() app.MainLoop()
This code just creates a simple frame with a panel that contains a multi-line text control and a button. Whenever you press the button, it will print out some text to stdout, which we have redirected to the text control. Give it a try to see how well it works!
Now you know several different methods for redirecting stdout to files. Some methods are better than others. Personally I thought it was cool that Python 3 now has a context manager built-in just for this purpose. Speaking of which, Python 3 also has a function for redirecting stderr. All of these examples can be modified slightly to support redirecting stderr or both stdout and stderr. The very last thing we touched on was redirecting stdout to a text control in wxPython. This can be really useful for debugging or for grabbing the output from a subprocess, although in the latter case you will need to print out the output to have it redirected correctly.