javascript - 无法让 Jupyter 笔记本访问 javascript 变量
问题描述
我有一个做文件格式转换的网站,这个服务可以通过window.postMessage系统被其他网站使用。如果一个人也可以通过 Jupyter 笔记本做到这一点,那就太好了。然而,我遇到了一个严重的问题。我可以让 python 创建一个 Javascript 命令,将格式 A 的文件发送到网站,我可以在延迟一段时间后访问 Javascript 中的响应(格式 B 的文件),但我无法将 Javascript 的响应返回到 Python 中进一步处理。我制作了一个尽可能简单的笔记本来演示这个问题。原因似乎是 Python 首先执行所有单元格(在 Run all 场景中),然后才查看任何新的执行请求。
stackoverflow 上有类似的问题,例如: IPython Notebook Javascript: retrieve content from JavaScript variables。从我目前发现的情况来看,这个问题没有解决办法。我觉得这非常令人失望,因为创建可从 Jupyter 笔记本直接访问的 Web 服务会非常酷,而不必走 node.js 路线。
import asyncio, time
from IPython.display import display, Javascript
下面的代码将 Javascript 插入页面: 'sendRequest' 函数只是延迟 5 秒,但它旨在通过 window.postMessage 与另一个网页通信。
futureResponse 是一个 asyncio Future (python)。当 sendRequest (Javascript) 完成并使用 kernel.execute (Javascript) 调用 responseHandler (python) 时,它会得到一个结果。
global futureResponse
def responseHandler(response):
futureResponse.set_result(response)
display(Javascript("""
window.responseHandler = (response) => {
var kernel = IPython.notebook.kernel;
var pyCommand = 'responseHandler(\\''+response+'\\')';
kernel.execute(pyCommand);
console.log('responseHandler called')
};
window.sendRequest = () => {
return new Promise( (resolve,reject) => { setTimeout(()=>resolve('RESPONSE'),5000) } )
}
"""))
下面的代码插入了调用 sendRequest 的 Javascript。这将在 setTimeout 延迟(5 秒)之后调用 responseHandler(Javascript),它调用 kernel.execute 调用 responseHandler(python),它设置 futureResponse 的结果。但是,如果我使用“等待 futureResponse”,它永远不会实现。
# the value of futureResponse will be set by the responseHandler
futureResponse = asyncio.Future()
display(Javascript("""
window.sendRequest().then(
(response) => window.responseHandler(response)
)
"""))
# This does not work (futureResponse unresolved):
time.sleep(6)
# With await, the notebook keeps waiting forever.
#await futureResponse
print(futureResponse)
如果在前一个单元格之后立即评估下面的单元格(如在 Run all 场景中),则 futureResponse 仍然未解决。但如果在 5 秒后评估,futureResponse 的值为“RESPONSE”。
futureResponse
解决方案
我对此进行了进一步研究并找到了一个解决方案,但它确实是一个可能会在 Jupyter notebook 的未来版本中破坏的 hack。我希望有人有一个更面向未来的答案。
这个想法是为了避免代码示例使用 Jupyter 消息系统的 stdin 通道创建的 catch-22 ( https://jupyter-client.readthedocs.io/en/stable/messaging.html )。
代码的工作方式如下:当代码单元包含input('prompt-text')
命令时,将通过专用的标准输入通道将其发送到笔记本客户端,该通道独立于正常的单元执行流。因此,我“破解”了笔记本客户端的代码,以捕获内核发送的输入请求,并在不提示用户的情况下提供答案。
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
const command = JSON.parse(msg.content.prompt);
const kernel = IPython.notebook.kernel;
// in the future: send the command to a server and wait for a response.
// for now: specify a 5 second delay and return 'RESPONSE'
setTimeout(() => { kernel.send_input_reply('RESPONSE') },5000)
} catch(err) {
console.log('Not a command',msg,err);
this.native_handle_input_request(msg);
}
}
"""))
response = input(json.dumps({"do":"something"}))
print(response)
这会阻止执行 5 秒,然后打印“响应”并继续。
推荐阅读
- express - 使用 mocha 测试 express 应用时出现 JWT 格式错误
- dart - 给定字符串作为方法名称时动态调用类方法
- python - Scrapy:存储和处理数据
- javascript - 在 MongoDB 和 node.js 中将多个搜索选项合二为一
- javascript - 动态添加数据javascript
- java - 递归 lambda TriFunction
- javascript - For 循环、等待和承诺 - Javascript
- javascript - 无法从 redux 存储中获取数据
- javascript - 如何使用“this”关键字将 JS 函数应用于多个 HTML 元素?
- c# - 连接访问动态