处理

一个 Notebook 处理器

Notebook 单元格开头的特殊注释可以用于向 nbdev 提供有关如何处理单元格的信息,因此我们需要能够找到这些注释的位置。

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

源代码

nb_lang

 nb_lang (nb)

源代码

first_code_ln

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

获取代码出现的第一行行号,其中 code_list 是代码列表

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

源代码

extract_directives

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

ss 中的代码行提取开头的注释指令,移除 #|,并进行分割

注释指令以 #| 开头,后跟空格分隔的 token。 extract_directives 会从单元格开头提取这些指令,直到遇到空行或包含非注释内容的行。提取的行会从源代码中移除。

exp  = AttrDict(source = """#|export module
#|eval:false
#| hide
# | foo bar
# |woo: baz
1+2
#bar""")

# this one has #|hide: with a colon at the end, wich is quarto compliant
exp2  = AttrDict(source = """#|export module
#|eval:false
#| hide:
# | foo bar
# |woo: baz
1+2
#bar""")

_answer = {'export':['module'], 'hide':[], 'eval:': ['false'], 'foo': ['bar'], 'woo:': ['baz']}

test_eq(extract_directives(exp), _answer)
test_eq(extract_directives(exp2), _answer)
test_eq(exp.source, '#|eval: false\n# |woo: baz\n1+2\n#bar')

源代码

opt_set

 opt_set (var, newval)

如果 newval 存在则使用 newval,否则使用 var


源代码

实例化

 instantiate (x, **kwargs)

如果 x 是一个类型,则将其实例化


源代码

NBProcessor

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

处理 Notebook 中的单元格和 nbdev 注释

单元格处理器可以是可调用对象(例如普通函数),在这种情况下,它们会对每个单元格进行调用(将单元格的 source 设置为 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']

注释指令会作为字典存储在单元格属性 directive_ 中,字典的键是指令名称

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

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

然而,一种更方便处理注释指令的方法是使用一个 作为处理器,并在你的类中包含一个与你的指令同名(前后加下划线)的方法

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

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

如果你的处理器只支持一个注释指令,你可以只使用一个普通函数,函数名与你的指令同名,但末尾附加一个下划线 – 例如这里的 printme_ 与上面的 _PrintExample 相同

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

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

源代码

Processor

 Processor (nb)

处理器的基类

对于更复杂的行为,可以继承 Processor,并重写 begin()(在处理任何单元格之前调用)、cell()(为每个单元格调用)和 end()(在处理完所有单元格之后调用)中的一个或多个方法。你还可以在这些子类中包含注释指令(例如上面的 _printme 示例)。子类会自动访问包含已处理 Notebook 的 self.nb.

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