process

A notebook processor

Special comments at the start of a cell can be used to provide information to nbdev about how to process a cell, so we need to be able to find the location of these comments.

minimal = read_nb('../../tests/minimal.ipynb')

source

nb_lang

 nb_lang (nb)

source

first_code_ln

 first_code_ln (code_list, re_pattern=None, lang='python')

get first line number where code occurs, where code_list is a list of code

_tst = """ 
#|default_exp
 #|export
#|hide_input
foo
"""
test_eq(first_code_ln(_tst.splitlines(True)), 4)

source

extract_directives

 extract_directives (cell, remove=True, lang='python')

Take leading comment directives from lines of code in ss, remove #|, and split

Comment directives start with #|, followed by whitespace delimited tokens, which extract_directives extracts from the start of a cell, up until a blank line or a line containing something other than comments. The extracted lines are removed from the source.

exp  = AttrDict(source = """#|export module
#|eval:false
#| hide
# | foo bar
# |woo: baz
1+2
#bar""")
test_eq(extract_directives(exp), {'export':['module'], 'hide':[], 'eval:': ['false'], 'foo': ['bar'], 'woo:': ['baz']})
test_eq(exp.source, '#|eval: false\n# |woo: baz\n1+2\n#bar')

source

opt_set

 opt_set (var, newval)

newval if newval else var


source

instantiate

 instantiate (x, **kwargs)

Instantiate x if it’s a type


source

NBProcessor

 NBProcessor (path=None, procs=None, nb=None, debug=False,
              rm_directives=True, process=False)

Process cells and nbdev comments in a notebook

Cell processors can be callables (e.g regular functions), in which case they are called for every cell (set a cell’s source to None to remove the cell):

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']

Comment directives are put in a cell attribute directive_ as a dictionary keyed by directive name:

def printme_func(cell):
    if cell.directives_ and 'printme' in cell.directives_: print(cell.directives_['printme'])

NBProcessor(everything_fn, printme_func).process()
['testing']

However, a more convenient way to handle comment directives is to use a class as a processor, and include a method in your class with the same name as your directive, surrounded by underscores:

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

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

In the case that your processor supports just one comment directive, you can just use a regular function, with the same name as your directive, but with an underscore appended – here printme_ is identical to _PrintExample above:

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

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

source

Processor

 Processor (nb)

Base class for processors

For more complex behavior, inherit from Processor, and override one of more of begin() (called before any cells are processed), cell() (called for each cell), and end() (called after all cells are processed). You can also include comment directives (such as the _printme example above) in these subclasses. Subclasses will automatically have access to self.nb, containing the processed notebook.

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