process

A notebook processor

NBProcessor applies processors to notebook cells. Processors can handle every cell or respond to directives at the start of a cell.

Directive parsing comes from fastcore.nbio. This module re-exports its langs, nb_lang, first_code_ln and NbCell. Cells provide the directives property and remove_directives method.


source

opt_set

def opt_set(
    var, newval
):

newval if newval else var


source

instantiate

def instantiate(
    x, **kwargs
):

Instantiate x if it’s a type


source

NBProcessor

def NBProcessor(
    path:NoneType=None, procs:NoneType=None, nb:NoneType=None, debug:bool=False, rm_directives:bool=True,
    process:bool=False
):

Process cells and nbdev comments in a notebook

A callable processor runs on every cell. To remove a cell, set its source to None:

everything_fn = '../../tests/01_everything.ipynb'

def print_execs(cell):
    if 'exec' in cell.source: print(cell.source)

NBProcessor(everything_fn, print_execs).process()
---
title: Foo
execute:
  echo: false
---
exec("o_y=1")
exec("p_y=1")
_all_ = [o_y, 'p_y']

NBProcessor saves each cell’s directives in directives_ before processing. This dictionary maps names to raw string values, with '' for bare directives. Directive handlers get these values split on whitespace into positional arguments.

Notebook metadata can hold directives under an nbdev key. NBProcessor adds them to the first code cell’s directives_. A directive with the same name in any cell takes precedence. For example, default_exp can go in notebook metadata without needing a dedicated cell:

_nbm = dict2nb(dict(cells=[mk_cell('# a note', 'markdown'), mk_cell('1+1')],
                    metadata=dict(nbdev=dict(default_exp='core')), nbformat=4, nbformat_minor=5))
NBProcessor(nb=_nbm)
test_eq(_nbm.cells[1].directives_, {'default_exp': 'core'})

_nbm = dict2nb(dict(cells=[mk_cell('#| default_exp: other\n1+1')],
                    metadata=dict(nbdev=dict(default_exp='core')), nbformat=4, nbformat_minor=5))
NBProcessor(nb=_nbm)
test_eq(_nbm.cells[0].directives_, {'default_exp': 'other'})
def printme_func(cell):
    if cell.directives_ and 'printme' in cell.directives_: print(cell.directives_['printme'])

NBProcessor(everything_fn, printme_func).process()
testing

To handle directives in a class, name each method after its directive with surrounding underscores:

class _PrintExample:
    def _printme_(self, cell, to_print): print(to_print)

NBProcessor(everything_fn, _PrintExample()).process()
testing

For a single directive, a function with a trailing underscore works too. This printme_ function does the same work as _PrintExample:

def printme_(cell, to_print): print(to_print)

NBProcessor(everything_fn, printme_).process()
testing
NBProcessor(everything_fn, _PrintExample()).process()
testing

source

Processor

def Processor(
    nb
):

Base class for processors

A Processor subclass can keep state across cells. It has access to the notebook through self.nb. Override these methods as needed:

Subclasses can also have directive handlers such as _printme_.

class CountCellProcessor(Processor):
    def begin(self):
        print(f"First cell:\n{self.nb.cells[0].source}")
        self.count=0
    def cell(self, cell):
        if cell.cell_type=='code': self.count += 1
    def end(self): print(f"* There were {self.count} code cells")
NBProcessor(everything_fn, CountCellProcessor).process()
First cell:
---
title: Foo
execute:
  echo: false
---
* There were 26 code cells