How to get output model for a given cell in a JupyterLab extension?

To start with, I am not convinced that trying to obtain this information from JupyterLab frontend is the prime way to approach the problem, but let’s entertain it for educational reasons.

First, you can get the output of the cell from its model and your attempts to use model.list were not too far off. You may have seen empty values because the operations are asynchronous, i.e. the first time when Output Area becomes visible it is not yet populated. This can be resolved by listening on changes to its content:

outputs-demo

Code:

{
  id: 'CellOutputExample',
  autoStart: true,
  requires: ["@jupyterlab/notebook:INotebookTracker"],
  activate: function(app, notebookTracker) {
      notebookTracker.widgetAdded.connect((tracker, panel) => {
          let notebook = panel.content;
          const notebookModel = notebook.model;
          notebookModel.cells.changed.connect((_, change) => {
              if (change.type != 'add') {
                  return;
              }
              for (const cellModel of change.newValues) {
                  // ensure we have CodeCellModel
                  if (cellModel.type != 'code') {
                      return;
                  }
                  // IOutputAreaModel
                  let outputs = cellModel.outputs;
                  if (!outputs) {
                      continue;
                  }
                  outputs.changed.connect(() => {
                      console.log('Outputs of the cell', cellModel.id, 'in', notebook.title.label, 'changed:');
                      console.log(
                          '\tThere are now', outputs.length, 'outputs:'
                      );
                      for (let i = 0; i < outputs.length; i++) {
                          // IOutputModel
                          const outputModel = outputs.get(i);
                          console.log('\t\t', outputModel.data);
                          // also has `outputModel.executionCount` and `outputModel.metadata`
                      }
                  });
              }
          });
      })
  }
}

(Note this is JavaScript for GitHub - jupyterlab/jupyterlab-plugin-playground: A dynamic extension loader for JupyterLab, not a proper TypeScript code, it’s Sunday, I’m not opening my IDE today).

Second, I would really avoid observing the results of execution by engaging the DOM or widgets at all; instead I would listen to the NotebookActions.executed which also enables you to handle errors in a clean way. I also highly recommend sticking to the public members (do not use underscored properties like ._output). Peeking with the debugger/console is a good approach, but it should be secondary to reading the API reference which let me quickly create the example above. I usually go back and forth between logging to console, API reference and - if needed - the actual source code of JupyterLab (and extensions). Also, a well-configured autocompletion can help a lot!

Third, there is a lot of prior work mentioned in the other DAG thread but one other extension not mentioned there, but probably very relevant to what you are trying to achieve (though this might not be immediately obvious) is GitHub - nbsafety-project/nbsafety: Fearless interactivity for Jupyter notebooks. as it tracks the execution of the cells, their results and modifies the display of the notebook on the frontend to guide users on out-of-order execution. I think that building upon their approach is one of the most promising ways to implement DAG notebooks (with equally GitHub - davidbrochart/akernel: Asynchronous, reactive Python Jupyter kernel. being another promising projects).

Finally, in another thread you hypothesise that getting the result of cell execution is difficult in JupyterLab and Maybe that is one of the reasons why other implementations of observable notebooks are based on extra kernels?. While I hope I demonstrated above that the premise is not entirely correct, I want to also point you to one of the previous experiments, GitHub - cphyc/ipyspaghetti which implements what I believe is the most functional DAG notebook for JupyterLab 3.x and it does not require an extra kernel. Maybe, just maybe, contributing to that project would be a better way forward rather than trying to re-implement it from scratch?

3 Likes