IPython Notebook Javascript: retrieve content from JavaScript variables
javascript, jupyter-notebook, python
Solution
I wrote a related question (Cannot get Jupyter notebook to access javascript variables) and came up with a hack that does the job. It uses the fact that the input(prompt) command in Python does block the execution loop and waits for user input. So I looked how this is processed on the Javascript side and inserted interception code there.
The interception code is:
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);
}
}
"""))
The interception code looks whether the input prompt is valid JSON, and in that case it executes an action depending on the `command` argument. In this case, it runs the commend["eval"] javascript expression and returns the result. After running this cell, you can use:
notebook_path = input(json.dumps({"eval":"IPython.notebook.notebook_path"}))
Quite a hack, I must admit.
Problem
Is there a way for a function (called by an IPython Notebook cell) to retrieve the content of a JavaScript variable (for example `IPython.notebook.notebook_path` which contains the path of the current notebook)? The following works well when written directly within a cell (for example, based on this question and its comments): ``` from IPython.display import display,Javascript Javascript('IPython.notebook.kernel.execute("mypath = " + "\'"+IPython.notebook.notebook_path+"\'");') ``` But that falls apart if I try to put it in a function: ``` # 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 ``` (And yes, I've tried to make `global` the `mypath` variable, both from within the `my_js` script and from within the function. Also note: don't be fooled by possible leftover values in variables from previous commands; to make sure, use `mypath = None; del mypath` to reset the variable before calling the function, or restart the kernel.) Another way to formulate the question is: "what's the scope (time and place) of a variable set by `IPython.notebook.kernel.execute()`"? I think it isn't an innocuous question, and is probably related to the mechanism that IPython uses to control its kernels and their variables and that I don't know much about. The following experiment illustrate some aspect of that mechanism. The following works when done in two separate cells, but doesn't work if the two cells are merged: Cell [1]: ``` my_out = None del my_out my_js = """ IPython.notebook.kernel.execute("my_out = 'hello world'"); """ Javascript(my_js) ``` Cell [2]: ``` print(my_out) ``` This works and produces the expected `hello world`. But if you merge the two cells, it doesn't work (`NameError: name 'my_out' is not defined`).