6

Is it possible in an (interactive) IPython session to pass the stdout output through a pager, like less? If so, how?

For example, in

In [1]: from some_module import function_that_prints_a_lot

In [2]: function_that_prints_a_lot()

... everything scrolls away ...

I would like to page through the stdout output of function_that_prints_a_lot.

Another example:

In [1]: %run script_that_prints_a_lot.py

I've looked through IPython magic commands but didn't find any solution.

Alexey
  • 3,241
  • 5
  • 25
  • 40
  • `help(function_with_LONG_docstring)` already is passed through a pager, it SHOULD be possible... OK I'll start a bounty – gboffi Oct 31 '17 at 14:22
  • which version of python are you using? – Tarun Lalwani Oct 31 '17 at 15:24
  • Also check if `%page -r ` works for you? Put some data with newlines in variable and see this gives you the expected paging you are looking for – Tarun Lalwani Oct 31 '17 at 17:18
  • @TarunLalwani, i think it is the version of IPython that matters, not so much the one of Python. Anyway, i am using more or less the latest versions of everything. – Alexey Oct 31 '17 at 18:54
  • As to using `%page`, did you mean [redirecting `stdout` to a string](https://docs.python.org/3.5/library/contextlib.html#contextlib.redirect_stdout)? I would appreciate a simpler method. – Alexey Oct 31 '17 at 18:54
  • @Alexey, is the function using `print` to print these logs? Also in the end Kernel Python version does matter, if it is Python2 or Python3. That is the reason I asked – Tarun Lalwani Oct 31 '17 at 18:58
  • @TarunLalwani, I am using Python 3.6. Yes, the function uses `print`. Why does `print` matter? – Alexey Oct 31 '17 at 19:14
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/157938/discussion-between-tarun-lalwani-and-alexey). – Tarun Lalwani Oct 31 '17 at 19:22

2 Answers2

5

As discussed in chat there is no simple way of doing this. Since the function prints the values, the only thing you can do is Capture output + Then page output. There are few issues on jupyter that you might be interested in

https://github.com/jupyter/notebook/issues/2049

https://github.com/ipython/ipython/issues/6516

Capturing output

Output capturing can be done multiple ways

1. Overload Print Method

import sys
data = ""
def myprint(value, *args, sep=' ', end='\n', file=sys.stdout, flush=False):
    global data
    current_text = value + " ".join(map(str, args)) + "\n"
    data += current_text

original_print = print
print = myprint

def testing():
    for i in range(1,1000):
        print ("i =", i)

testing()

original_print("The output from testing function is", data)

2. Capture output using StringIO

from cStringIO import StringIO
import sys

class Capturing(list):
    def __enter__(self):
        self._stdout = sys.stdout
        sys.stdout = self._stringio = StringIO()
        return self
    def __exit__(self, *args):
        self.extend(self._stringio.getvalue().splitlines())
        del self._stringio    # free up some memory
        sys.stdout = self._stdout

Usage:

with Capturing() as output:
    do_something(my_object)

3. Capture output using redirect_stdout

import io
from contextlib import redirect_stdout

f = io.StringIO()
with redirect_stdout(f):
    do_something(my_object)
out = f.getvalue()

4. Capture using %%capture magic command

Capture magic

Paging Output

You can use magin %page

%page -r <variablename>

https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-page

Or you can use Ipython code

from IPython.core import page
page.page(variable)

For more details refer to below

PS: Some helpful threads

How to capture stdout output from a Python function call?

How can I redirect print output of a function in python

https://github.com/ipython/ipython/wiki/Cookbook:-Sending-built-in-help-to-the-pager

overload print python

Tarun Lalwani
  • 124,930
  • 8
  • 149
  • 214
  • Links only answer – gboffi Nov 01 '17 at 13:01
  • @TarunLalwani, i think it is usually recommended to include some relevant quotes directly into the answer. – Alexey Nov 02 '17 at 07:31
  • +1 for being comprehensive. The other answer only showcases one possible way -- this one lists them all. It has to be concise to fit all, but it should include critical points from at least the off-site links 'cuz they'll definitely go down someday. – ivan_pozdeev Nov 06 '17 at 21:21
  • I think that some evidence (a quote from a github issue, for example) is needed to confirm that there is no built-in method in IPython to page stdout output. – Alexey Nov 07 '17 at 08:53
  • @Alexey, probably this open github issue? https://github.com/ipython/ipython/issues/9891 – Tarun Lalwani Nov 07 '17 at 09:13
1

Using bits and pieces from various sources, but essentially it's from IPython's cookbook and Defining custom magics from IPython official documentation

In [1]: from IPython.core.magic import register_line_magic

In [2]: @register_line_magic
   ...: def my_pager(line):
   ...:     "my line magic"
   ...:     import io
   ...:     from IPython.core import page
   ...:     from contextlib import redirect_stdout
   ...:     f = io.StringIO()
   ...:     with redirect_stdout(f):
   ...:         eval(line)
   ...:     page.pager_page(f.getvalue())
   ...: del my_pager # don't pollute my namespace    

In [3]: def pippo(): print('\n'.join(str(i)for i in range(80)))

In [4]: %my_pager pippo()

This approach has a serious drawback: if the function call that is the argument to %my_pager returns a value, said value is lost (no, %mypager result=print_and_return() won't work...)

gboffi
  • 17,041
  • 5
  • 45
  • 76
  • Thanks, but my example with a function was just an example, the question was about sending all the `stdout` output to the pager permanently (when using `%run` too). Would it be possible with this approach? I will edit the question. – Alexey Nov 01 '17 at 14:58
  • Re `%run`: if the script needs no input from `stdin`, I can't foresee particular problems in writing a magic that captures the output and later passes it to `page.x` — in general imho you can't unless you rewrite (a part of) IPython. – gboffi Nov 01 '17 at 15:20