【问题标题】:IPython Notebook Javascript: retrieve content from JavaScript variablesIPython Notebook Javascript:从 JavaScript 变量中检索内容
【发布时间】:2015-06-17 21:41:35
【问题描述】:

函数(由 IPython Notebook 单元调用)是否可以检索 JavaScript 变量的内容(例如 IPython.notebook.notebook_path,其中包含当前笔记本的路径)?

以下内容在直接写入单元格时效果很好(例如,基于this question 及其 cmets):

from IPython.display import display,Javascript
Javascript('IPython.notebook.kernel.execute("mypath = " + "\'"+IPython.notebook.notebook_path+"\'");')

但是如果我尝试把它放在一个函数中,那它就会崩溃:

# this doesn't work
from IPython.display import display,Javascript
def getname():
    my_js = """
    IPython.notebook.kernel.execute("mypath = " + "\'"+IPython.notebook.notebook_path+"\'");
    """
    Javascript(my_js)
    return mypath

(是的,我已经尝试在 my_js 脚本和函数中创建 global 变量 mypath 。另请注意:不要被变量中可能的剩余值所迷惑来自之前的命令;为确保在调用函数之前使用mypath = None; del mypath 重置变量,或重新启动内核。)

另一种表述问题的方法是:“IPython.notebook.kernel.execute() 设置的变量的范围(时间和地点)是什么?”?

我认为这不是一个无害的问题,并且可能与 IPython 用于控制其内核及其变量的机制有关,而我不太了解。以下实验说明了该机制的某些方面。以下在两个单独的单元格中完成时有效,但如果两个单元格合并则不起作用:

单元格 [1]:

my_out = None
del my_out
my_js = """
IPython.notebook.kernel.execute("my_out = 'hello world'");
"""
Javascript(my_js)

单元格 [2]:

print(my_out)

这有效并产生预期的hello world。但是如果你合并这两个单元格,它就不起作用了(NameError: name 'my_out' is not defined)。

【问题讨论】:

标签: javascript python ipython-notebook


【解决方案1】:

我认为问题与 Javascript 是异步有关,而 python 不是。通常你会认为 Javascript(""" python cmd """) 命令被执行了,然后你的打印语句应该能按预期正常工作。但是,Javascript 命令被触发但未执行。最有可能在单元格 1 执行完全完成后执行。

我用睡眠功能尝试了你的例子。没有帮助。

asnyc 问题可以通过在 my_js 中,但在 kernel.execute 行之前添加一条警告语句来轻松查看。甚至在尝试执行 python 命令之前就应该触发警报。

但是在单元格 1 中存在 print (my_out) 语句时,您将再次收到相同的错误而没有任何警报。如果你把打印行拿出来,你会看到在单元格 1 中弹出警报。但是变量 my_out 是在之后设置的。

my_out = None
del my_out
my_js = """
**alert ("about to execute python comand");**
IPython.notebook.kernel.execute("my_out = 'hello world'");
"""
Javascript(my_js)

笔记本中还有其他 javascript 实用程序,例如 IPython.display.display_xxx,从显示视频到文本对象各不相同,但即使是文本对象选项也不起作用。

很有趣,我用我的 webgl 画布应用程序测试了这个,它在 HTML5 画布上显示对象; display.display_javascript(javascript object) 工作正常(这是一个 looong html5 文档),而输出的两个单词没有显示?!也许我应该将输出嵌入到画布应用程序的某个地方,所以它会显示在画布上:)

