diff --git a/nbdev/export.py b/nbdev/export.py
index f7fc6e7bd..94acee1cf 100644
--- a/nbdev/export.py
+++ b/nbdev/export.py
@@ -30,7 +30,7 @@ def _export_(self, cell, exp_to=None):
def __call__(self, cell):
src = cell.source
if not src: return
- if cell.cell_type=='markdown' and src.startswith('# '): self.modules['#'].append(cell)
+ if cell.cell_type=='markdown' and (src.startswith('# ') or 'export' in cell.directives_): self._exporti_(cell)
_exports_=_export_
# %% ../nbs/api/04_export.ipynb #76717e36
diff --git a/nbdev/maker.py b/nbdev/maker.py
index 78e886210..58cc7bf35 100644
--- a/nbdev/maker.py
+++ b/nbdev/maker.py
@@ -175,14 +175,15 @@ def _import2relative(cells, lib_path=None):
# %% ../nbs/api/02_maker.ipynb #5bff9d71
def _retr_mdoc(cells):
- "Search for md meta quote lines, used to create module docstring"
+ "Search for markdown cells used to create module docstring"
md1 = first(o for o in cells if o.cell_type=='markdown' and o.source.startswith('# '))
if not md1: return ''
lines = dropwhile(lambda l: not l.startswith('> '), md1.source.splitlines())
lines = list(takewhile(lambda l: l.startswith('> '), lines))
- if not lines: return ''
summ = '\n'.join(l.lstrip('> ').strip() for l in lines)
- return f'"""{summ}"""\n\n' if summ else ''
+ docs = L(o.source.rstrip() for o in cells if o.cell_type=='markdown' and 'export' in getattr(o,'directives_',{}))
+ mdoc = '\n\n'.join(L(summ)+docs).strip()
+ return f'"""{mdoc}\n"""\n\n' if mdoc else ''
# %% ../nbs/api/02_maker.ipynb #cdd205d6
@patch
diff --git a/nbdev/processors.py b/nbdev/processors.py
index cff43200b..539732edf 100644
--- a/nbdev/processors.py
+++ b/nbdev/processors.py
@@ -198,8 +198,9 @@ def rm_header_dash(cell):
_hide_dirs = {'export','exporti', 'hide','default_exp'}
def rm_export(cell):
- "Remove cells that are exported or hidden"
- if cell.directives_ and (cell.directives_.keys() & _hide_dirs): del(cell['source'])
+ "Remove code cells that are exported or hidden"
+ if cell.cell_type=='code' and cell.directives_ and (cell.directives_.keys() & _hide_dirs): del(cell['source'])
+
# %% ../nbs/api/10_processors.ipynb #2d9a0a30
_re_showdoc = re.compile(r'^show_doc', re.MULTILINE)
diff --git a/nbs/api/02_maker.ipynb b/nbs/api/02_maker.ipynb
index a32f178a2..2049ab902 100644
--- a/nbs/api/02_maker.ipynb
+++ b/nbs/api/02_maker.ipynb
@@ -491,14 +491,15 @@
"source": [
"#| export\n",
"def _retr_mdoc(cells):\n",
- " \"Search for md meta quote lines, used to create module docstring\"\n",
+ " \"Search for markdown cells used to create module docstring\"\n",
" md1 = first(o for o in cells if o.cell_type=='markdown' and o.source.startswith('# '))\n",
" if not md1: return ''\n",
" lines = dropwhile(lambda l: not l.startswith('> '), md1.source.splitlines())\n",
" lines = list(takewhile(lambda l: l.startswith('> '), lines))\n",
- " if not lines: return ''\n",
" summ = '\\n'.join(l.lstrip('> ').strip() for l in lines)\n",
- " return f'\"\"\"{summ}\"\"\"\\n\\n' if summ else ''"
+ " docs = L(o.source.rstrip() for o in cells if o.cell_type=='markdown' and 'export' in getattr(o,'directives_',{}))\n",
+ " mdoc = '\\n\\n'.join(L(summ)+docs).strip()\n",
+ " return f'\"\"\"{mdoc}\\n\"\"\"\\n\\n' if mdoc else ''"
]
},
{
@@ -510,7 +511,10 @@
"source": [
"#| hide\n",
"nb = read_nb('02_maker.ipynb')\n",
- "test_eq(_retr_mdoc(nb.cells), '\"\"\"Create one or more modules from selected notebook cells\"\"\"\\n\\n')"
+ "test_eq(_retr_mdoc(nb.cells), '\"\"\"Create one or more modules from selected notebook cells\"\"\"\\n\\n')\n",
+ "nb.cells.append(mk_cell('Extra module docs', 'markdown'))\n",
+ "nb.cells[-1].directives_ = {'export': ''}\n",
+ "test_eq(_retr_mdoc(nb.cells), '\"\"\"Create one or more modules from selected notebook cells\\n\\nExtra module docs\"\"\"\\n\\n')\n"
]
},
{
@@ -557,26 +561,46 @@
{
"data": {
"text/markdown": [
+ "
\n",
+ "\n",
"```python\n",
"# AUTOGENERATED! DO NOT EDIT! File to edit: ../../04_export.ipynb.\n",
"\n",
- "# %% ../../04_export.ipynb #73b66309\n",
+ "# %% ../../04_export.ipynb #dec4cd88\n",
"from __future__ import print_function\n",
"\n",
"# %% auto #0\n",
"__all__ = ['b']\n",
"\n",
- "# %% ../../04_export.ipynb #c168485a\n",
+ "# %% ../../04_export.ipynb #b2a73a89\n",
"#| export\n",
"def a(): ...\n",
"\n",
- "# %% ../../04_export.ipynb #94c4db18\n",
+ "# %% ../../04_export.ipynb #470e0aea\n",
"def b(): ...\n",
"\n",
- "```"
+ "```\n",
+ "\n",
+ "
"
],
"text/plain": [
- ""
+ "Markdown(```python\n",
+ "# AUTOGENERATED! DO NOT EDIT! File to edit: ../../04_export.ipynb.\n",
+ "\n",
+ "# %% ../../04_export.ipynb #dec4cd88\n",
+ "from __future__ import print_function\n",
+ "\n",
+ "# %% auto #0\n",
+ "__all__ = ['b']\n",
+ "\n",
+ "# %% ../../04_export.ipynb #b2a73a89\n",
+ "#| export\n",
+ "def a(): ...\n",
+ "\n",
+ "# %% ../../04_export.ipynb #470e0aea\n",
+ "def b(): ...\n",
+ "\n",
+ "```)"
]
},
"execution_count": null,
@@ -637,24 +661,42 @@
{
"data": {
"text/markdown": [
+ "\n",
+ "\n",
"```python\n",
"# AUTOGENERATED! DO NOT EDIT! File to edit: ../../01_export.ipynb.\n",
"\n",
- "# %% ../../01_export.ipynb #cfd87ddf\n",
+ "# %% ../../01_export.ipynb #65e15f2e\n",
"from __future__ import print_function\n",
"\n",
- "# %% ../../01_export.ipynb #23244dea\n",
+ "# %% ../../01_export.ipynb #1e9ea7e8\n",
"#| export\n",
"def a(): ...\n",
"\n",
- "# %% ../../01_export.ipynb #c710d552\n",
+ "# %% ../../01_export.ipynb #d73c328f\n",
"#| export\n",
"class A:\n",
"\n",
- "```"
+ "```\n",
+ "\n",
+ "
"
],
"text/plain": [
- ""
+ "Markdown(```python\n",
+ "# AUTOGENERATED! DO NOT EDIT! File to edit: ../../01_export.ipynb.\n",
+ "\n",
+ "# %% ../../01_export.ipynb #65e15f2e\n",
+ "from __future__ import print_function\n",
+ "\n",
+ "# %% ../../01_export.ipynb #1e9ea7e8\n",
+ "#| export\n",
+ "def a(): ...\n",
+ "\n",
+ "# %% ../../01_export.ipynb #d73c328f\n",
+ "#| export\n",
+ "class A:\n",
+ "\n",
+ "```)"
]
},
"execution_count": null,
@@ -717,31 +759,56 @@
{
"data": {
"text/markdown": [
+ "\n",
+ "\n",
"```python\n",
"# AUTOGENERATED! DO NOT EDIT! File to edit: ../../04_export.ipynb.\n",
"\n",
- "# %% ../../04_export.ipynb #73b66309\n",
+ "# %% ../../04_export.ipynb #dec4cd88\n",
"from __future__ import print_function\n",
"\n",
"# %% auto #0\n",
"__all__ = ['b', 'c', 'd']\n",
"\n",
- "# %% ../../04_export.ipynb #c168485a\n",
+ "# %% ../../04_export.ipynb #b2a73a89\n",
"#| export\n",
"def a(): ...\n",
"\n",
- "# %% ../../04_export.ipynb #94c4db18\n",
+ "# %% ../../04_export.ipynb #470e0aea\n",
"def b(): ...\n",
"\n",
- "# %% ../../04_export.ipynb #e53b62a7\n",
+ "# %% ../../04_export.ipynb #4e1e1d2e\n",
"def c(): ...\n",
"\n",
- "# %% ../../04_export.ipynb #90b96fb2\n",
+ "# %% ../../04_export.ipynb #7199ab8d\n",
"def d(): ...\n",
- "```"
+ "```\n",
+ "\n",
+ "
"
],
"text/plain": [
- ""
+ "Markdown(```python\n",
+ "# AUTOGENERATED! DO NOT EDIT! File to edit: ../../04_export.ipynb.\n",
+ "\n",
+ "# %% ../../04_export.ipynb #dec4cd88\n",
+ "from __future__ import print_function\n",
+ "\n",
+ "# %% auto #0\n",
+ "__all__ = ['b', 'c', 'd']\n",
+ "\n",
+ "# %% ../../04_export.ipynb #b2a73a89\n",
+ "#| export\n",
+ "def a(): ...\n",
+ "\n",
+ "# %% ../../04_export.ipynb #470e0aea\n",
+ "def b(): ...\n",
+ "\n",
+ "# %% ../../04_export.ipynb #4e1e1d2e\n",
+ "def c(): ...\n",
+ "\n",
+ "# %% ../../04_export.ipynb #7199ab8d\n",
+ "def d(): ...\n",
+ "```)"
]
},
"execution_count": null,
@@ -761,7 +828,9 @@
"outputs": [],
"source": [
"try:\n",
+ " sys.path.append('')\n",
" g = exec_import('tmp.test.testing', '*')\n",
+ " sys.path.pop()\n",
" for s in \"b c d\".split(): assert s in g, s\n",
" assert 'a' not in g\n",
" assert g['b']() is None\n",
@@ -816,7 +885,16 @@
]
}
],
- "metadata": {},
+ "metadata": {
+ "solveit": {
+ "default_code": true,
+ "mode": "learning",
+ "use_fence": false,
+ "use_thinking": true,
+ "use_tools": true,
+ "ver": 2
+ }
+ },
"nbformat": 4,
"nbformat_minor": 5
}
diff --git a/nbs/api/04_export.ipynb b/nbs/api/04_export.ipynb
index cd79c50ce..6c253e8c1 100644
--- a/nbs/api/04_export.ipynb
+++ b/nbs/api/04_export.ipynb
@@ -54,7 +54,7 @@
"from pdb import set_trace\n",
"from importlib import reload\n",
"from fastcore import shutil\n",
- "from fastcore.nbio import read_nb"
+ "from fastcore.nbio import *"
]
},
{
@@ -76,7 +76,7 @@
" def __call__(self, cell):\n",
" src = cell.source\n",
" if not src: return\n",
- " if cell.cell_type=='markdown' and src.startswith('# '): self.modules['#'].append(cell)\n",
+ " if cell.cell_type=='markdown' and (src.startswith('# ') or 'export' in cell.directives_): self._exporti_(cell)\n",
" _exports_=_export_"
]
},
@@ -107,6 +107,33 @@
"assert 'h_n' in exp.in_all['some.thing'][0].source"
]
},
+ {
+ "cell_type": "markdown",
+ "id": "9e635bb9",
+ "metadata": {},
+ "source": [
+ "Markdown title cells and `#| export` markdown cells are collected into the module so `ModuleMaker` can build the module docstring.\n",
+ "They must not be added to `in_all`, since `__all__` generation only applies to Python symbols from code cells."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ddb7e224",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nb = dict2nb({'cells':[\n",
+ " mk_cell('#| default_exp tmp_doc'),\n",
+ " mk_cell('# Test module\\n> Short summary', 'markdown'),\n",
+ " mk_cell('#| export\\nExtra docs', 'markdown'),\n",
+ " mk_cell('#| export\\ndef f(): return 1')]})\n",
+ "exp = ExportModuleProc(); NBProcessor(nb=nb, procs=exp).process()\n",
+ "test_eq([(c.cell_type, c.source.splitlines()[0]) for c in exp.modules['#']], \n",
+ " [('markdown', '# Test module'), ('markdown', 'Extra docs'), ('code', 'def f(): return 1')])\n",
+ "test_eq([c.cell_type for c in exp.in_all['#']], ['code'])"
+ ]
+ },
{
"cell_type": "markdown",
"id": "94eb949b",
@@ -168,9 +195,13 @@
"shutil.rmtree('tmp', ignore_errors=True)\n",
"nb_export('../../tests/00_some.thing.ipynb', 'tmp')\n",
"\n",
+ "sys.path.append('')\n",
"g = exec_new('import tmp.some.thing')\n",
+ "sys.path.pop()\n",
"test_eq(g['tmp'].some.thing.__all__, ['a'])\n",
- "test_eq(g['tmp'].some.thing.a, 1)"
+ "test_eq(g['tmp'].some.thing.a, 1)\n",
+ "test_eq(g['tmp'].some.thing.__doc__, \n",
+ "\"Test module some.thing\\n\\nThis notebook is used to demonstrate exporting to an existing module. See the notebooks in `nbs` for how it's used.\")\n"
]
},
{
@@ -242,17 +273,18 @@
"g = exec_new('import nbdev.export')\n",
"assert hasattr(g['nbdev'].export, 'nb_export')"
]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "184b448f",
- "metadata": {},
- "outputs": [],
- "source": []
}
],
- "metadata": {},
+ "metadata": {
+ "solveit": {
+ "default_code": false,
+ "mode": "learning",
+ "use_fence": false,
+ "use_thinking": false,
+ "use_tools": false,
+ "ver": 2
+ }
+ },
"nbformat": 4,
"nbformat_minor": 5
}
diff --git a/nbs/api/10_processors.ipynb b/nbs/api/10_processors.ipynb
index 9bdf6e85b..56326abc0 100644
--- a/nbs/api/10_processors.ipynb
+++ b/nbs/api/10_processors.ipynb
@@ -633,8 +633,8 @@
"_hide_dirs = {'export','exporti', 'hide','default_exp'}\n",
"\n",
"def rm_export(cell):\n",
- " \"Remove cells that are exported or hidden\"\n",
- " if cell.directives_ and (cell.directives_.keys() & _hide_dirs): del(cell['source'])"
+ " \"Remove code cells that are exported or hidden\"\n",
+ " if cell.cell_type=='code' and cell.directives_ and (cell.directives_.keys() & _hide_dirs): del(cell['source'])\n"
]
},
{
diff --git a/tests/00_some.thing.ipynb b/tests/00_some.thing.ipynb
index a7963e64d..1bb86040b 100644
--- a/tests/00_some.thing.ipynb
+++ b/tests/00_some.thing.ipynb
@@ -2,14 +2,27 @@
"cells": [
{
"cell_type": "markdown",
+ "id": "f88a8d95",
"metadata": {},
"source": [
+ "# Test Module some.thing\n",
+ "\n",
+ "> Test module some.thing"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "21cd9596",
+ "metadata": {},
+ "source": [
+ "#| export\n",
"This notebook is used to demonstrate exporting to an existing module. See the notebooks in `nbs` for how it's used."
]
},
{
"cell_type": "code",
"execution_count": null,
+ "id": "f23727ae",
"metadata": {},
"outputs": [],
"source": [
@@ -20,6 +33,7 @@
{
"cell_type": "code",
"execution_count": null,
+ "id": "581f3271",
"metadata": {},
"outputs": [],
"source": [
@@ -29,12 +43,15 @@
}
],
"metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
+ "solveit": {
+ "default_code": true,
+ "mode": "learning",
+ "use_fence": false,
+ "use_thinking": false,
+ "use_tools": true,
+ "ver": 2
}
},
"nbformat": 4,
- "nbformat_minor": 4
+ "nbformat_minor": 5
}