JupyterLab 4 - iterating over all cells in a notebook

With the general availability now of Jupyterlab v4, I have been trying to migrate various extensions over from 3.x.

One of the extensions used cell metadata tags to help custom style cells in a notebook. In JuoyterLab 3.x, the extension listened out for a fully rendered notebook using:

notebook.fullyRendered.connect((notebook) => {}

and then iterated over the cells using:

for (const cell of notebook.widgets) {}

In JupyterLab 4, how do I identify notebooks that have been opened and then iterate over all the cells in them (I need to be able to access the cell metadata, and set class attributes on the cell node)?

I can set state / node class attributes on selected cells in a notebook from a toolbar button that accesses a notebookTracker: INotebookTracker object and listens out for the current notebook using notebookTracker.currentChanged.connect((tracker, panel) => {}. It then grabs a notebook/cell reference as notebookTracker.activeCell .

But I can’t seem to find a way of catching references to opened notebooks and then iterating through all the cells in them?

Any hints?

–tony

Hello Tony,

I am currently working on a project doing something similar where I iterate over the entire notebooks cells to check for a certain key on the cells metadata.

Firstly I am importing LabShell and NotebookPanel
Then inside my plugin I do:

const labShell = app.shell as LabShell;
labShell.currentChanged.connect(() => {
  const notebook = app.shell.currentWidget as NotebookPanel;
  const cellList = notebook.content.model.cells;

  for (const cell of cellList) {
    console.log('METADATA: ', cell.metadata)
  }
});

The LabShell part is to detect when users switch between notebooks or open a new notebook.

Hopefully this is enough to get you started. If you have any questions please let me know.

– Luke

Ah, thanks Luke, will give that a go…

–tony

I’m not sure what I’m doing wrong - however I try to find a list of cells, I only seem to get one cell reported in the console log, (cell list size of 1). And from the cell metadata, it doesn’t look like the cell is being read from the notebook I’m opening.

I am quite possibly doing something very wrong in the simplistic and ignorant way I have tried to build the extension (cribbing from jupyterlab extensions examples). I don’t really understand any of the code — I’m just trying to plug code fragments together like Lego bricks…!

Messy WIP here.

–tony

It sounds like there might be some async issues.

Try doing notebook.revealed:

labShell.currentChanged.connect(() => {
      const notebook = app.shell.currentWidget as NotebookPanel;
      
      if (notebook) {
          notebook.revealed.then(() => {
              const cellList = notebook.content.model.cells;
              let i=1;
              for (const cell of cellList) {
                  console.log("a cell of type", cell.type, i)
                  i=i+1;
                  console.log('METADATA: ', cell.metadata)
              }
          });
      }
    });

I am not sure if this is the optimal way to do it but it might help.
Disclaimer: I am still pretty new to the jupyter extension development environment.

That seemed to work, thanks:-)

I just needed to move to notebook.content.widgets?.forEach(cell=>{}) so I could access both metadata and the cell.node (for modifying HTML class attributes) and from a quick informal test it seems to work…

Thanks again…

–tony

PS notes here Updating the Empinken Extension from JupyterLab 3.x to 4.x – OUseful.Info, the blog…

1 Like