|
109 | 109 | " verbose=False, # stream stdout/stderr from cells to console?\n", |
110 | 110 | " save=False, # write outputs back to notebook on success?\n", |
111 | 111 | " profile:bool=None, # load the IPython profile, as `ipykernel` does? (default: `exec_profile` config key)\n", |
112 | | - " cell_timeout:int=600 # seconds before each cell times out (None: no limit)\n", |
| 112 | + " cell_timeout:int=600, # seconds before each cell times out (None: no limit)\n", |
| 113 | + " cell_timing_min:float=None # print cells slower than this many seconds (None: no timing output)\n", |
113 | 114 | "):\n", |
114 | 115 | " \"Execute tests in notebook in `fn` except those with `skip_flags`\"\n", |
115 | 116 | " if not IN_NOTEBOOK and threading.current_thread() is threading.main_thread(): signal.signal(signal.SIGINT, _int_handler)\n", |
|
130 | 131 | " if not does_cell_eval(cell, dflt): return True\n", |
131 | 132 | " return flags & (getattr(cell, 'directives_', {}) or {}).keys()\n", |
132 | 133 | "\n", |
| 134 | + " def _postproc(cell):\n", |
| 135 | + " elapsed = cell.metadata['execution']['total']\n", |
| 136 | + " if cell_timing_min is not None and elapsed > cell_timing_min: print(f'{fn.name}:{cell.id}: {elapsed:.3f}s')\n", |
| 137 | + "\n", |
133 | 138 | " start = time.time()\n", |
134 | 139 | " if profile is None: profile = bool(get_config(fn.parent).exec_profile)\n", |
135 | 140 | " k = CaptureShell(fn, profile=profile)\n", |
136 | 141 | " _cur_shell[0] = k\n", |
137 | 142 | " if do_print: print(f'Starting {fn}')\n", |
138 | 143 | " try:\n", |
139 | 144 | " with working_directory(fn.parent):\n", |
140 | | - " k.run_all(nb, exc_stop=True, preproc=_no_eval, verbose=verbose, cell_timeout=cell_timeout)\n", |
| 145 | + " k.run_all(nb, exc_stop=True, preproc=_no_eval, postproc=_postproc, verbose=verbose, cell_timeout=cell_timeout)\n", |
141 | 146 | " if save: write_nb(nb, fn)\n", |
142 | 147 | " res = True\n", |
143 | 148 | " except: \n", |
|
211 | 216 | "from fastcore.xtras import modified_env" |
212 | 217 | ] |
213 | 218 | }, |
| 219 | + { |
| 220 | + "cell_type": "markdown", |
| 221 | + "id": "bae187da", |
| 222 | + "metadata": {}, |
| 223 | + "source": [ |
| 224 | + "Pass `cell_timing_min` to identify slow cells without flooding ordinary test output. Each cell over the threshold prints its notebook name, stable cell id, and elapsed seconds; `--cell-timing-min=0.1` is a useful first pass." |
| 225 | + ] |
| 226 | + }, |
214 | 227 | { |
215 | 228 | "cell_type": "markdown", |
216 | 229 | "id": "e3e69d17", |
|
283 | 296 | " verbose:bool=False, # Print stdout/stderr from notebook cells?\n", |
284 | 297 | " save:bool=False, # Write outputs back to notebooks on success?\n", |
285 | 298 | " cell_timeout:int=600, # Seconds before each cell times out (0: no limit)\n", |
| 299 | + " cell_timing_min:float=None, # Print cells slower than this many seconds (None: no timing output)\n", |
286 | 300 | " **kwargs\n", |
287 | 301 | "):\n", |
288 | 302 | " \"Test in parallel notebooks matching `path`, passing along `flags`\"\n", |
|
302 | 316 | " try:\n", |
303 | 317 | " results = parallel(test_nb, files, skip_flags=skip_flags, force_flags=force_flags, n_workers=n_workers,\n", |
304 | 318 | " basepath=cfg.config_path, pause=pause, do_print=do_print, verbose=verbose, save=save,\n", |
305 | | - " cell_timeout=cell_timeout or None, **kw)\n", |
| 319 | + " cell_timeout=cell_timeout or None, cell_timing_min=cell_timing_min, **kw)\n", |
306 | 320 | " except KeyboardInterrupt:\n", |
307 | 321 | " sys.stderr.write('\\nnbdev-test interrupted; in-flight notebook stacks shown above\\n')\n", |
308 | 322 | " sys.exit(130)\n", |
|
0 commit comments