We would like to add some cells programmatically. There seem to be some limitations given by the design of Jupyter; basically the file on disk, the kernel, and the frontend are different things. The following “payload” approach seems to be the only way to create a new cell without writing extensions for different frontends. However, this approach seems to be at risk of deprecation.
def add_cells( contents:list[str], # The contents to be placed in the new cells.):
Adds several cells below the calling cells once excution of the current cell finishes
add_cells(["# Hi", "# there"])
# Hi
# there
add_cells([]) # This should not do anything
The calls to the payload manager are scheduled and executed in order after the cell execution finishes.
add_cell("# first")add_cell("# second")
# second
# first
This means if you don’t reverse the desired output order, cells will be in the wrong order. The last cell needs to be added (scheduled) first. This makes streaming impossible - you need to know the end first. You can only post-process fully received output.
Let’s post-process markdown strings - add one cell per header (up to a certain level). This is helpful to split up long texts in small logical chunks, e.g., when importing a paper to Jupyter.
Let’s assign a long_string to demonstrate a use case:
long_string ="""# Cell 1This is the content for the first cell.## Section 2If `max_level` is larger than 1, this section will be its own cell.# Final cellA last top-level cell with `# some comment`."""
If max_level is larger than 1, this section will be its own cell.
Final cell
A last top-level cell with # some comment.
The resulting cells are unfortunately code cells - you need to go manually through them to convert them to markdown (m) and execute (Shift + Enter, execute and go to next cell). I currently don’t have a good idea to make this automatic because as I understand this would only be possible from the frontend and therefore would require specialized code for each different frontend.