From cdaa1fe0a2830b3eaa815cd0f37f08906a378fdd Mon Sep 17 00:00:00 2001 From: Jeremy Howard Date: Mon, 21 Sep 2026 20:30:52 +1000 Subject: [PATCH] Name the exception in `exec_show_docs` errors, and leave symbol-less modules out of `_modidx.py` --- nbdev/_modidx.py | 6 ------ nbdev/doclinks.py | 2 +- nbdev/processors.py | 4 ++-- nbs/api/05_doclinks.ipynb | 28 +++++++++++++++++++++++++++- nbs/api/10_processors.ipynb | 23 +++++++++++++++++++++-- nbs/tutorials/migrating.ipynb | 7 +++---- 6 files changed, 54 insertions(+), 16 deletions(-) diff --git a/nbdev/_modidx.py b/nbdev/_modidx.py index 1b96027b2..30c90bfa4 100644 --- a/nbdev/_modidx.py +++ b/nbdev/_modidx.py @@ -137,7 +137,6 @@ 'nbdev.export.ExportModuleProc.begin': ('api/export.html#exportmoduleproc.begin', 'nbdev/export.py'), 'nbdev.export.nb_export': ('api/export.html#nb_export', 'nbdev/export.py'), 'nbdev.export.nb_mdoc': ('api/export.html#nb_mdoc', 'nbdev/export.py')}, - 'nbdev.extract_attachments': {}, 'nbdev.frontmatter': { 'nbdev.frontmatter.FrontmatterProc': ('api/frontmatter.html#frontmatterproc', 'nbdev/frontmatter.py'), 'nbdev.frontmatter.FrontmatterProc._update': ( 'api/frontmatter.html#frontmatterproc._update', 'nbdev/frontmatter.py'), @@ -150,7 +149,6 @@ 'nbdev.frontmatter._dict2fm': ('api/frontmatter.html#_dict2fm', 'nbdev/frontmatter.py'), 'nbdev.frontmatter._fm2dict_md': ('api/frontmatter.html#_fm2dict_md', 'nbdev/frontmatter.py'), 'nbdev.frontmatter._insertfm': ('api/frontmatter.html#_insertfm', 'nbdev/frontmatter.py')}, - 'nbdev.imports': {}, 'nbdev.maker': { 'nbdev.maker.ModuleMaker': ('api/maker.html#modulemaker', 'nbdev/maker.py'), 'nbdev.maker.ModuleMaker.__init__': ('api/maker.html#modulemaker.__init__', 'nbdev/maker.py'), 'nbdev.maker.ModuleMaker._last_future': ('api/maker.html#modulemaker._last_future', 'nbdev/maker.py'), @@ -215,7 +213,6 @@ 'nbdev.migrate.migrate_nb': ('api/migrate.html#migrate_nb', 'nbdev/migrate.py'), 'nbdev.migrate.nbdev_migrate': ('api/migrate.html#nbdev_migrate', 'nbdev/migrate.py'), 'nbdev.migrate.nbdev_migrate_config': ('api/migrate.html#nbdev_migrate_config', 'nbdev/migrate.py')}, - 'nbdev.moddocs': {}, 'nbdev.process': { 'nbdev.process.NBProcessor': ('api/process.html#nbprocessor', 'nbdev/process.py'), 'nbdev.process.NBProcessor.__init__': ('api/process.html#nbprocessor.__init__', 'nbdev/process.py'), 'nbdev.process.NBProcessor._proc': ('api/process.html#nbprocessor._proc', 'nbdev/process.py'), @@ -364,12 +361,10 @@ 'nbdev.release.update_changelog': ('api/release.html#update_changelog', 'nbdev/release.py'), 'nbdev.release.write_conda_meta': ('api/release.html#write_conda_meta', 'nbdev/release.py'), 'nbdev.release.write_requirements': ('api/release.html#write_requirements', 'nbdev/release.py')}, - 'nbdev.scrubmagics': {}, 'nbdev.serve': { 'nbdev.serve._is_qpy': ('api/serve.html#_is_qpy', 'nbdev/serve.py'), 'nbdev.serve._keep_file': ('api/serve.html#_keep_file', 'nbdev/serve.py'), 'nbdev.serve._proc_file': ('api/serve.html#_proc_file', 'nbdev/serve.py'), 'nbdev.serve.proc_nbs': ('api/serve.html#proc_nbs', 'nbdev/serve.py')}, - 'nbdev.serve_drv': {}, 'nbdev.showdoc': { 'nbdev.showdoc.BasicMarkdownRenderer': ('api/showdoc.html#basicmarkdownrenderer', 'nbdev/showdoc.py'), 'nbdev.showdoc.BasicMarkdownRenderer._repr_markdown_': ( 'api/showdoc.html#basicmarkdownrenderer._repr_markdown_', 'nbdev/showdoc.py'), @@ -378,7 +373,6 @@ 'nbdev.showdoc.doc': ('api/showdoc.html#doc', 'nbdev/showdoc.py'), 'nbdev.showdoc.show_doc': ('api/showdoc.html#show_doc', 'nbdev/showdoc.py'), 'nbdev.showdoc.showdoc_nm': ('api/showdoc.html#showdoc_nm', 'nbdev/showdoc.py')}, - 'nbdev.skill': {}, 'nbdev.sync': { 'nbdev.sync._mod_files': ('api/sync.html#_mod_files', 'nbdev/sync.py'), 'nbdev.sync._to_absolute': ('api/sync.html#_to_absolute', 'nbdev/sync.py'), 'nbdev.sync._update_mod': ('api/sync.html#_update_mod', 'nbdev/sync.py'), diff --git a/nbdev/doclinks.py b/nbdev/doclinks.py index 815e7241f..9f49f55fa 100644 --- a/nbdev/doclinks.py +++ b/nbdev/doclinks.py @@ -92,7 +92,7 @@ def _stor(nm): if isinstance(tree, ast.ClassDef): for t2 in tree.body: if isinstance(t2, _def_types): _stor(f'{tree.name}.{t2.name}') - return {mod_name: d} + return {mod_name: d} if d else {} # %% ../nbs/api/05_doclinks.ipynb #587df0d4 def _build_modidx(dest=None, nbs_path=None, skip_exists=False): diff --git a/nbdev/processors.py b/nbdev/processors.py index c1d92bd77..222e8564f 100644 --- a/nbdev/processors.py +++ b/nbdev/processors.py @@ -257,8 +257,8 @@ def __call__(self, cell): if str2bool(fm.get('skip_showdoc', False)): return if _do_eval(cell): self.k.cell(cell) title = fm.get('title', '') - if self.k.exc: - raise Exception(f"Error{' in notebook: '+title if title else ''} in cell {cell.idx_} :\n{cell.source}") from self.k.exc + if (e := self.k.exc): + raise Exception(f"Error{' in notebook: '+title if title else ''} in cell {cell.idx_} :\n{cell.source}\n{type(e).__name__}: {e}") from e def end(self): try: from ipywidgets import Widget diff --git a/nbs/api/05_doclinks.ipynb b/nbs/api/05_doclinks.ipynb index ed143fb19..fa950e2e2 100644 --- a/nbs/api/05_doclinks.ipynb +++ b/nbs/api/05_doclinks.ipynb @@ -210,7 +210,7 @@ " if isinstance(tree, ast.ClassDef):\n", " for t2 in tree.body:\n", " if isinstance(t2, _def_types): _stor(f'{tree.name}.{t2.name}')\n", - " return {mod_name: d}" + " return {mod_name: d} if d else {}" ] }, { @@ -224,6 +224,32 @@ "# _get_modidx(get_config().lib_path/'sync.py', get_config().lib_path, get_config().nbs_path)" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "325f22b8", + "metadata": {}, + "outputs": [], + "source": [ + "#| hide\n", + "import tempfile" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fbbdbd9d", + "metadata": {}, + "outputs": [], + "source": [ + "#| hide\n", + "# A data file with no symbols gets no entry. The index is then the same whether or not a generated file exists.\n", + "with tempfile.TemporaryDirectory() as d:\n", + " d = Path(d).resolve()\n", + " (d/'data.py').write_text(\"{'a': 1}\")\n", + " test_eq(_get_modidx(d/'data.py', d, d), {})" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/nbs/api/10_processors.ipynb b/nbs/api/10_processors.ipynb index 8b25f5141..d79d3dd38 100644 --- a/nbs/api/10_processors.ipynb +++ b/nbs/api/10_processors.ipynb @@ -765,8 +765,8 @@ " if str2bool(fm.get('skip_showdoc', False)): return\n", " if _do_eval(cell): self.k.cell(cell)\n", " title = fm.get('title', '')\n", - " if self.k.exc: \n", - " raise Exception(f\"Error{' in notebook: '+title if title else ''} in cell {cell.idx_} :\\n{cell.source}\") from self.k.exc\n", + " if (e := self.k.exc):\n", + " raise Exception(f\"Error{' in notebook: '+title if title else ''} in cell {cell.idx_} :\\n{cell.source}\\n{type(e).__name__}: {e}\") from e\n", "\n", " def end(self):\n", " try: from ipywidgets import Widget\n", @@ -798,6 +798,25 @@ "assert res" ] }, + { + "cell_type": "markdown", + "id": "b1a63b85", + "metadata": {}, + "source": [ + "When a cell fails, the error names the exception as well as the cell. The last lines of a docs build log then state the cause." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93734aba", + "metadata": {}, + "outputs": [], + "source": [ + "nb = dict2nb(dict(cells=[mk_cell('#| export\\ndef f(): pass'), mk_cell('import not_a_real_module')], metadata={}, nbformat=4, nbformat_minor=5))\n", + "with ExceptionExpected(regex='ModuleNotFoundError'): NBProcessor(nb=nb, procs=[add_show_docs, exec_show_docs]).process()" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/nbs/tutorials/migrating.ipynb b/nbs/tutorials/migrating.ipynb index e34a5802a..9f049e10a 100644 --- a/nbs/tutorials/migrating.ipynb +++ b/nbs/tutorials/migrating.ipynb @@ -148,8 +148,7 @@ "rm -f .git/hooks/post-merge\n", "\n", "rm -f setup.py\n", - "curl -O https://raw.githubusercontent.com/fastai/nbdev3-template/master/styles.css\n", - "curl -O https://raw.githubusercontent.com/fastai/nbdev3-template/master/setup.py\n", + "curl -O https://raw.githubusercontent.com/AnswerDotAI/nbdev3-template/master/styles.css\n", "\n", "cat >>.gitignore <