在IPython的cell_magic中无法访问笔记本变量

3
我正试图在ipython笔记本中对ROOT文件进行一些操作(这里的ROOT是CERN的ROOT数据分析程序,具有python接口)。 ROOT的一个令人讨厌的特征是它经常将输出直接发送到stdout而不是将此类输出作为字符串返回。 为了使此输出出现为ipython notebook中的结果,我编写了一个小的cell_magic,它:

  • 告诉ROOT将stdout重定向到文件
  • 运行单元格中的python命令
  • 关闭ROOT的输出重定向
  • 读取文件并打印内容

这是我的小的cell magic代码

import tempfile
import ROOT
from IPython.core.magic import register_cell_magic

@register_cell_magic
def rootprint(line, cell):
  """Capture Root stdout output and print in ipython notebook."""

  with tempfile.NamedTemporaryFile() as tmpFile:

    ROOT.gSystem.RedirectOutput(tmpFile.name, "w")
    exec cell
    ROOT.gROOT.ProcessLine("gSystem->RedirectOutput(0);")
    print tmpFile.read()

如果我将这段代码放入一个 IPython 笔记本单元格并执行,那么它就可以很好地工作。例如:
In [53]: f = ROOT.TFile('myRootFile.root')   # Load the Root file


In [54]: %%rootprint
         f.ls()  # Show the contents of the ROOT file

         ... f.ls() output appears here - yay! ...
< p >通常,f.ls() 的输出会送到stdout,而不会出现在单元格的结果中。但是使用这个单元格魔法,输出确实会出现在单元格结果中!太棒了!

但是,如果我将单元格魔法代码放入模块中,则无法正常工作。例如,我将上面的代码放入ipythonRoot.py中,并在笔记本中执行import ipythonRoot.py。当我尝试运行上面的%%rootprint单元格时,会出现错误,指出f未定义。我尝试将exec行更改为exec cell in globals(),但这并没有帮助。

有没有方法可以做到这一点?此外,是否有更好的编写cell_magic函数的方法(例如,应该返回输出而不是打印输出)?非常感谢您的任何帮助!

2个回答

5
我通过查看IPython的代码,特别是IPython/core/magics/execution.py文件中的一些提示来理解了这个问题。这是我的新模块。
import tempfile
import ROOT
from IPython.core.magic import (Magics, magics_class, cell_magic)

@magics_class
class RootMagics(Magics):
  """Magics related to Root.

      %%rootprint  - Capture Root stdout output and show in result cell
  """

    def __init__(self, shell):
      super(RootMagics, self).__init__(shell)

  @cell_magic
  def rootprint(self, line, cell):
    """Capture Root stdout output and print in ipython notebook."""

    with tempfile.NamedTemporaryFile() as tmpFile:

      ROOT.gSystem.RedirectOutput(tmpFile.name, "w")
      ns = {}
      exec cell in self.shell.user_ns, ns
      ROOT.gROOT.ProcessLine("gSystem->RedirectOutput(0);")
      print tmpFile.read()

# Register
ip = get_ipython() 
ip.register_magics(RootMagics)

请注意使用 Magics 类和 shell 属性,其中包含笔记本命名空间等内容。 它可以定期导入并且运行正常。

请注意,有一个快捷方式 - 如果您使用IPython实例(作为“shell”传递给__init__),则可以调用shell.ex(cell)来执行代码。 - Thomas K

1
如果我理解正确,魔术/别名/用户命名空间是分开的。进行“导入”不会干扰魔术/别名命名空间。这就是为什么有%load_ext 魔术命令的原因,但你必须定义入口点。[使用R magic的示例]。(https://github.com/ipython/ipython/blob/master/IPython/extensions/rmagic.py#L617)。
另外,我建议看一下capture magic,它可以在没有临时文件的情况下捕获stdout/err。
一旦这个工作正常,你也可以将你的扩展添加到扩展索引中。

谢谢你提供的好建议!Root 的一个问题是它自己处理 stdout/err,所以我认为没有办法避免告诉它重定向到文件。 - Adam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接