{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "hide_input": false }, "outputs": [], "source": [ "#hide\n", "#default_exp sync\n", "#default_cls_lvl 3\n", "from nbdev.showdoc import show_doc" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "hide_input": false }, "outputs": [], "source": [ "#export\n", "from nbdev.imports import *\n", "from nbdev.export import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Synchronize and diff\n", "\n", "> The functions that propagates small changes in the library back to notebooks" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The library is primarily developed in notebooks so any big changes should be made there. But sometimes, it's easier to fix small bugs or typos in the modules directly. `script2notebook` is the function that will propagate those changes back to the corresponding notebooks. Note that you can't create new cells with that functionality, so your corrections should remain limited." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Finding the way back to notebooks" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need to get the name of the object we are looking for, and then we'll try to find it in our index file." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def _get_property_name(p):\n", " \"Get the name of property `p`\"\n", " if hasattr(p, 'fget'):\n", " return p.fget.func.__qualname__ if hasattr(p.fget, 'func') else p.fget.__qualname__\n", " else: return next(iter(re.findall(r'\\'(.*)\\'', str(p)))).split('.')[-1]\n", "\n", "def get_name(obj):\n", " \"Get the name of `obj`\"\n", " if hasattr(obj, '__name__'): return obj.__name__\n", " elif getattr(obj, '_name', False): return obj._name\n", " elif hasattr(obj,'__origin__'): return str(obj.__origin__).split('.')[-1] #for types\n", " elif type(obj)==property: return _get_property_name(obj)\n", " else: return str(obj).split('.')[-1]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from nbdev.export import DocsTestClass" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(get_name(in_ipython), 'in_ipython')\n", "test_eq(get_name(DocsTestClass.test), 'test')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def qual_name(obj):\n", " \"Get the qualified name of `obj`\"\n", " if hasattr(obj,'__qualname__'): return obj.__qualname__\n", " if inspect.ismethod(obj): return f\"{get_name(obj.__self__)}.{get_name(fn)}\"\n", " return get_name(obj)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Qualified name is different from name in python for methods and properties:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(qual_name(DocsTestClass.test), 'DocsTestClass.test')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#hide\n", "class _PropertyClass:\n", " p_lambda = property(lambda x: x)\n", " def some_getter(self): return 7\n", " p_getter = property(some_getter)\n", "\n", "test_eq(get_name(_PropertyClass.p_lambda), '_PropertyClass.')\n", "test_eq(get_name(_PropertyClass.p_getter), '_PropertyClass.some_getter')\n", "test_eq(get_name(_PropertyClass), '_PropertyClass')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def source_nb(func, is_name=None, return_all=False, mod=None):\n", " \"Return the name of the notebook where `func` was defined\"\n", " is_name = is_name or isinstance(func, str)\n", " if mod is None: mod = get_nbdev_module()\n", " index = mod.index\n", " name = func if is_name else qual_name(func)\n", " while len(name) > 0:\n", " if name in index: return (name,index[name]) if return_all else index[name]\n", " name = '.'.join(name.split('.')[:-1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can either pass an object or its name (by default `is_name` will look if `func` is a string or not to determine if it should be `True` or `False`, but you can override if there is some inconsistent behavior). \n", "\n", "If passed a method of a class, the function will return the notebook in which the largest part of the function name was defined in case there is a monkey-matching that defines `class.method` in a different notebook than `class`. If `return_all=True`, the function will return a tuple with the name by which the function was found and the notebook.\n", "\n", "For properties defined using `property` or our own `add_props` helper, we approximate the name by looking at their getter functions, since we don't seem to have access to the property name itself. If everything fails (a getter cannot be found), we return the name of the object that contains the property. This suffices for `source_nb` to work." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(source_nb(notebook2script), '00_export.ipynb')\n", "test_eq(source_nb(DocsTestClass), '00_export.ipynb')\n", "test_eq(source_nb(DocsTestClass.test), '00_export.ipynb')\n", "assert source_nb(int) is None" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Reading the library" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If someone decides to change a module instead of the notebooks, the following functions help update the notebooks accordingly." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "_re_cell = re.compile(r'^# Cell|^# Internal Cell|^# Comes from\\s+(\\S+), cell')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def _split(code):\n", " lines = code.split('\\n')\n", " nbs_path = Config().nbs_path.relative_to(Config().config_file.parent)\n", " prefix = '' if nbs_path == Path('.') else f'{nbs_path}/'\n", " default_nb = re.search(f'File to edit: {prefix}(\\\\S+)\\\\s+', lines[0]).groups()[0]\n", " s,res = 1,[]\n", " while _re_cell.search(lines[s]) is None: s += 1\n", " e = s+1\n", " while e < len(lines):\n", " while e < len(lines) and _re_cell.search(lines[e]) is None: e += 1\n", " grps = _re_cell.search(lines[s]).groups()\n", " nb = grps[0] or default_nb\n", " content = lines[s+1:e]\n", " while len(content) > 1 and content[-1] == '': content = content[:-1]\n", " res.append((nb, '\\n'.join(content)))\n", " s,e = e,e+1\n", " return res" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def relimport2name(name, mod_name):\n", " \"Unwarps a relative import in `name` according to `mod_name`\"\n", " if mod_name.endswith('.py'): mod_name = mod_name[:-3]\n", " mods = mod_name.split(os.path.sep)\n", " i = last_index(Config().lib_name, mods)\n", " mods = mods[i:]\n", " if name=='.': return '.'.join(mods[:-1])\n", " i = 0\n", " while name[i] == '.': i += 1\n", " return '.'.join(mods[:-i] + [name[i:]])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we say from \n", "``` python\n", "from .submodule import bla\n", "``` \n", "in a module, it needs to be converted to something like \n", "``` python\n", "from module.submodule import bla\n", "```\n", "or \n", "``` python\n", "from module1.module2.submodule import bla\n", "``` \n", "depending on where we are. This function deals with those imports renaming." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(relimport2name('.core', 'nbdev/data.py'), 'nbdev.core')\n", "test_eq(relimport2name('.core', 'home/sgugger/fastai_dev/nbdev/nbdev/data.py'), 'nbdev.core')\n", "test_eq(relimport2name('..core', 'nbdev/vision/data.py'), 'nbdev.core')\n", "test_eq(relimport2name('.transform', 'nbdev/vision/data.py'), 'nbdev.vision.transform')\n", "test_eq(relimport2name('..notebook.core', 'nbdev/data/external.py'), 'nbdev.notebook.core')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "#Catches any from .bla import something and catches .bla in group 1, the imported thing(s) in group 2.\n", "_re_loc_import = re.compile(r'(^\\s*)from (\\.\\S*) import (.*)$')\n", "_re_loc_import1 = re.compile(r'(^\\s*)import (\\.\\S*)(.*)$')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def _deal_loc_import(code, fname):\n", " def _replace(m):\n", " sp,mod,obj = m.groups()\n", " return f\"{sp}from {relimport2name(mod, fname)} import {obj}\"\n", " def _replace1(m):\n", " sp,mod,end = m.groups()\n", " return f\"{sp}import {relimport2name(mod, fname)}{end}\"\n", " return '\\n'.join([_re_loc_import1.sub(_replace1, _re_loc_import.sub(_replace,line)) for line in code.split('\\n')])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#hide\n", "code = \"from .core import *\\nnothing to see\\n from .vision import bla1, bla2\\nimport .vision\\nimport .utils as u\"\n", "test_eq(_deal_loc_import(code, 'nbdev/data.py'), \"\"\"from nbdev.core import *\n", "nothing to see\n", " from nbdev.vision import bla1, bla2\n", "import nbdev.vision\n", "import nbdev.utils as u\"\"\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def _script2notebook(fname, dic, silent=False):\n", " \"Put the content of `fname` back in the notebooks it came from.\"\n", " if os.environ.get('IN_TEST',0): return # don't export if running tests\n", " fname = Path(fname)\n", " with open(fname, encoding='utf8') as f: code = f.read()\n", " splits = _split(code)\n", " rel_name = fname.absolute().resolve().relative_to(Config().lib_path)\n", " key = str(rel_name.with_suffix(''))\n", " assert len(splits)==len(dic[key]), f'\"{rel_name}\" exported from notebooks should have {len(dic[key])} cells but has {len(splits)}.'\n", " assert all([c1[0]==c2[1]] for c1,c2 in zip(splits, dic[key]))\n", " splits = [(c2[0],c1[0],c1[1]) for c1,c2 in zip(splits, dic[key])]\n", " nb_fnames = {Config().nbs_path/s[1] for s in splits}\n", " for nb_fname in nb_fnames:\n", " nb = read_nb(nb_fname)\n", " for i,f,c in splits:\n", " c = _deal_loc_import(c, str(fname))\n", " if f == nb_fname.name:\n", " flags = split_flags_and_code(nb['cells'][i], str)[0]\n", " nb['cells'][i]['source'] = flags + '\\n' + c.replace('', '')\n", " NotebookNotary().sign(nb)\n", " nbformat.write(nb, str(nb_fname), version=4)\n", "\n", " if not silent: print(f\"Converted {rel_name}.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#hide\n", "\n", "Some things are written to the library with a `#nbdev_comment ` comment prefix. e.g. The following cell:\n", "\n", "```python\n", "#export\n", "def _not_included_by_default(): pass\n", "_all_=[_not_included_by_default]\n", "```\n", "\n", "would get written to `{module}.py` as:\n", "\n", "```python\n", "def _not_included_by_default(): pass\n", "#nbdev_comment _all_=[_not_included_by_default]\n", "```\n", "\n", "In this case\n", "- `_all_=[_not_included_by_default]` should not be part of the module\n", "- but we can't just remove it, or we would lose code when writing back to notebooks." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[JupyterApp] WARNING | Config option `kernel_spec_manager_class` not recognized by `JupyterApp`.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Converted export.py.\n" ] } ], "source": [ "#hide\n", "dic = notebook2script(silent=True, to_dict=True)\n", "_script2notebook(Config().lib_path/'export.py', dic)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def script2notebook(fname=None, silent=False):\n", " \"Update the notebooks from any changes made in the modules corresponding to `fname`\"\n", " if os.environ.get('IN_TEST',0): return\n", " dic = notebook2script(silent=True, to_dict=True)\n", " exported = get_nbdev_module().modules\n", "\n", " if fname is None:\n", " files = [f for f in Config().lib_path.glob('**/*.py') if str(f.relative_to(Config().lib_path)) in exported]\n", " else: files = glob.glob(fname)\n", " [ _script2notebook(f, dic, silent=silent) for f in files]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If `fname` is not specified, this will convert all modules and submodules in the `lib_folder` defined in `setting.ini`. Otherwise `fname` can be a single filename or a glob expression.\n", "\n", "`silent` makes the command not print any statement. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#hide\n", "#script2notebook()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Diff notebook - library" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Before making a commit, you may want to check there is no diff between the exported library and the notebooks. You may also want to make this part of your CI, so that you don't accidentally merge a PR that introduces some changes between the two. This function is there to print this diff." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "import subprocess\n", "from distutils.dir_util import copy_tree" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def diff_nb_script():\n", " \"Print the diff between the notebooks and the library in lib_path\"\n", " lib_folder = Config().lib_path\n", " with tempfile.TemporaryDirectory() as d1, tempfile.TemporaryDirectory() as d2:\n", " copy_tree(Config().lib_path, d1)\n", " notebook2script(silent=True)\n", " copy_tree(Config().lib_path, d2)\n", " shutil.rmtree(Config().lib_path)\n", " shutil.copytree(d1, str(Config().lib_path))\n", " for d in [d1, d2]:\n", " if (Path(d)/'__pycache__').exists(): shutil.rmtree(Path(d)/'__pycache__')\n", " res = subprocess.run(['diff', '-ru', d1, d2], stdout=subprocess.PIPE)\n", " print(res.stdout.decode('utf-8'))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "No export destination, ignored:\n", "e\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n", "Warning: Exporting to \"None.py\" but this module is not part of this build\n" ] }, { "ename": "AttributeError", "evalue": "'NoneType' object has no attribute 'start'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mdiff_nb_script\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36mdiff_nb_script\u001b[0;34m()\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mtempfile\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTemporaryDirectory\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0md1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtempfile\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTemporaryDirectory\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0md2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0mcopy_tree\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mConfig\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlib_path\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0md1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mnotebook2script\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msilent\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 8\u001b[0m \u001b[0mcopy_tree\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mConfig\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlib_path\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0md2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0mshutil\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrmtree\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mConfig\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlib_path\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/git/nbdev/nbs/nbdev/export.py\u001b[0m in \u001b[0;36mnotebook2script\u001b[0;34m(fname, silent, to_dict)\u001b[0m\n\u001b[1;32m 434\u001b[0m \u001b[0md\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcollections\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdefaultdict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlist\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mto_dict\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 435\u001b[0m \u001b[0mmodules\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcreate_mod_files\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfiles\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mto_dict\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 436\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mf\u001b[0m \u001b[0;32min\u001b[0m \u001b[0msorted\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfiles\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0md\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_notebook2script\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodules\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msilent\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msilent\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mto_dict\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 437\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mto_dict\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0md\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 438\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0madd_init\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mConfig\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlib_path\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/git/nbdev/nbs/nbdev/export.py\u001b[0m in \u001b[0;36m_notebook2script\u001b[0;34m(fname, modules, silent, to_dict)\u001b[0m\n\u001b[1;32m 372\u001b[0m \u001b[0mcode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_from_future_import\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfname_out\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mflags\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mto_dict\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 373\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0ma\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 374\u001b[0;31m \u001b[0;32mif\u001b[0m \u001b[0mto_dict\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0m_add2all\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfname_out\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34mf\"'{f}'\"\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mf\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mnames\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m'.'\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mf\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mextra\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 375\u001b[0m \u001b[0mmod\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mfname\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mf\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mnames\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 376\u001b[0m \u001b[0mcode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mre\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msub\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mr' +$'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mflags\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mre\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mMULTILINE\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/git/nbdev/nbs/nbdev/export.py\u001b[0m in \u001b[0;36m_add2all\u001b[0;34m(fname, names, line_width)\u001b[0m\n\u001b[1;32m 230\u001b[0m \u001b[0mtw\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mTextWrapper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mwidth\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m120\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minitial_indent\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m''\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msubsequent_indent\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m' '\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0;36m11\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbreak_long_words\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 231\u001b[0m \u001b[0mre_all\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_re__all__def\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msearch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtext\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 232\u001b[0;31m \u001b[0mstart\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mend\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mre_all\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstart\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mre_all\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 233\u001b[0m \u001b[0mtext_all\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtw\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwrap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"{text[start:end-1]}{'' if text[end-2]=='[' else ', '}{', '.join(names)}]\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 234\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'w'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mencoding\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'utf8'\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwrite\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0mstart\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m'\\n'\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtext_all\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mend\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mAttributeError\u001b[0m: 'NoneType' object has no attribute 'start'" ] } ], "source": [ "diff_nb_script()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you receive an output, you'll need to either run `notebook2script()` or `script2notebook()` to fix the difference." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Export -" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Converted 00_export.ipynb.\n", "Converted 01_sync.ipynb.\n", "Converted 02_showdoc.ipynb.\n", "Converted 03_export2html.ipynb.\n", "Converted 04_test.ipynb.\n", "Converted 05_merge.ipynb.\n", "Converted 06_cli.ipynb.\n", "Converted 07_clean.ipynb.\n", "Converted 08_flag_tests.ipynb.\n", "Converted 99_search.ipynb.\n", "Converted index.ipynb.\n", "Converted tutorial.ipynb.\n" ] } ], "source": [ "#hide\n", "from nbdev.export import *\n", "notebook2script()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "jupytext": { "split_at_heading": true }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 4 }