9

So I was trying to redirect stdout for a some of the cells in my Jupyter Notebook to a file with this and then cancel it with this for the rest of the cells. The output from the first set of cells landed in the file like it was meant to. The second set of cells after the cancel command sys.stdout = sys.__stdout__ was giving no output, appearing to do nothing, but I later realised it was landing in the terminal where the notebook was launched.

It works perfectly in the Python interpreter with exactly the same Python:

(miniconda3-latest) cardamom@pegasus:~/Documents/project1 $ python
Python 3.6.0 |Continuum Analytics, Inc.| (default, Dec 23 2016, 12:22:00) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.stdout = open('stdout.txt', 'w')
>>> print("a")
>>> print("a")
>>> print("a")
>>> sys.stdout = sys.__stdout__
>>> print ("b")
b
>>> print ("b")
b
>>> print ("b")
b
>>> # lots of a's are in the file and no b's

A similar approach in the Jupyter notebook is giving this in the terminal:

[W 22:05:34.192 NotebookApp] 404 GET /nbextensions/widgets/notebook/js/extension.js (127.0.0.1) 1.71ms referer=http://localhost:8889/notebooks/test_stdout.ipynb
b
b
b

How can this code be adapted so that after resetting it, I get the b's appearing under the cells where the output usually goes instead of in the terminal?

cardamom
  • 6,873
  • 11
  • 48
  • 102

2 Answers2

12

The only explanation I see is that sys.stdout is not sys.__stdout__ but a rerouted/modified file object in order to be able to put data in cells (your comment indicates that it's a ipykernel.iostream.OutStream instance).

So instead of resetting to sys.__stdout__, you should store the reference of sys.stdout:

import sys
old_stdout = sys.stdout
sys.stdout = open('stdout.txt', 'w')
...
sys.stdout = old_stdout

Note that this method also works with a standard terminal.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • Thanks, that works. Never thought it was something you could backup but when I look at the object for `old_stdout` in the notebook it looks like this: `` – cardamom Jul 19 '17 at 20:42
  • I must admit that I didn't even notice that `sys.__stdout__` even existed. I always used the method described in my answer (must be used to languages like C where everything must be done by hand :)). Note that you're not backuping, it, you're just storing the _reference_ instead of losing it. – Jean-François Fabre Jul 19 '17 at 20:44
  • 3
    The problem comes when your program is aborted. Jupyter remembers the state of stdout, and it can't be recovered without a kernel restart. This is inconvenient. Is there some other way to reset the stdout to redirect to the Jupyter notebook? – Steve Jul 29 '18 at 07:13
0

To handle your case of aborted execution, you could try to handle the error.

import sys
nb_stdout = sys.stdout
with open("cell_output.deleteme", 'w') as cl_stdout:
    sys.stdout = cl_stdout
    try:
        print("hello, world!")
        raise Exception("goodbye, cruel world!")
    except Exception as e:
        print(e)
    finally:
        print("cell stdout")
        sys.stdout = nb_stdout
        print("notebook stdout")
Ion Freeman
  • 512
  • 4
  • 19