Skip to content

Commit 389f2af

Browse files
authored
Loosen cell ID regex to match nbformat spec (#136)
1 parent 90894f6 commit 389f2af

File tree

2 files changed

+16
-8
lines changed

2 files changed

+16
-8
lines changed

jupyter_server_documents/outputs/handlers.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,9 @@ async def get(self, file_id=None, cell_id=None):
6767
# URL to handler mappings
6868
# -----------------------------------------------------------------------------
6969

70-
# Strict UUID regex (matches standard 8-4-4-4-12 UUIDs)
71-
_uuid_regex = r"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"
72-
73-
_file_id_regex = rf"(?P<file_id>{_uuid_regex})"
74-
_cell_id_regex = rf"(?P<cell_id>{_uuid_regex})"
70+
_file_id_regex = r"(?P<file_id>[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})"
71+
# In nbformat, cell_ids follow this format, compatible with uuid4
72+
_cell_id_regex = rf"(?P<cell_id>[a-zA-Z0-9_-]+)"
7573

7674
# non-negative integers
7775
_output_index_regex = r"(?P<output_index>0|[1-9]\d*)"
@@ -80,4 +78,3 @@ async def get(self, file_id=None, cell_id=None):
8078
(rf"/api/outputs/{_file_id_regex}/{_cell_id_regex}(?:/{_output_index_regex}.output)?", OutputsAPIHandler),
8179
(rf"/api/outputs/{_file_id_regex}/{_cell_id_regex}/stream", StreamAPIHandler),
8280
]
83-

jupyter_server_documents/outputs/manager.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ def process_loaded_notebook(self, file_id: str, file_data: dict) -> dict:
207207
# Notebook content is a tree of nbformat.NotebookNode objects,
208208
# which are a subclass of dict.
209209
nb = file_data['content']
210+
# We need cell ids which are only in nbformat >4.5. We use this to
211+
# upgrade all notebooks to 4.5 or later
212+
nb = nbformat.v4.upgrade(nb, from_version=nb.nbformat, from_minor=nb.nbformat_minor)
210213

211214
# Check if the notebook metadata has placeholder_outputs set to True
212215
if nb.get('metadata', {}).get('placeholder_outputs') is True:
@@ -233,8 +236,12 @@ def _process_loaded_placeholders(self, file_id: str, nb: dict) -> dict:
233236
dict: The notebook with placeholder outputs loaded from disk
234237
"""
235238
for cell in nb.get('cells', []):
239+
# Ensure all cells have IDs regardless of type
240+
if not cell.get('id'):
241+
cell['id'] = str(uuid.uuid4())
242+
236243
if cell.get('cell_type') == 'code':
237-
cell_id = cell.get('id', str(uuid.uuid4()))
244+
cell_id = cell['id']
238245
try:
239246
# Try to get outputs from disk
240247
output_strings = self.get_outputs(file_id=file_id, cell_id=cell_id)
@@ -268,10 +275,14 @@ def _process_loaded_no_placeholders(self, file_id: str, nb: dict) -> dict:
268275
dict: The notebook with outputs saved to disk and replaced with placeholders
269276
"""
270277
for cell in nb.get('cells', []):
278+
# Ensure all cells have IDs regardless of type
279+
if not cell.get('id'):
280+
cell['id'] = str(uuid.uuid4())
281+
271282
if cell.get('cell_type') != 'code' or 'outputs' not in cell:
272283
continue
273284

274-
cell_id = cell.get('id', str(uuid.uuid4()))
285+
cell_id = cell['id']
275286
processed_outputs = []
276287
for output in cell.get('outputs', []):
277288
display_id = output.get('metadata', {}).get('display_id')

0 commit comments

Comments
 (0)