Skip to content

Commit 3159178

Browse files
committed
fixes #1637
1 parent 44a6bdf commit 3159178

4 files changed

Lines changed: 28 additions & 8 deletions

File tree

‎nbdev/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
- `nbdev.extract_attachments`: A preprocessor that extracts all of the attachments from the notebook file. The extracted attachments are returned in the 'resources' dictionary.
66
- `nbdev.moddocs`: # Module docs: creating them from existing notebooks
7-
- `nbdev.skill`: Author clear, executable nbdev notebooks where code, prose, examples, outputs, and tests form one coherent narrative."""
7+
- `nbdev.skill`: Author clear, executable nbdev notebooks where code, prose, examples, outputs, and tests form one coherent narrative. Trigger: ALWAYS read this before ANY notebook edit."""
88

99
__version__ = "3.3.13"
1010

‎nbdev/test.py‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ def test_nb(
6565
verbose=False, # stream stdout/stderr from cells to console?
6666
save=False, # write outputs back to notebook on success?
6767
profile:bool=None, # load the IPython profile, as `ipykernel` does? (default: `exec_profile` config key)
68-
cell_timeout:int=600 # seconds before each cell times out (None: no limit)
68+
cell_timeout:int=600, # seconds before each cell times out (None: no limit)
69+
cell_timing_min:float=None # print cells slower than this many seconds (None: no timing output)
6970
):
7071
"Execute tests in notebook in `fn` except those with `skip_flags`"
7172
if not IN_NOTEBOOK and threading.current_thread() is threading.main_thread(): signal.signal(signal.SIGINT, _int_handler)
@@ -86,14 +87,18 @@ def _no_eval(cell):
8687
if not does_cell_eval(cell, dflt): return True
8788
return flags & (getattr(cell, 'directives_', {}) or {}).keys()
8889

90+
def _postproc(cell):
91+
elapsed = cell.metadata['execution']['total']
92+
if cell_timing_min is not None and elapsed > cell_timing_min: print(f'{fn.name}:{cell.id}: {elapsed:.3f}s')
93+
8994
start = time.time()
9095
if profile is None: profile = bool(get_config(fn.parent).exec_profile)
9196
k = CaptureShell(fn, profile=profile)
9297
_cur_shell[0] = k
9398
if do_print: print(f'Starting {fn}')
9499
try:
95100
with working_directory(fn.parent):
96-
k.run_all(nb, exc_stop=True, preproc=_no_eval, verbose=verbose, cell_timeout=cell_timeout)
101+
k.run_all(nb, exc_stop=True, preproc=_no_eval, postproc=_postproc, verbose=verbose, cell_timeout=cell_timeout)
97102
if save: write_nb(nb, fn)
98103
res = True
99104
except:
@@ -131,6 +136,7 @@ def nbdev_test(
131136
verbose:bool=False, # Print stdout/stderr from notebook cells?
132137
save:bool=False, # Write outputs back to notebooks on success?
133138
cell_timeout:int=600, # Seconds before each cell times out (0: no limit)
139+
cell_timing_min:float=None, # Print cells slower than this many seconds (None: no timing output)
134140
**kwargs
135141
):
136142
"Test in parallel notebooks matching `path`, passing along `flags`"
@@ -150,7 +156,7 @@ def nbdev_test(
150156
try:
151157
results = parallel(test_nb, files, skip_flags=skip_flags, force_flags=force_flags, n_workers=n_workers,
152158
basepath=cfg.config_path, pause=pause, do_print=do_print, verbose=verbose, save=save,
153-
cell_timeout=cell_timeout or None, **kw)
159+
cell_timeout=cell_timeout or None, cell_timing_min=cell_timing_min, **kw)
154160
except KeyboardInterrupt:
155161
sys.stderr.write('\nnbdev-test interrupted; in-flight notebook stacks shown above\n')
156162
sys.exit(130)

‎nbs/api/12_test.ipynb‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@
109109
" verbose=False, # stream stdout/stderr from cells to console?\n",
110110
" save=False, # write outputs back to notebook on success?\n",
111111
" 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",
113114
"):\n",
114115
" \"Execute tests in notebook in `fn` except those with `skip_flags`\"\n",
115116
" if not IN_NOTEBOOK and threading.current_thread() is threading.main_thread(): signal.signal(signal.SIGINT, _int_handler)\n",
@@ -130,14 +131,18 @@
130131
" if not does_cell_eval(cell, dflt): return True\n",
131132
" return flags & (getattr(cell, 'directives_', {}) or {}).keys()\n",
132133
"\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",
133138
" start = time.time()\n",
134139
" if profile is None: profile = bool(get_config(fn.parent).exec_profile)\n",
135140
" k = CaptureShell(fn, profile=profile)\n",
136141
" _cur_shell[0] = k\n",
137142
" if do_print: print(f'Starting {fn}')\n",
138143
" try:\n",
139144
" 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",
141146
" if save: write_nb(nb, fn)\n",
142147
" res = True\n",
143148
" except: \n",
@@ -211,6 +216,14 @@
211216
"from fastcore.xtras import modified_env"
212217
]
213218
},
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+
},
214227
{
215228
"cell_type": "markdown",
216229
"id": "e3e69d17",
@@ -283,6 +296,7 @@
283296
" verbose:bool=False, # Print stdout/stderr from notebook cells?\n",
284297
" save:bool=False, # Write outputs back to notebooks on success?\n",
285298
" 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",
286300
" **kwargs\n",
287301
"):\n",
288302
" \"Test in parallel notebooks matching `path`, passing along `flags`\"\n",
@@ -302,7 +316,7 @@
302316
" try:\n",
303317
" results = parallel(test_nb, files, skip_flags=skip_flags, force_flags=force_flags, n_workers=n_workers,\n",
304318
" 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",
306320
" except KeyboardInterrupt:\n",
307321
" sys.stderr.write('\\nnbdev-test interrupted; in-flight notebook stacks shown above\\n')\n",
308322
" sys.exit(130)\n",

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ classifiers = [
1919
"License :: OSI Approved :: Apache Software License",
2020
]
2121
dynamic = ["version"]
22-
dependencies = [ "fastcore>=2.2.7", "execnb>=0.3.2", "astunparse", "ghapi>=2.0.2", "watchdog", "asttokens",
22+
dependencies = [ "fastcore>=2.2.7", "execnb>=0.3.3", "astunparse", "ghapi>=2.0.2", "watchdog", "asttokens",
2323
"setuptools", "build", "fastgit>=0.0.7", "pyyaml", "tomli; python_version < '3.11'", ]
2424

2525
[project.optional-dependencies]

0 commit comments

Comments
 (0)