importnb imports notebooks as modules & packages. Use notebooks as tests, source code, importable modules, and command line utilities.
pip install importnb
conda install -c conda-forge importnb
After importnb
is install, pytest will discover and import notebooks as tests.
pytest readme.ipynb
Notebooks are often used as informal tests, now they can be formally tested with pytest plugins
importnb
can run unittests and doctests against notebook modules.
ipython -m importnb.test readme
importnb
interprets the first markdown cell as a docstring. This is a nice place to put doctests to improve the reusability of a notebook.
importnb
can run notebooks as command line scripts. Any literal variable in the notebook, may be applied as a parameter from the command line.
ipython -m importnb -- readme.ipynb --foo "A new value"
Restart and run all or it didn't happen.
importnb
excels in an interactive environment and if a notebook will Restart and Run All then it may reused as python code. The Notebook
context manager will allow notebooks with valid names to import with Python.
>>> from importnb import Notebook
with __import__('importnb').Notebook():
import readme
importnb.loader
will find notebooks available anywhere along thesys.path
.
from importnb import Notebook
with Notebook():
import readme
foo = 42
import readme
assert readme.foo is 42
assert readme.__file__.endswith('.ipynb')
The context manager is required to reload
a module.
from importlib import reload
with Notebook():
reload(readme)
The importnb.loader.Notebook
will import a notebook even if there is an exception by supplying the exceptions
option. The exception is found on module._exception
.
with Notebook(exceptions=BaseException):
try: from . import readme
except: import readme
The lazy
option will delay the evaluation of a module until one of its attributes are accessed the first time.
with Notebook(lazy=True):
import readme
if __name__ == '__main__':
with Notebook():
import __a_me
assert __a_me.__file__ == readme.__file__
Python does not provide a way to import file names starting with numbers of contains special characters. importnb
installs a fuzzy import logic to import files containing these edge cases.
import __2018__6_01_A_Blog_Post
will find the first file matching *2018*6?01?A?Blog?Post
. Importing Untitled314519.ipynb
could be supported with the query below.
import __314519
importnb
can capture the stdout
, stderr
, and display
in the context manager. The arguments are similar to IPython.util.capture.capture_output
.
with Notebook(stdout=True, stderr=True, display=True) as output:
import readme
The first markdown cell will become the module docstring.
if __name__ == '__main__':
print(readme.__doc__.splitlines()[0])
__importnb__ imports notebooks as modules & packages. Use notebooks as tests, source code, importable modules, and command line utilities.
Meaning non-code blocks can be executeb by doctest.
if __name__ == '__main__':
__import__('doctest').testmod(readme)
Notebook names may not be valid Python paths. In this case, use Notebook.from_filename
.
Notebook.from_filename('readme.ipynb')
Import under the __main__
context.
Notebook('__main__').from_filename('readme.ipynb')
Literal ast statements are converted to notebooks parameters.
In readme
, foo
is a parameter because it may be evaluated with ast.literal_val
from importnb import Parameterize
f = Parameterize().from_filename(readme.__file__)
The parameterized module is a callable that evaluates with different literal statements.
assert callable(f)
f.__signature__
<Signature (*, foo=42)>
assert f().foo == 42
assert f(foo='importnb').foo == 'importnb'
Run any notebook from the command line with importnb. Any parameterized expressions are available as parameters on the command line.
!ipython -m importnb -- readme.ipynb --foo "The new value"
Avoid the use of the context manager using loading importnb as IPython extension.
%load_ext importnb
%unload_ext importnb
will unload the extension.
importnb
may allow notebooks to import by default with
!importnb-install
If you'd like to play with source code on binder then you must execute the command above. Toggle the markdown cell to a code cell and run it.
This extension will install a script into the default IPython profile startup that is called each time an IPython session is created.
Uninstall the extension with importnb-install
.
When the default extension is loaded any notebook can be run from the command line. After the importnb
extension is created notebooks can be execute from the command line.
ipython -m readme
In the command line context, __file__ == sys.arv[0] and __name__ == '__main__'
.
See the deploy step in the travis build.
importnb
installs a pytest plugin when it is setup. Any notebook obeying the py.test discovery conventions can be used in to pytest. This is great because notebooks are generally your first test.
!ipython -m pytest -- src
Will find all the test notebooks and configurations as pytest would any Python file.
To package notebooks add recursive-include package_name *.ipynb
if __name__ == '__main__':
if globals().get('__file__', None) == __import__('sys').argv[0]:
print(foo, __import__('sys').argv)
else:
from subprocess import call
from importnb.capture import capture_output
with capture_output() as out: __import__('pytest').main("src".split())
print('plugins'+out.stdout.split('plugins', 1)[-1])
"""Formatting"""
from pathlib import Path
from importnb.utils.export import export
root = 'src/importnb/notebooks/'
for path in Path(root).rglob("""*.ipynb"""):
if 'checkpoint' not in str(path):
export(path, Path('src/importnb') / path.with_suffix('.py').relative_to(root))
call("jupyter nbconvert --to markdown --NbConvertApp.output_files_dir=docs/{notebook_name}_files readme.ipynb".split())
plugins: cov-2.5.1, benchmark-3.1.1, hypothesis-3.56.5, importnb-0.3.1
collecting ... collected 26 items
src/importnb/tests/test_importnb.ipynb::test_single_file_with_context PASSED [ 3%]
src/importnb/tests/test_importnb.ipynb::test_from_filename PASSED [ 7%]
src/importnb/tests/test_importnb.ipynb::test_from_execute PASSED [ 11%]
src/importnb/tests/test_importnb.ipynb::test_with_doctest PASSED [ 15%]
src/importnb/tests/test_importnb.ipynb::test_from_filename_main PASSED [ 19%]
src/importnb/tests/test_importnb.ipynb::test_parameterize PASSED [ 23%]
src/importnb/tests/test_importnb.ipynb::test_commandline PASSED [ 26%]
src/importnb/tests/test_importnb.ipynb::test_python_file PASSED [ 30%]
src/importnb/tests/test_importnb.ipynb::test_single_file_with_capture PASSED [ 34%]
src/importnb/tests/test_importnb.ipynb::test_capturer PASSED [ 38%]
src/importnb/tests/test_importnb.ipynb::test_single_file_with_lazy PASSED [ 42%]
src/importnb/tests/test_importnb.ipynb::test_single_file_without_context XPASS [ 46%]
src/importnb/tests/test_importnb.ipynb::test_single_file_relative XPASS [ 50%]
src/importnb/tests/test_importnb.ipynb::test_single_with_extension PASSED [ 53%]
src/importnb/tests/test_importnb.ipynb::test_package PASSED [ 57%]
src/importnb/tests/test_importnb.ipynb::test_package_failure xfail [ 61%]
src/importnb/tests/test_importnb.ipynb::test_package_failure_partial PASSED [ 65%]
src/importnb/tests/test_importnb.ipynb::test_tmp_dir PASSED [ 69%]
src/importnb/tests/test_importnb.ipynb::test_query_numeric_files PASSED [ 73%]
src/importnb/tests/test_unittests.ipynb::TestExtension::test_failure xfail [ 76%]
src/importnb/tests/test_unittests.ipynb::TestExtension::test_import PASSED [ 80%]
src/importnb/tests/test_unittests.ipynb::TestContext::test_import PASSED [ 84%]
src/importnb/tests/test_unittests.ipynb::TestContext::test_reload_with_context PASSED [ 88%]
src/importnb/tests/test_unittests.ipynb::TestPartial::test_exception PASSED [ 92%]
src/importnb/tests/test_unittests.ipynb::TestPartial::test_traceback PASSED [ 96%]
src/importnb/tests/test_unittests.ipynb::TestRemote::test_imports SKIPPED [100%]
========= 21 passed, 1 skipped, 2 xfailed, 2 xpassed in 3.86 seconds ==========
if __name__ == '__main__':
try:
from IPython.display import display, Image
from IPython import get_ipython
with capture_output():
get_ipython().system("cd docs && pyreverse importnb -opng -pimportnb")
display(Image(url='docs/classes_importnb.png', ))
except: ...