= read_nb('../../tests/minimal.ipynb') minimal
process
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.
nb_lang
nb_lang (nb)
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
"""
True)), 4) test_eq(first_code_ln(_tst.splitlines(
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.
= AttrDict(source = """#|export module
exp #|eval:false
#| hide
# | foo bar
# |woo: baz
1+2
#bar""")
# this one has #|hide: with a colon at the end, wich is quarto compliant
= AttrDict(source = """#|export module
exp2 #|eval:false
#| hide:
# | foo bar
# |woo: baz
1+2
#bar""")
= {'export':['module'], 'hide':[], 'eval:': ['false'], 'foo': ['bar'], 'woo:': ['baz']}
_answer
test_eq(extract_directives(exp), _answer)
test_eq(extract_directives(exp2), _answer)'#|eval: false\n# |woo: baz\n1+2\n#bar') test_eq(exp.source,
opt_set
opt_set (var, newval)
newval if newval else var
instantiate
instantiate (x, **kwargs)
Instantiate x
if it’s a type
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):
= '../../tests/01_everything.ipynb'
everything_fn
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
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