diff --git a/nbdev/test.py b/nbdev/test.py index 8ba0ec06d..2cdbe9b30 100644 --- a/nbdev/test.py +++ b/nbdev/test.py @@ -48,12 +48,11 @@ async def test_nb( fm = nb_frontmatter(nb) if str2bool(fm.get('skip_exec', False)) or nb_lang(nb) != 'python': return True, 0 + dflt = fm_default_eval(fm) def _no_eval(cell): if cell.cell_type != 'code': return True - if 'nbdev_export'+'(' in cell.source: return True - direc = getattr(cell, 'directives_', {}) or {} - if direc.get('eval', '').lower() == 'false': return True - return flags & direc.keys() + if not does_cell_eval(cell, dflt): return True + return flags & (getattr(cell, 'directives_', {}) or {}).keys() start = time.time() if profile is None: profile = bool(get_config(fn.parent).exec_profile) diff --git a/nbs/api/12_test.ipynb b/nbs/api/12_test.ipynb index e0c5cfe35..357d3b9b1 100644 --- a/nbs/api/12_test.ipynb +++ b/nbs/api/12_test.ipynb @@ -76,12 +76,11 @@ " fm = nb_frontmatter(nb)\n", " if str2bool(fm.get('skip_exec', False)) or nb_lang(nb) != 'python': return True, 0\n", "\n", + " dflt = fm_default_eval(fm)\n", " def _no_eval(cell):\n", " if cell.cell_type != 'code': return True\n", - " if 'nbdev_export'+'(' in cell.source: return True\n", - " direc = getattr(cell, 'directives_', {}) or {}\n", - " if direc.get('eval', '').lower() == 'false': return True\n", - " return flags & direc.keys()\n", + " if not does_cell_eval(cell, dflt): return True\n", + " return flags & (getattr(cell, 'directives_', {}) or {}).keys()\n", "\n", " start = time.time()\n", " if profile is None: profile = bool(get_config(fn.parent).exec_profile)\n", @@ -314,7 +313,31 @@ "id": "8ee3f4db", "metadata": {}, "source": [ - "## Eval -" + "## Eval" + ] + }, + { + "cell_type": "markdown", + "id": "0317b83e", + "metadata": {}, + "source": [ + "`test_nb` decides which cells run through the `eval` cascade (`fastcore.nbio.does_cell_eval`): a cell's own `#| eval:` directive wins; otherwise the notebook-level `eval` directive — in frontmatter, or the notebook's `metadata.nbdev` mapping — sets the default; with neither, cells run. `#| eval: false` therefore skips one cell, as it always has, while a notebook-level `eval: false` flips the whole notebook to opt-in: only cells marked `#| eval: true` run, which suits a slow or service-dependent notebook where just a few cells are worth testing. Unlike `skip_exec: true`, which skips a notebook unconditionally, marked cells still run — here the unmarked cell would raise if executed, so the passing test is the proof it was skipped:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c358146", + "metadata": {}, + "outputs": [], + "source": [ + "with tempfile.TemporaryDirectory() as td:\n", + " cells = [mk_cell('---\\neval: false\\n---', 'raw'), mk_cell('raise Exception(\"unmarked: must not run\")'),\n", + " mk_cell('#| eval: true\\nx = 1')]\n", + " fn = Path(td)/'optin.ipynb'\n", + " write_nb(new_nb(cells), fn)\n", + " success,_ = await test_nb(fn)\n", + "assert success" ] }, { diff --git a/pyproject.toml b/pyproject.toml index 331ef3ecd..730df1c54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ classifiers = [ "License :: OSI Approved :: Apache Software License", ] dynamic = ["version"] -dependencies = [ "fastcore>=2.2.3", "execnb>=0.2.11", "astunparse", "ghapi>=2.0.2", "watchdog", "asttokens", +dependencies = [ "fastcore>=2.2.7", "execnb>=0.2.11", "astunparse", "ghapi>=2.0.2", "watchdog", "asttokens", "setuptools", "build", "fastgit>=0.0.7", "pyyaml", "tomli; python_version < '3.11'", ] [project.optional-dependencies]