Package: importnb
importnbimports notebooks as python modules.if you're here, then there is a chance you have a notebook (.ipynb) in a directory saved asUntitled.ipynb. it is just sitting there, but what if it could be used as a python module?importnbis here to answer that question.basic exampleuseimportnb'sNotebookfinder and loader to import notebooks as modules# with the new api
from importnb import imports
with imports("ipynb"):
    import Untitled

# with the explicit api
from importnb import imports
with Notebook():
    import UntitledWhat does this snippet do?the snippet beginswitha context manager that modifies the files python can discover.
it will find  theUntitled.ipynbnotebook and import it as a module with__name__Untitled.
the__file__description will have.ipynbas an extension.maybe when we give notebooks new life they eventually earn a better name thanUntitled?run a notebook as a scripttheimportnbcommand line interface mimics python's. it permits running notebooks files, modules, and raw json data.the commands below execute a notebook module and file respectively.importnb -m Untitled         # call the Untitled module as __main__
importnb Untitled.ipynb      # call the Untitled file as __main__installingimportnbuse eitherpiporconda/mambapip install importnb
conda install -cconda-forge importnb
mamba install -cconda-forge importnbimportnbfeaturesimportnb.Notebookoffers parameters to customize how modules are importedimports Jupyter notebooks as python modulesfuzzy finding conventions for finding files that are not valid python namesworks with top-level await statementsintegration withpytestextensible machinery and entry pointstranslates Jupyter notebook files (ie.ipynbfiles) line-for-line to python source providing natural error messagescommand line interface for running notebooks as python scriptshas no required dependenciescustomizing parameterstheNotebookobject has a few features that can be toggled:lazy:bool=Falselazy load the module, the namespace is populated when the module is access the first time.position:int=0the relative position of the import loader in thesys.path_hooksfuzzy:bool=Trueuse fuzzy searching syntax when underscores are encountered.include_markdown_docstring:bool=Truemarkdown blocks preceding function/class defs become docstrings.include_magic:bool=Trueignore any ipython magic syntaxesonly_defs:bool=Falseimport only function and class definitions. ignore intermediate * expressions.no_magic:bool=FalseexecuteIPythonmagic statements from the loader.these features are defined in theimportnb.loader.Interfaceclass and they can be controlled throught the command line interface.importing notebooksthe primary goal of this library is to make it easy to reuse python code in notebooks. below are a few ways to invoke python's import system within the context manager.with importnb.imports("ipynb"):
    import Untitled
    import Untitled as nb
    __import__("Untitled")
    from importlib import import_module
    import_module("Untitled")import data filesthere is support for discovering data files. when discovered, data from disk on loaded and stored on the module with rich reprs.with importnb.imports("toml", "json", "yaml"):
    passall the available entry points are found withfrom importnb.entry_points import list_aliases
list_aliases()loading directly from fileUntitled = Notebook.load("Untitled.ipynb")fuzzy findingoften notebooks have names that are not valid python files names that are restricted alphanumeric characters and an_.  theimportnbfuzzy finder converts python's import convention into globs that will find modules matching specific patters. consider the statement:with importnb.Notebook():
    import U_titl__d                        # U*titl**d.ipynbimportnbtranslatesU_titl__dto a glob format that matches the patternU*titl**d.ipynbwhen searching for the source. that means thatimportnbshould fineUntitled.ipynbas the source for the import[^unless].with importnb.Notebook():
    import _ntitled                        # *ntitled.ipynb
    import __d                     # **d.ipynb
    import U__                        # U**.ipynba primary motivation for this feature is name notebooks as if they were blog posts using theYYYY-MM-DD-title-here.ipynbconvention. there are a few ways we could this file explicitly. the fuzzy finder syntax could like any of the following:with importnb.Notebook():
    import __title_here
    import YYYY_MM_DD_title_here
    import __MM_DD_title_herefuzzy name ambiguityit is possible that a fuzzy import may be ambiguous are return multiple files.
theimportnbfuzzy finder will prefer the most recently changed file.ambiguity can be avoided by using more explicit fuzzy imports that will reduce collisions.
another option is use python's explicit import functions.with importnb.Notebook():
    __import__("YYYY-MM-DD-title-here")
    import_module("YYYY-MM-DD-title-here")importing your most recently changed notebookan outcome of resolving the most recently changed is that you can import your most recent notebook with:import __                        # **.ipynbintegrationspytestsinceimportnbtransforms notebooks to python documents we can use these as source for tests.importnbspytestextension is not fancy, it only allows for conventional pytest test discovery.nbvalis alternative testing tools that validates notebook outputs. this style is near to using notebooks asdoctestwhileimportnbprimarily adds the ability to writeunittests in notebooks. adding tests to notebooks help preserve them over time.extensibletheimportnb.Notebookmachinery is extensible. it allows other file formats to be used. for example,pidgyusesimportnbto importmarkdownfiles as compiled python code.class MyLoader(importnb.Notebook): passdeveloperpipinstall-e.# install in development modehatchruntest:cov# testimportnbuseshatchfor testing in python andIPythonappendixline-for-line translation and natural error messagesa challenge with Jupyter notebooks is that they arejsondata. this poses problems:every valid line of code in a Jupyter notebook is a quotedjsonstringjsonparsers don't have a reason to return line numbers.the problem with quoted codeline-for-linejsonparserpython'sjsonmodule is not pluggable in the way we need to find line numbers. sinceimportnbis meant to be dependency free on installation we couldn't look to any other packages likeujsonorjson5.the need for line numbers is enough that we ship a standalonejsongrammar parser. to do this without extra dependencies we use thelarkgrammar package at build time:we've defined ajson.gramarwe usehatchhooks to invokelark-standalonethat generates a standalone parser for the grammar. the generated file is shipped with the package.the result ofimportnbisjsondata translated into vertically sparse, valid python code.reproducibility caution with the fuzzy finder⚠️ fuzzy finding is not reproducible as your system will change over time. in python, "explicit is better than implicit" so defining strong fuzzy strings is best practice if you MUST use esotric names. an alternative option is to use theimportlib.import_modulemachinery