11# Copyright Modal Labs 2022
22import pytest
3+ import time
34from pathlib import Path
45
56
@@ -9,7 +10,7 @@ def notebook_runner(servicer, credentials):
910 import jupytext
1011 import nbformat
1112 from nbclient import NotebookClient
12- from nbclient .exceptions import CellExecutionError
13+ from nbclient .exceptions import CellExecutionError , DeadKernelError
1314
1415 def runner (notebook_path : Path ):
1516 output_notebook_path = notebook_path .with_suffix (".output.ipynb" )
@@ -25,18 +26,29 @@ def runner(notebook_path: Path):
2526token_secret = "{ credentials [1 ]} "
2627'''
2728
28- client = NotebookClient (nb )
29-
30- try :
31- client .execute ()
32- except CellExecutionError :
33- nbformat .write (nb , output_notebook_path )
34- pytest .fail (
35- f"""There was an error when executing the notebook.
29+ # Only transient kernel-startup failures are retried; cell failures
30+ # are real test failures. The worst-case budget (attempts * timeout
31+ # + sleep) must stay under the 300s per-test pytest timeout.
32+ startup_attempts = 2
33+ for attempt in range (startup_attempts ):
34+ client = NotebookClient (nb , startup_timeout = 120 )
35+ try :
36+ client .execute ()
37+ break
38+ except CellExecutionError :
39+ nbformat .write (nb , output_notebook_path )
40+ pytest .fail (
41+ f"""There was an error when executing the notebook.
3642
3743Inspect the output notebook: { output_notebook_path }
3844"""
39- )
45+ )
46+ except DeadKernelError :
47+ raise
48+ except (RuntimeError , ValueError ):
49+ if attempt == startup_attempts - 1 :
50+ raise
51+ time .sleep (5 )
4052 tagged_cells = {}
4153 for cell in nb ["cells" ]:
4254 for tag in cell ["metadata" ].get ("tags" , []):
0 commit comments