ipython / ipython

Official repository for IPython itself. Other repos in the IPython organization contain things like the website, documentation builds, etc.

Home Page:https://ipython.readthedocs.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bug in "capture" magic

JaumeAmoresDS opened this issue · comments

Reproducible example: python module (e.g., example.py) with the following code:

def run_code(ip, code):
    ip.run_cell_magic("capture", "output", code)
    output.show()

Call this code from either an ipython terminal or jupyter notebook like so:

from IPython import get_ipython
import example as ex

ip = get_ipython()
ex.run_code (ip, "3+2")

This will produce the following error:

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[1], line 1
----> 1 ex.run_code (ip, "3+2")

File example.py:3, in run_code(ip, code)
      1 def run_code(ip, code):
      2     ip.run_cell_magic("capture", "output", code)
----> 3     output.show()

NameError: name 'output' is not defined

However, if we define the same function in the ipython terminal, it works well:

def run_code(ip, code):
    ip.run_cell_magic("capture", "output", code)
    output.show()

We call it:

run_code (ip, "3+2")

and here's the output:

   5

The same dual behaviour happens if the ip object is created in the module's function vs in the terminal. In the module, it produces the above "NameError" error, but not in the terminal:

In the module:

from IPython import get_ipython

def run_code2 (code):
    get_ipython().run_cell_magic("capture", "output", code)
    output.show()

And call it like so:

ex.run_code2 ("3+2")

This will produce the error shown above.

In the terminal:

def run_code2 (code):
    get_ipython().run_cell_magic("capture", "output", code)
    output.show()

And call it like so:

run_code2 ("3+2")

This will not produce any error.

My guess is that this issue is related with the fact that the variable stack_depth value is currently hard-coded to 2 in run_cell_magic.

I don't think the magics were thoughts to be use in the middle of Python code like so.
Really IPython use string transforms to replace the %%capture syntax, so there are likely a bunch of assumption being broken when you use it in the way.

It would be nice to fix, but I'm not sure I have time to invest into fixing this use case.

Thank you @Carreau! I just figured the way to achieve what I wanted. Using the same example as above, I would write:

from IPython.utils.capture import capture_output

def run_code(ip, code):
    with capture_output(True, True, True) as output:
        ip.run_cell(cell)
    output.show()

and then call the function as shown above:

ip = get_ipython()
run_code (ip, "3+2")

In stackoverflow, @fomightez proposed another solution that uses ip.user_ns['output']

It seems those are valid for my current purpose, although might not be the solution in general.