I have a front end extension which has the following code:
define(
[
'base/js/namespace'
],
function(Jupyter) {
function load_ipython_extension() {
var original_execute_cells = Jupyter.notebook.execute_cells;
function alternative_execute_cells(cell_indices) {
console.log('EXTENSION: running decorated execute_cells function');
}
Jupyter.notebook.execute_cells = alternative_execute_cells;
}
return {
load_ipython_extension: load_ipython_extension
};
}
);
It is supposed to log something to the console every time I run cells. However, it does not do that and cells are run as usual. I had a look at this stackoverflow post, but I cannot find what I am doing wrong.
Is there some special logic, which causes this? Or is it a reference by value issue, so that I only modify the Jupyter object I have in the extension, but not the actual one?
@ZelphirKaltstahl I think you need to modify Jupyter.Notebook.prototype.execute_cells to be able to modify the base value. That's what I do when I override functions in NotebookList anyway and it seems to work
@louise-davies Thanks! That works. At first I had a typo I did not see.
It tells me that:
accessing `Notebook` is deprecated. Use `require("notebook/js/notebook").Notebook`
But when I replace Jupyter with: require("notebook/js/notebook").Notebook, it stops working again. So I guess I'll have to stick with the deprecated way?
I've noticed, that the problem seems to be a bit more difficult than I thought.
I tried overwriting the function and the way @louise-davies suggested works. Except that in the original function this is used:
Notebook.prototype.execute_cells = function (indices) {
if (indices.length === 0) {
return;
}
var cell;
for (var i = 0; i < indices.length; i++) {
cell = this.get_cell(indices[i]);
cell.execute();
}
this.select(indices[indices.length - 1]);
this.command_mode();
this.set_dirty(true);
};
And when my own code runs and calls the original function as a last step:
return original_execute_cells(cell_indices);
The value of that functions this seems to be not available and I get the following TypeError:
TypeError: this is undefined[Learn More] main.min.js:27578:13
Which is the first line of the original function, which does anything with this.
So I guess I somehow have to make it so that the this still remains defined. How would I do this?
I'm not an expert, but I think what you need is bind(). It would work something like this:
return original_execute_cells.bind(this)(cell_indices);
@ZelphirKaltstahl Ah yes, I forgot that the deprecation warning would pop up. However, when I use
require("notebook/js/notebook").Notebook.prototype.execute_cells
It seems to work for me?
As for the troubles with this, @takluyver suggestion to use bind is correct. I was hasty when posting my original reply and I forgot to mention that it would be required.
Thanks for the great help!
I think it has to be:
return original_execute_cells.bind(Jupyter.notebook)(cell_indices);
In my case, as I want the original this and not the one of my current scope. Also the:
require("notebook/js/notebook").Notebook...
Seems to work now. Not sure what was wrong before. Maybe only an only inspector window and I got confused or something like that.
Great, I learned something more about JS today and I have a way to decorate any notebook function. In the other issue @takluyver already showed me how to use events though and I think that might be a cleaner way to do it, in cases where running the original function has the same meaning as firing the event. However, when a specific function does not fire such event, this can still be very useful, I think.
If there are hooks you'd like to add where there isn't currently an event, feel free to open an issue (or a PR ;-) to suggest extra events.
Yes, using events seems like the right solution for this:
define(['base/js/events'], function(events) {
function load_ipython_extension() {
events.on('execute.CodeCell', function (event) {
var cell = event.cell;
console.log('EXTENSION: running decorated execute_cells function');
});
}
return {
load_ipython_extension: load_ipython_extension
};
});