全部 --
好的...我已经尝试了各种方法,并且在这个过程中得到了不错的教育。归根结底,从远程服务器上运行的Jupyter Notebook(也称为IPython)可以调用基于本地主机的服务,并且可能还可以获取结果以便在Python中使用(虽然有限制)。这很重要,因为它使生物工作流能够调用Cytoscape桌面应用程序的基于REST的自动化接口。
我正在编写这篇文章,以便后继者可以看到并受益,或者放弃。我得到了其他StackOverlow和独立文章的大量帮助...太多了,无法一一列举,最突出的是:
ipython-notebook-javascript-python-communication
Execute Discussion
workaround-for-wrapping-a-js-function-in-python-in-jupyter-notebook
这个问题有三个部分:
- 执行HTTP操作(...假设是GET,但POST,PUT等也可以)
- 获取HTTP状态和结果
- 在Python中使用结果
有多个挑战:
- 将URL和负载(用于PUT / POST)传递到HTTP调用器中
- 仅在HTTP结果可用时返回给调用者(而不是之前)
- 通过Python变量使HTTP结果可用
存在一个主要问题,因为Python是一种传统的同步语言,在基于浏览器的(IPython)环境中执行(部分)。通常,这不是问题,因为远程Jupyter服务器执行完整的Python单元格,然后执行另一个单元格。问题出现在HTTP调用必须在Javascript中进行(即在%%js或%%javascript单元格中或作为IPython.display.HTML()调用的参数),这在本地浏览器中执行(而不是在远程服务器上)。基于浏览器的Javascript的一个基本原则是长时间(例如HTTP)操作应异步完成(例如使用Promises的Fetch(),使用Yield()的生成器或正常的XMLHttpRequest调用),以保持高度交互式浏览器性能。对于等待答案的工作流程,异步执行会通过回调地狱使不擅长生物信息学的Python程序员陷入困境。此外,这样的工作流程预计会占用大量计算资源,并且不需要交互性...必须保留因果关系。
在Javascript中,解决方案的要点是使用同步XMLHttpRequest调用。这会得到同步的答案。接下来,使用未记录的IPython.notebook.kernel.execute()将一个值存储到远程服务器上的Python变量中。这是一项异步操作,必须在最后完成。(您只能通过使用Chrome浏览器中的开发模式查看execute()的源代码: F12->static/notebook/js/services/kernels/kernel.js,并找到kernel.js/Kernel.prototype.execute)。如果这是在单元格的末尾完成的,IPython将在获取下一个单元格或等待用户输入时暂停。在这两种情况下,似乎Python内核服务于由execute()创建的语句队列。如果确实是这样,那么可以安全地期望包含HTTP结果的Python变量在随后的单元格中准备好读取。如果在同一单元格中执行execute()和Python变量获取,则很可能不会准备好。
当你向XMLHttpRequest.open()函数传递“false”时,会创建一个同步的XMLHttpRequest调用。一些浏览器可能会抱怨,因为它们认为同步调用是有问题的并已被弃用。最好使用一个不抱怨的浏览器(例如,Chrome)。
奇怪的事情:如果你调用HTML()来执行Javascript,它应该是单元格中的最后一个调用;否则,HTML()调用将失败。如果你将HTML()调用包装在IPython.display.display()调用中,在同一单元格中可以放置其他Python语句……只是不要指望HTTP结果变量被设置,直到IPython转换到下一个单元格。请注意,像Timeout()这样的解决方法在这里是无效的,因为它似乎不能强制处理Execute()语句队列,所以您真正需要等到下一个单元格才能访问HTTP结果。
这个Javascript单元格创建了将执行工作的Javascript函数。请注意Cy*命名约定,这使与IPython浏览器代码的冲突不太可能发生。
%%js
function CyHttpGet(url) {
var xhr = new XMLHttpRequest()
xhr.open('GET', url, false)
xhr.send(null);
if (xhr.status === 200) {
return xhr.responseText;
}
else {
return "bad news:" + xhr.status
}
}
function CySetKernelVar(varValue) {
varValue = varValue.replace(/'/g, "/'"); // Double any single quotes
var command = "httpResult = '"+ varValue + "'";
var kernel = IPython.notebook.kernel;
kernel.execute(command);
}
这个Python代码块执行实际的HTTP请求:
from IPython.display import HTML
javascript = """
<script type="text/Javascript">
// This is what actually executes the HTTP and sets the result as a Python variable
CySetKernelVar(CyHttpGet('http://localhost:1234'));
</script>
"""
HTML(javascript)
这个Python单元格利用了HTTP结果(必须不是前一个单元格的一部分):
最初的回答:
print(httpResult)
httpResult='xxx' # clear variable so we're not fooled if the Javascript code doesn't do what we thought it would
请注意,通过Execute()从Javascript传递值到Python的能力确实取决于Python内核在单元格之间清除Execute队列的能力。如果这并没有真正发生,我们会时不时地从print(httpResult)中看到"xxx",这是非常糟糕的消息。
请注意,一些网帖假定通过将值存储为DIV语句的innerHTML来从Javascript传递值到Python,并且然后使用HTML()或Javascript()函数读取innerHTML并返回它。我还没有让这个方法起作用,但如果可以,Execute()可能就不必要了。