【讨论】:

    【解决方案2】:

    我写了一个相关的问题 (Cannot get Jupyter notebook to access javascript variables) 并想出了一个可以完成这项工作的 hack。它使用 Python 中的 input(prompt) 命令确实会阻塞执行循环并等待用户输入这一事实。所以我看看这是如何在 Javascript 端处理的,并在那里插入拦截代码。

    拦截代码为:

    import json
    from IPython.display import display, Javascript
    display(Javascript("""
      const CodeCell = window.IPython.CodeCell;
    
      CodeCell.prototype.native_handle_input_request = CodeCell.prototype.native_handle_input_request || CodeCell.prototype._handle_input_request
      CodeCell.prototype._handle_input_request = function(msg) {
        try {
          // only apply the hack if the command is valid JSON
          console.log(msg.content.prompt)
          const command = JSON.parse(msg.content.prompt);
          const kernel = IPython.notebook.kernel;
          // return some value in the Javascript domain, depending on the 'command'.
          // for now: specify a 5 second delay and return 'RESPONSE'
          kernel.send_input_reply(eval(command["eval"]))
        } catch(err) {
          console.log('Not a command',msg,err);
          this.native_handle_input_request(msg);
        }
      }
    """))
    

    拦截代码查看输入提示是否为有效 JSON,在这种情况下,它会根据 command 参数执行操作。在这种情况下,它运行commend["eval"] javascript 表达式并返回结果。 运行此单元后,您可以使用:

    notebook_path = input(json.dumps({"eval":"IPython.notebook.notebook_path"}))
    

    我必须承认,这确实是一个 hack。

    【讨论】:

      【解决方案3】:

      好的,我找到了解决问题的方法:从 Javascript 调用 Python 函数并让它完成我需要的所有操作,而不是将名称返回到“上方”​​并在那里使用该名称。

      对于上下文:我和我的同事有很多实验笔记本;我们试验了一段时间并尝试了各种事情(在机器学习环境中)。在每个变体/运行结束时,我想保存笔记本,以反映时间的名称复制它,将其上传到 S3,将其从输出中剥离并推送到 git,记录文件名、cmets 和结果分数进入数据库等。简而言之,我想自动跟踪我们所有的实验。

      这是我目前所拥有的。在我的笔记本底部,我写了:

      In [127]: import mymodule.utils.lognote as lognote
                lognote.snap()
      
      In [128]: # not to be run in the same shot as above
                lognote.last
      Out[128]: {'file': '/data/notebook-snapshots/2015/06/18/20150618-004408-save-note-exp.ipynb',
                 'time': datetime.datetime(2015, 6, 18, 0, 44, 8, 419907)}
      

      在一个单独的文件中,例如mymodule/utils/lognote.py:

      # (...)
      
      from datetime import datetime
      from subprocess import call
      from os.path import basename, join
      from IPython.display import display, Javascript
      
      # TODO: find out where the topdir really is instead of hardcoding it
      _notebook_dir = '/data/notebook'
      _snapshot_dir = '/data/notebook-snapshots'
      
      def jss():
          return """
          IPython.notebook.save_notebook();
          IPython.notebook.kernel.execute("import mymodule.utils.lognote as lognote");
          IPython.notebook.kernel.execute("lognote._snap('" + IPython.notebook.notebook_path + "')");
          """
      def js():
          return Javascript(jss())
      
      def _snap(x):
          global last
          snaptime = datetime.now()
          src = join(_notebook_dir, x)
          dstdir = join(_snapshot_dir, '{}'.format(snaptime.strftime("%Y/%m/%d")))
          dstfile = join(dstdir, '{}-{}'.format(snaptime.strftime("%Y%m%d-%H%M%S"), basename(x)))
          call(["mkdir", "-p", dstdir])
          call(["cp", src, dstfile])
          last = {
              'time': snaptime,
              'file': dstfile
              }
      
      def snap():
          display(js())
      

      【讨论】:

      • 啊,IPython.notebook.save_notebook() 在笔记本实际保存之前立即返回,所以我们最终复制了一些不完整/脏的笔记本。我需要解决这个问题。
      • 你最终解决了吗?我面临着类似的问题,我是否需要在运行时将 JS 变量从 Canvas 游戏写入 python:|
      【解决方案4】:

      要添加到其他出色的答案,尝试在 nb 加载时运行 jupyter nb javascript 魔法的浏览器存在细微差别。

      演示:创建并运行以下单元格:

      %%javascript 
      IPython.notebook.kernel.execute('1')
      

      现在保存笔记本,关闭它,然后重新打开它。当你这样做时,在那个单元格下你会突然看到一个红色的错误:

      Javascript error adding output!
      TypeError: Cannot read property 'execute' of null
      See your browser Javascript console for more details.
      

      这意味着浏览器已经解析了一些 js 代码并尝试运行它。这是chrome中的错误,在不同的浏览器中可能会有所不同。

      我不知道为什么这个 jupyter javascript 魔法单元在加载时运行,为什么 jupyter notebook 没有正确地转义东西,但是浏览器看到一些 js 代码,所以它运行它并且失败了,因为 notebook 内核没有还不存在!

      所以你必须添加一个对象是否存在的检查:

      %%javascript 
      if (IPython.notebook.kernel) {
          IPython.notebook.kernel.execute('1')
      }
      

      现在加载没有问题。

      就我而言,我需要保存笔记本并在其上运行外部脚本,所以我最终使用了以下代码:

      from IPython.display import display, Javascript
      def nb_auto_export():
          display(Javascript("if (IPython.notebook) { IPython.notebook.save_notebook() }; if (IPython.notebook.kernel) { IPython.notebook.kernel.execute('!./notebook2script.py  ' + IPython.notebook.notebook_name )}"))
      

      在笔记本的最后一个单元格中:

      nb_auto_export()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-02
        • 2022-11-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多