From 68aac697ddfd9f0cf09bafee1c9166de9b0e2cf7 Mon Sep 17 00:00:00 2001 From: Peter Butterfill Date: Wed, 16 Sep 2020 08:09:50 +0100 Subject: [PATCH 1/3] removed callbacks --- docs/sidebar.json | 3 +- nbdev/export2html.py | 6 - nbdev/imports.py | 15 +-- nbdev/test.py | 2 - nbs/03_export2html.ipynb | 74 +++-------- nbs/04_test.ipynb | 43 ------ nbs/nbdev_callbacks.ipynb | 271 -------------------------------------- 7 files changed, 21 insertions(+), 393 deletions(-) delete mode 100644 nbs/nbdev_callbacks.ipynb diff --git a/docs/sidebar.json b/docs/sidebar.json index 7406963fd..bc60ffd76 100644 --- a/docs/sidebar.json +++ b/docs/sidebar.json @@ -12,7 +12,6 @@ "Command line functions": "cli.html", "Clean notebooks": "clean.html", "Setting up Search": "search.html", - "Magic Flags": "magic_flags.html", - "Callbacks": "nbdev_callbacks.html" + "Magic Flags": "magic_flags.html" } } diff --git a/nbdev/export2html.py b/nbdev/export2html.py index b2406f91f..41abf6ba4 100644 --- a/nbdev/export2html.py +++ b/nbdev/export2html.py @@ -534,7 +534,6 @@ def convert_nb(fname, cls=HTMLExporter, template_file=None, exporter=None, dest= fname = Path(fname).absolute() # os.chdir(fname.parent) nb = read_nb(fname) - call_cb('begin_doc_nb', nb, fname, 'html') meta_jekyll = get_metadata(nb['cells']) meta_jekyll['nb_path'] = str(fname.relative_to(Config().lib_path.parent)) cls_lvl = find_default_level(nb['cells']) @@ -544,11 +543,9 @@ def convert_nb(fname, cls=HTMLExporter, template_file=None, exporter=None, dest= nb['cells'] = [_func(c) for c in nb['cells']] if execute: nb = execute_nb(nb, mod=mod) nb['cells'] = [clean_exports(c) for c in nb['cells']] - call_cb('after_doc_nb_preprocess', nb, fname, 'html') if exporter is None: exporter = nbdev_exporter(cls=cls, template_file=template_file) with open(_nb2htmlfname(fname, dest=dest),'w') as f: f.write(exporter.from_notebook_node(nb, resources=meta_jekyll)[0]) - call_cb('after_doc_nb', fname, 'html') # Cell def _notebook2html(fname, cls=HTMLExporter, template_file=None, exporter=None, dest=None, execute=True): @@ -596,14 +593,12 @@ def convert_md(fname, dest_path, img_path='docs/images/', jekyll=True): if not img_path: img_path = fname.stem + '_files/' Path(img_path).mkdir(exist_ok=True, parents=True) nb = read_nb(fname) - call_cb('begin_doc_nb', nb, fname, 'md') meta_jekyll = get_metadata(nb['cells']) try: meta_jekyll['nb_path'] = str(fname.relative_to(Config().lib_path.parent)) except: meta_jekyll['nb_path'] = str(fname) nb['cells'] = compose(*process_cells)(nb['cells']) nb['cells'] = [compose(partial(adapt_img_path, fname=fname, dest=dest_path, jekyll=jekyll), *process_cell)(c) for c in nb['cells']] - call_cb('after_doc_nb_preprocess', nb, fname, 'md') fname = Path(fname).absolute() dest_name = fname.with_suffix('.md').name exp = nbdev_exporter(cls=MarkdownExporter, template_file='jekyll-md.tpl' if jekyll else 'md.tpl') @@ -615,7 +610,6 @@ def convert_md(fname, dest_path, img_path='docs/images/', jekyll=True): if hasattr(export[1]['outputs'], 'items'): for n,o in export[1]['outputs'].items(): with open(Path(dest_path)/img_path/n, 'wb') as f: f.write(o) - call_cb('after_doc_nb', fname, 'md') # Cell _re_att_ref = re.compile(r' *!\[(.*)\]\(attachment:image.png(?: "(.*)")?\)') diff --git a/nbdev/imports.py b/nbdev/imports.py index 6b07d2102..a42d27f0c 100644 --- a/nbdev/imports.py +++ b/nbdev/imports.py @@ -72,17 +72,4 @@ def re(self): if not hasattr(Config(), 'lib_name'): raise Exception("Please fill in the library name in settings.ini.") self.pat = self.pat.replace('LIB_NAME', Config().lib_name) if self._re is None: self._re = re.compile(self.pat, self.flags) - return self._re - -def call_cb(cb_name, *args): - "Calls `cb_name` from the `nbdev_callbacks` module but won't fail if it doesn't exist" - if 'nbdev_callbacks' not in globals(): - _sys_path=sys.path - try: - cfg=Config() - sys.path=[str(cfg.config_file.parent/cfg.get('callbacks_path', '.'))] - try: import nbdev_callbacks - except: nbdev_callbacks={} - finally: sys.path=_sys_path - if not hasattr(nbdev_callbacks, cb_name): return args[0] if args else None - return getattr(nbdev_callbacks, cb_name)(*args) + return self._re \ No newline at end of file diff --git a/nbdev/test.py b/nbdev/test.py index 03f08272d..1164e9b87 100644 --- a/nbdev/test.py +++ b/nbdev/test.py @@ -53,11 +53,9 @@ def test_nb(fn, flags=None): if flags is None: flags = [] try: nb = read_nb(fn) - nb = call_cb('begin_test_nb', nb, fn, flags) for f in get_all_flags(nb['cells']): if f not in flags: return ep = NoExportPreprocessor(flags, timeout=600, kernel_name='python3') pnb = nbformat.from_dict(nb) ep.preprocess(pnb) - nb = call_cb('after_test_nb', fn) finally: os.environ.pop("IN_TEST") \ No newline at end of file diff --git a/nbs/03_export2html.ipynb b/nbs/03_export2html.ipynb index ea38805e8..99f9c1bb0 100644 --- a/nbs/03_export2html.ipynb +++ b/nbs/03_export2html.ipynb @@ -1832,7 +1832,6 @@ " fname = Path(fname).absolute()\n", "# os.chdir(fname.parent)\n", " nb = read_nb(fname)\n", - " call_cb('begin_doc_nb', nb, fname, 'html')\n", " meta_jekyll = get_metadata(nb['cells'])\n", " meta_jekyll['nb_path'] = str(fname.relative_to(Config().lib_path.parent))\n", " cls_lvl = find_default_level(nb['cells'])\n", @@ -1842,11 +1841,9 @@ " nb['cells'] = [_func(c) for c in nb['cells']]\n", " if execute: nb = execute_nb(nb, mod=mod)\n", " nb['cells'] = [clean_exports(c) for c in nb['cells']]\n", - " call_cb('after_doc_nb_preprocess', nb, fname, 'html')\n", " if exporter is None: exporter = nbdev_exporter(cls=cls, template_file=template_file)\n", " with open(_nb2htmlfname(fname, dest=dest),'w') as f:\n", - " f.write(exporter.from_notebook_node(nb, resources=meta_jekyll)[0])\n", - " call_cb('after_doc_nb', fname, 'html')" + " f.write(exporter.from_notebook_node(nb, resources=meta_jekyll)[0])" ] }, { @@ -1929,37 +1926,11 @@ ], "source": [ "#hide\n", - "class TestCallbacks:\n", - " def begin(self, nb, file_name, output_type):\n", - " self.begin_data=dict(nb=nb,file_name=file_name,output_type=output_type)\n", - " return nb\n", - " def middle(self, nb, file_name, output_type):\n", - " self.middle_data=dict(nb=nb,file_name=file_name,output_type=output_type)\n", - " return nb\n", - " def end(self, file_name, output_type):\n", - " self.end_data=dict(file_name=file_name,output_type=output_type)\n", - "test_callbacks=TestCallbacks()\n", - "call_cb('this makes sure nbdev_callbacks is loaded from the right place')\n", - "import nbdev_callbacks as cbs\n", - "original_callbacks=cbs.begin_doc_nb,cbs.after_doc_nb_preprocess,cbs.after_doc_nb\n", - "try:\n", - " cbs.begin_doc_nb=test_callbacks.begin\n", - " cbs.after_doc_nb_preprocess=test_callbacks.middle\n", - " cbs.after_doc_nb=test_callbacks.end\n", - " assert not hasattr(test_callbacks,'begin_data')\n", - " # Test when an argument is given to notebook2html\n", - " p1 = Path('/tmp/sync.html')\n", - " if p1.exists(): p1.unlink()\n", - " Path('/tmp').mkdir(exist_ok=True)\n", - " notebook2html('01_sync.ipynb', dest='/tmp');\n", - " assert p1.exists()\n", - " # Check that callback handlers have been called\n", - " for d in [test_callbacks.begin_data, test_callbacks.middle_data, test_callbacks.end_data]:\n", - " test_eq(d['file_name'], Path('01_sync.ipynb').absolute())\n", - " test_eq(d['output_type'], 'html')\n", - " if d is not test_callbacks.end_data: assert d['nb']\n", - "finally:\n", - " cbs.begin_doc_nb,cbs.after_doc_nb_preprocess,cbs.after_doc_nb=original_callbacks" + "# Test when an argument is given to notebook2html\n", + "p1 = Path('/tmp/sync.html')\n", + "if p1.exists(): p1.unlink()\n", + "notebook2html('01_sync.ipynb', dest='/tmp');\n", + "assert p1.exists()" ] }, { @@ -1983,20 +1954,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "converting: /home/jhoward/git/nbdev/nbs/magic_flags.ipynb\n", - "converting: /home/jhoward/git/nbdev/nbs/nbdev_callbacks.ipynb\n", - "converting: /home/jhoward/git/nbdev/nbs/05_merge.ipynb\n", - "converting: /home/jhoward/git/nbdev/nbs/05a_conda.ipynb\n", - "converting: /home/jhoward/git/nbdev/nbs/06_cli.ipynb\n", - "converting: /home/jhoward/git/nbdev/nbs/index.ipynb\n", - "converting: /home/jhoward/git/nbdev/nbs/03_export2html.ipynb\n", - "converting: /home/jhoward/git/nbdev/nbs/07_clean.ipynb\n", - "converting: /home/jhoward/git/nbdev/nbs/04_test.ipynb\n", - "converting: /home/jhoward/git/nbdev/nbs/00_export.ipynb\n", - "converting: /home/jhoward/git/nbdev/nbs/tutorial.ipynb\n", - "converting: /home/jhoward/git/nbdev/nbs/02_showdoc.ipynb\n", - "converting: /home/jhoward/git/nbdev/nbs/01_sync.ipynb\n", - "converting: /home/jhoward/git/nbdev/nbs/99_search.ipynb\n" + "converting: /home/peter/github/pete88b/nbdev/nbs/01_sync.ipynb\n", + "converting: /home/peter/github/pete88b/nbdev/nbs/05_merge.ipynb\n", + "converting: /home/peter/github/pete88b/nbdev/nbs/99_search.ipynb\n", + "converting: /home/peter/github/pete88b/nbdev/nbs/tutorial.ipynb\n", + "converting: /home/peter/github/pete88b/nbdev/nbs/06_cli.ipynb\n", + "converting: /home/peter/github/pete88b/nbdev/nbs/index.ipynb\n", + "converting: /home/peter/github/pete88b/nbdev/nbs/00_export.ipynb\n", + "converting: /home/peter/github/pete88b/nbdev/nbs/04_test.ipynb\n", + "converting: /home/peter/github/pete88b/nbdev/nbs/magic_flags.ipynb\n", + "converting: /home/peter/github/pete88b/nbdev/nbs/07_clean.ipynb\n", + "converting: /home/peter/github/pete88b/nbdev/nbs/02_showdoc.ipynb\n", + "converting: /home/peter/github/pete88b/nbdev/nbs/03_export2html.ipynb\n" ] } ], @@ -2057,14 +2026,12 @@ " if not img_path: img_path = fname.stem + '_files/'\n", " Path(img_path).mkdir(exist_ok=True, parents=True)\n", " nb = read_nb(fname)\n", - " call_cb('begin_doc_nb', nb, fname, 'md')\n", " meta_jekyll = get_metadata(nb['cells'])\n", " try: meta_jekyll['nb_path'] = str(fname.relative_to(Config().lib_path.parent))\n", " except: meta_jekyll['nb_path'] = str(fname)\n", " nb['cells'] = compose(*process_cells)(nb['cells'])\n", " nb['cells'] = [compose(partial(adapt_img_path, fname=fname, dest=dest_path, jekyll=jekyll), *process_cell)(c)\n", " for c in nb['cells']]\n", - " call_cb('after_doc_nb_preprocess', nb, fname, 'md')\n", " fname = Path(fname).absolute()\n", " dest_name = fname.with_suffix('.md').name\n", " exp = nbdev_exporter(cls=MarkdownExporter, template_file='jekyll-md.tpl' if jekyll else 'md.tpl')\n", @@ -2075,8 +2042,7 @@ " with (Path(dest_path)/dest_name).open('w') as f: f.write(md)\n", " if hasattr(export[1]['outputs'], 'items'):\n", " for n,o in export[1]['outputs'].items():\n", - " with open(Path(dest_path)/img_path/n, 'wb') as f: f.write(o)\n", - " call_cb('after_doc_nb', fname, 'md')" + " with open(Path(dest_path)/img_path/n, 'wb') as f: f.write(o)" ] }, { @@ -2374,13 +2340,11 @@ "Converted 03_export2html.ipynb.\n", "Converted 04_test.ipynb.\n", "Converted 05_merge.ipynb.\n", - "Converted 05a_conda.ipynb.\n", "Converted 06_cli.ipynb.\n", "Converted 07_clean.ipynb.\n", "Converted 99_search.ipynb.\n", "Converted index.ipynb.\n", "Converted magic_flags.ipynb.\n", - "Converted nbdev_callbacks.ipynb.\n", "Converted tutorial.ipynb.\n" ] } diff --git a/nbs/04_test.ipynb b/nbs/04_test.ipynb index fbd3c6d5a..d5276c6eb 100644 --- a/nbs/04_test.ipynb +++ b/nbs/04_test.ipynb @@ -241,13 +241,11 @@ " if flags is None: flags = []\n", " try:\n", " nb = read_nb(fn)\n", - " nb = call_cb('begin_test_nb', nb, fn, flags)\n", " for f in get_all_flags(nb['cells']):\n", " if f not in flags: return\n", " ep = NoExportPreprocessor(flags, timeout=600, kernel_name='python3')\n", " pnb = nbformat.from_dict(nb)\n", " ep.preprocess(pnb)\n", - " nb = call_cb('after_test_nb', fn)\n", " finally: os.environ.pop(\"IN_TEST\")" ] }, @@ -260,45 +258,6 @@ "test_nb('index.ipynb')" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#hide\n", - "nbdev users should not need to `import nbdev_callbacks` but we need to patch `begin_test_nb` and `after_test_nb` on to the `nbdev_callbacks` module to test that `test_nb` is calling the callbacks properly." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#hide\n", - "class TestCallbacks:\n", - " def begin_test_nb(self, nb, file_name, flags):\n", - " self.begin_test_nb_data=dict(nb=nb,file_name=file_name,flags=flags)\n", - " return nb\n", - " def after_test_nb(self, file_name):\n", - " self.after_test_nb_data=dict(file_name=file_name)\n", - "test_callbacks=TestCallbacks()\n", - "call_cb('This makes sure nbdev_callbacks is loaded from the right place')\n", - "import nbdev_callbacks\n", - "original_callbacks=nbdev_callbacks.begin_test_nb,nbdev_callbacks.after_test_nb\n", - "try:\n", - " nbdev_callbacks.begin_test_nb=test_callbacks.begin_test_nb\n", - " nbdev_callbacks.after_test_nb=test_callbacks.after_test_nb\n", - " assert not hasattr(test_callbacks,'begin_test_nb_data')\n", - " expected_file_name,expected_flags='../tests/single-cell-index.ipynb',['slow','cuda']\n", - " test_nb(expected_file_name,expected_flags)\n", - " assert len(test_callbacks.begin_test_nb_data['nb']['cells']) == 1\n", - " assert test_callbacks.begin_test_nb_data['file_name'] == expected_file_name\n", - " assert test_callbacks.begin_test_nb_data['flags'] == expected_flags\n", - " assert test_callbacks.after_test_nb_data['file_name'] == expected_file_name\n", - "finally:\n", - " nbdev_callbacks.begin_test_nb,nbdev_callbacks.after_test_nb=original_callbacks" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -321,13 +280,11 @@ "Converted 03_export2html.ipynb.\n", "Converted 04_test.ipynb.\n", "Converted 05_merge.ipynb.\n", - "Converted 05a_conda.ipynb.\n", "Converted 06_cli.ipynb.\n", "Converted 07_clean.ipynb.\n", "Converted 99_search.ipynb.\n", "Converted index.ipynb.\n", "Converted magic_flags.ipynb.\n", - "Converted nbdev_callbacks.ipynb.\n", "Converted tutorial.ipynb.\n" ] } diff --git a/nbs/nbdev_callbacks.ipynb b/nbs/nbdev_callbacks.ipynb deleted file mode 100644 index c4bb7586b..000000000 --- a/nbs/nbdev_callbacks.ipynb +++ /dev/null @@ -1,271 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# nbdev callbacks\n", - "\n", - "> The build docs and test nbs functions are extensible, with callbacks." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The goal of nbdev callbacks: Make it possible for you to customise the testing and build doc processes to suit the needs of your projects, without needing changes to nbdev.\n", - "\n", - "If you'd like to use callbacks, create `nbdev_callbacks.py` in your project. Feel free to copy [nbdev_callbacks.py](https://github.com/fastai/nbdev/blob/master/nbdev_callbacks.py) to get started.\n", - "\n", - "Before we get stuck into how you can use callbacks, we need to explain how *your* `nbdev_callbacks.py` gets imported." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#import\n", - "from nbdev import *" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "data": { - "text/markdown": [ - "

call_cb[source]

\n", - "\n", - "> call_cb(**`cb_name`**, **\\*`args`**)\n", - "\n", - "Calls `cb_name` from the `nbdev_callbacks` module but won't fail if it doesn't exist" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "show_doc(call_cb)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After making a temporary modification to `sys.path`, this function loads the `nbdev_callbacks` module via a regular python `import`.\n", - "\n", - "To be sure this import reads the `nbdev_callbacks.py` file from your project, `call_cb` will make the parameter `callbacks_path` (see `settings.ini`) the only entry in `sys.path` (defaulting to the project root).\n", - "\n", - "`nbdev_callbacks.py` does not have to exist and doesn't have to contain definitions for all callback handlers." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%nbdev_hide\n", - "# first we'll test with valid callback names\n", - "test_eq(call_cb('begin_test_nb', 'arg1', 2, 3), 'arg1')\n", - "test_eq(call_cb('after_test_nb', 'file.name'), None)\n", - "# If we pass an invalid callback name, we get the 1st arg back ...\n", - "test_eq(call_cb('bad_cb_name', 'arg1'), 'arg1')\n", - "test_eq(call_cb('bad_cb_name', False), False)\n", - "test_eq(call_cb('bad_cb_name', 'arg1', 2, 3), 'arg1')\n", - "# ... or None if we pass no args. Either way we see no errors\n", - "test_eq(call_cb('bad_cb_name'), None)\n", - "\n", - "try:\n", - " call_cb('begin_test_nb', 'arg1')\n", - " assert False, 'An error should be raised as we passed the wrong number of arguments to the cb'\n", - "except TypeError: pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "%nbdev_hide\n", - "nbdev users should not need to `import nbdev_callbacks` ↓ but we need it for the show_doc calls and to patch a mock function on to the `nbdev_callbacks` module to test `call_cb` when a callback function raises an error." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%nbdev_hide_input\n", - "call_cb('This makes sure nbdev_callbacks is loaded from the right place')\n", - "import nbdev_callbacks" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%nbdev_hide\n", - "nbdev_callbacks.raise_err = lambda: 1/0\n", - "try:\n", - " call_cb('raise_err')\n", - " assert False, 'An error should be raised because the cb handler raised an error'\n", - "except ZeroDivisionError: pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It is expected that you'll want to modify and return `nb` but your callback handler *could* create and return a new notebook.\n", - "\n", - "`file_name` can't be modified as it has already been used to create `nb`.\n", - "\n", - "`test_nb` calls `begin_test_nb` before checking flags so you *could* use callbacks to modify flags before testing starts." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It is expected that you'll want to modify and return `nb` but your callback handler *could* create and return a new notebook.\n", - "\n", - "`file_name` can't be modified as it has already been used to create `nb`.\n", - "\n", - "- When building HTML docs, `convert_nb` will set `output_type='html'`\n", - "- When building markdown docs, `convert_md` will set `output_type='md'`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`after_doc_nb_preprocess` gets the same arguments as `begin_doc_nb` but `nb` will have been modifed by processors that do things like:\n", - "- add `show_doc` calls for for exported functions and classes\n", - "- remove empty cells\n", - "- copy images and modify image paths\n", - "- add collapse metadata etc" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## What kind of things can you do with these callbacks?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Move img tags to the start of a new line" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To display images side-by-side we could use some HTML like:\n", - "```\n", - "
\n", - "
\n", - "
\n", - "
\n", - " \n", - "
Temp caption 1
\n", - "
\n", - "
\n", - "
\n", - "
\n", - " \n", - "
Temp caption 2
\n", - "
\n", - "
\n", - "
\n", - "
Main caption
\n", - "
\n", - "```\n", - "\n", - "Then you find that nbdev will only copy images and modify image paths for zero indented img tags.\n", - "\n", - "You could modify your HTML ... or you could move image tags with a callback:\n", - "```python\n", - "def begin_doc_nb(nb,file_name,output_type):\n", - " \"Called before converting a notebook to documentation. Return the notebook to be converted\"\n", - " for cell in nb['cells']:\n", - " if cell['cell_type']=='markdown' and not cell['source'].startswith('`'):\n", - " cell['source']=cell['source'].replace(\" Date: Wed, 16 Sep 2020 08:16:00 +0100 Subject: [PATCH 2/3] removed callbacks html --- docs/nbdev_callbacks.html | 246 -------------------------------------- 1 file changed, 246 deletions(-) delete mode 100644 docs/nbdev_callbacks.html diff --git a/docs/nbdev_callbacks.html b/docs/nbdev_callbacks.html deleted file mode 100644 index a0d16bb48..000000000 --- a/docs/nbdev_callbacks.html +++ /dev/null @@ -1,246 +0,0 @@ ---- - -title: nbdev callbacks - - -keywords: fastai -sidebar: home_sidebar - -summary: "The build docs and test nbs functions are extensible, with callbacks." -description: "The build docs and test nbs functions are extensible, with callbacks." -nb_path: "nbs/nbdev_callbacks.ipynb" ---- - - -
- - {% raw %} - -
- -
- {% endraw %} - -
-
-

The goal of nbdev callbacks: Make it possible for you to customise the testing and build doc processes to suit the needs of your projects, without needing changes to nbdev.

-

If you'd like to use callbacks, create nbdev_callbacks.py in your project. Feel free to copy nbdev_callbacks.py to get started.

-

Before we get stuck into how you can use callbacks, we need to explain how your nbdev_callbacks.py gets imported.

- -
-
-
- {% raw %} - -
-
- -
-
-
from nbdev import *
-
- -
-
-
- -
- {% endraw %} - - {% raw %} - -
- -
-
- -
- - -
-

call_cb[source]

call_cb(cb_name, *args)

-
-

Calls cb_name from the nbdev_callbacks module but won't fail if it doesn't exist

- -
- -
- -
-
- -
- {% endraw %} - -
-
-

After making a temporary modification to sys.path, this function loads the nbdev_callbacks module via a regular python import.

-

To be sure this import reads the nbdev_callbacks.py file from your project, call_cb will make the parameter callbacks_path (see settings.ini) the only entry in sys.path (defaulting to the project root).

-

nbdev_callbacks.py does not have to exist and doesn't have to contain definitions for all callback handlers.

- -
-
-
- {% raw %} - -
- -
- {% endraw %} - -
-
-

It is expected that you'll want to modify and return nb but your callback handler could create and return a new notebook.

-

file_name can't be modified as it has already been used to create nb.

-

test_nb calls begin_test_nb before checking flags so you could use callbacks to modify flags before testing starts.

- -
-
-
-
-
-

It is expected that you'll want to modify and return nb but your callback handler could create and return a new notebook.

-

file_name can't be modified as it has already been used to create nb.

-
    -
  • When building HTML docs, convert_nb will set output_type='html'
  • -
  • When building markdown docs, convert_md will set output_type='md'
  • -
- -
-
-
-
-
-

after_doc_nb_preprocess gets the same arguments as begin_doc_nb but nb will have been modifed by processors that do things like:

-
    -
  • add show_doc calls for for exported functions and classes
  • -
  • remove empty cells
  • -
  • copy images and modify image paths
  • -
  • add collapse metadata etc
  • -
- -
-
-
-
-
-

What kind of things can you do with these callbacks?

-
-
-
-
-
-

Move img tags to the start of a new line

-
-
-
-
-
-

To display images side-by-side we could use some HTML like:

- -
<figure>
-    <div style="display:flex">
-        <div style="flex:1">
-            <figure>
-                <img src="images/post_004/02.jpeg">
-                <figcaption><center>Temp caption 1</center></figcaption>
-            </figure>
-        </div>
-        <div style="flex:1">
-            <figure>
-                <img src="images/post_004/03.jpeg">
-                <figcaption><center>Temp caption 2</center></figcaption>
-            </figure>
-        </div>
-    </div>
-    <figcaption><center>Main caption</center></figcaption>
-</figure>
-

Then you find that nbdev will only copy images and modify image paths for zero indented img tags.

-

You could modify your HTML ... or you could move image tags with a callback:

-
def begin_doc_nb(nb,file_name,output_type):
-    "Called before converting a notebook to documentation. Return the notebook to be converted"
-    for cell in nb['cells']:
-        if cell['cell_type']=='markdown' and not cell['source'].startswith('`'):
-            cell['source']=cell['source'].replace("<img ", "\n<img ")
-    return nb
-
- -
-
-
-
-
-

Count the number of tests cells

-
-
-
-
-
-

To output the number of tests that have been run, you could count each cell that is not exported to your library:

-
from nbdev.export import find_default_export,is_export
-
-def begin_test_nb(nb,file_name,flags):
-    test_count=0
-    default_export=find_default_export(nb['cells'])
-    exports=[is_export(c, default_export) for c in nb['cells']]
-    cells=[(i,c,e) for i,(c,e) in enumerate(zip(nb['cells'],exports)) if c['cell_type']=='code']
-    for i,c,e in cells:
-        if not e: test_count+=1
-    print(file_name,'has',test_count,'test cells')
-    return nb
-
-

The above is just one interpretation of "the number of tests". We could easily write callback handlers to count tests in different ways, like:

-
    -
  • the number of assert or test_eq calls in the notebook
  • -
  • the number of cells that contain an assert or test_eq call
  • -
- -
-
-
-
-
-

Reserved callback handler names

-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - -
Callback handlerCall fromComments
begin_test_nbsafter_test_nbscli#nbdev_test_nbsMight be useful if writing test metrics to log files etc
begin_doc_nbsafter_doc_nbscli#nbdev_build_docs
- -
-
-
-
- - From 06ca1e3fdfa3b2b01db85c2e82b9385dea834da7 Mon Sep 17 00:00:00 2001 From: Peter Butterfill Date: Wed, 16 Sep 2020 08:57:25 +0100 Subject: [PATCH 3/3] removed callbacks py --- nbdev_callbacks.py | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 nbdev_callbacks.py diff --git a/nbdev_callbacks.py b/nbdev_callbacks.py deleted file mode 100644 index 5c0039587..000000000 --- a/nbdev_callbacks.py +++ /dev/null @@ -1,21 +0,0 @@ -__all__ = ['begin_test_nb', 'after_test_nb', 'begin_doc_nb', 'after_doc_nb_preprocess', 'after_doc_nb'] - -def begin_test_nb(nb, file_name, flags): - "Called before testing a notebook. Return the notebook to be tested" - return nb - -def after_test_nb(file_name): - "Called after testing a notebook" - pass - -def begin_doc_nb(nb, file_name, output_type): - "Called before converting a notebook to documentation. Return the notebook to be converted" - return nb - -def after_doc_nb_preprocess(nb, file_name, output_type): - "Called after pre-processing a notebook, before converting to documentation. Return the notebook to be converted" - return nb - -def after_doc_nb(file_name, output_type): - "Called after converting a notebook to documentation" - pass \ No newline at end of file