Jupyterlab Notebook APIs for accessing cell within a notebook

Hello Team,

Please help in finding Jupyterlab APIs for accessing Notebook cell and various operations over cell and at notebook level. Like creating cell in a notebook, writing code in the cell, running a cell and get output of a Cell etc.

If you haven’t already, you may want to work through some of the examples here: GitHub - jupyterlab/extension-examples: JupyterLab Extensions by Examples

This code shows a simple example where we are iterating over the cells in a notebook: etc-jupyterlab-cell-iterate-run/index.ts at cfa483b168c04ce3f10e0b8d0f7d1ced7074048b · educational-technology-collective/etc-jupyterlab-cell-iterate-run · GitHub

Once you have a reference to a cell, then you can obtain its model where you can get the content of the cell input and output.

If you need help, and if you can push your project to GitHub, please send me a link to the repository and I will try to help if I can.

Thanks for your reply Adpatter. Is there any way to get rest API to control notebook and cell level operations? I want to access jupyter lab notebook and cells using RET API call sfrom java Spring boot

I’m sorry. I was unable to understand what you are trying to achieve. If you would like to give a more detailed explanation, I might be able to help.

I need REST endpoints (via http verbs like GET, POST, PUT) for jupyter notebook creation, cell creation, code writing and updating with in the cell, running individual cell, fetching output of a cell etc.
Requirement: Rest endpoint url, request and response format for above functionality, to integrate jupyter as backend IDE.

I think I’m still not clear on what you are trying to achieve.

Sure adpatter.
I can be reached at mittaldivya01@gmail.com.
Can we schedule this in between IST 6am to 10pm.
Or let me know your convenient time.

Hi @mittaldivya02 , i am looking for a similar functionality as well, could you please share if you made any progress on this? I dont know of a way of initiating commands from server extensions (which are the REST handlers).

Hi everyone,
I have the same problem and the best solution that I found until now is:

But it does not edit the notebook file itself nor does it work for all notebooks, for example for functions the script breaks.
I have not found a method to execute the entire jupyter notebook at once.

For just editing a file the Jupyter Server API can be used:
https://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/jupyter_server/master/jupyter_server/services/api/api.yaml#/
The problem is that the entire document has do be downloaded first and after the changes uploaded again which is fine for infrequent changes and small files.

This is the code that I have so far:

import json
import requests
import datetime
import uuid
from websocket import create_connection

# -- config --
notebook_name = 'notebook1.ipynb'
token = "secret"
host = "localhost:5000"
# -- config end --

# Define the headers
headers = {
    'Authorization': f'Token {token}',
    'Content-Type': 'application/json'
}

# ---- create new notebook ----
data = {
    "type": "notebook"
}

response = requests.post(f"http://{host}/api/contents", headers=headers, data=json.dumps(data))

# Check the response
if response.status_code == 201:
    print('Notebook created successfully')
else:
    print('Failed to create notebook')
    exit(1)

previous_file_name = response.json()["path"]

# ---- rename the file ----
filename = {
    "path": f"/{notebook_name}"
}
response = requests.patch(f"http://{host}/api/contents/"+previous_file_name, headers=headers, data=json.dumps(filename))

if response.status_code == 200:
    print('Notebook renamed successfully')
else:
    print('Failed to rename notebook')
    exit(1)

# ---- edit file ----

# get file content
response = requests.get(f"http://{host}/api/contents/{notebook_name}", headers=headers, data=json.dumps(filename))
file = response.json()
code_cell_1 = {
    'cell_type': 'code',
    'metadata': {},
    'execution_count': None,
    'source': "print('Hello World')",
    'outputs': []
}

code_cell_2 = {
    'cell_type': 'code',
    'metadata': {},
    'execution_count': None,
    'source': "1+2*3",
    'outputs': []
}

code_cell_3 = {
    'cell_type': 'code',
    'metadata': {},
    'execution_count': None,
    'source': "print(1+2*3)",
    'outputs': []
}

code_cell_4 = {
    'cell_type': 'code',
    'metadata': {},
    'execution_count': None,
    'source': "a = 1",
    'outputs': []
}

code_cell_5= {
    'cell_type': 'code',
    'metadata': {},
    'execution_count': None,
    'source': "b = a+1\nprint(b)",
    'outputs': []
}
# add code cells
file['content']['cells'].append(code_cell_1)
file['content']['cells'].append(code_cell_2)
file['content']['cells'].append(code_cell_3)
# file['content']['cells'].append(code_cell_4)
# file['content']['cells'].append(code_cell_5)

# update the file
file_url = f"http://{host}/api/contents/{notebook_name}"

response = requests.put(file_url, headers=headers, data=json.dumps(file))
if response.status_code == 200:
    print('Notebook saved successfully')
else:
    print('Failed to save notebook')
    exit(1)

# ---- run code cells ----
url = f"http://{host}/api/kernels"
response = requests.post(url,headers=headers)
kernel = json.loads(response.text)

# Load the notebook and get the code of each cell
url = f"http://{host}/api/contents/{notebook_name}"
response = requests.get(url,headers=headers)
file = json.loads(response.text)
code = [ c['source'] for c in file['content']['cells'] if len(c['source'])>0 ]

# Execution request/reply is done on websockets channels
ws = create_connection(f"ws://{host}/api/kernels/" + kernel["id"] + "/channels", header=headers)

def send_execute_request(code):
    msg_type = 'execute_request';
    content = { 'code' : code, 'silent':False }
    hdr = { 'msg_id' : uuid.uuid1().hex,
        'username': 'test',
        'session': uuid.uuid1().hex,
        'data': datetime.datetime.now().isoformat(),
        'msg_type': msg_type,
        'version' : '5.0' }
    msg = { 'header': hdr, 'parent_header': hdr,
        'metadata': {},
        'content': content }
    return msg

for i, c in enumerate(code):
    ws.send(json.dumps(send_execute_request(c)))

    msg_type = '';
    while msg_type != "stream" and msg_type != "execute_result":
        rsp = json.loads(ws.recv())
        msg_type = rsp["msg_type"]

    result = {}
    if msg_type == "execute_result":
        result = {
            "data": rsp["content"]["data"],
            "output_type": "execute_result",
            "execution_count": rsp["content"]["execution_count"],
            "metadata": {}
        }
    else:
        result = {
            "name": rsp["content"]["name"],
            "output_type": "stream",
            "text": [rsp["content"]["text"]]
        }
    if file["content"]["cells"][i]["execution_count"] == None:
        file["content"]["cells"][i]["execution_count"] = 1
    else:
        file["content"]["cells"][i]["execution_count"] += 1
    file["content"]["cells"][i]["outputs"] = [result]

ws.close()
print("Notebook executed successfully")

# ---- save output ----
file_url = f"http://{host}/api/contents/{notebook_name}"

response = requests.put(file_url, headers=headers, data=json.dumps(file))
if response.status_code == 200:
    print('Notebook outputs saved successfully')
else:
    print('Failed to save notebook outputs')
    exit(1)

It kind of works for simple cases.
If all calculations are in the same cell it works, but if the next cell depends on the first cell the code breaks as it can be seen by uncommenting the cells 4 and 5.

If anyone has a better idea and/or solution for this problem please post it below.
Thanks.

2 Likes

The link is broken. It’s a struggle to figure out how the jlab code interfaces work with minimal examples and it changing every week