maker

从选定的 notebook 单元格创建一个或多个模块

辅助函数

变量辅助函数

这些函数允许我们在 Python 模块中查找和修改变量的定义。


源代码

find_var

 find_var (lines, varname)

lines 中查找定义 varname 的行号

t = '''a_=(1,
  2,
  3)

b_=3'''
test_eq(find_var(t.splitlines(), 'a_'), (0,3))
test_eq(find_var(t.splitlines(), 'b_'), (4,5))

源代码

read_var

 read_var (code, varname)

评估并返回在 code 中定义的 varname 的值

test_eq(read_var(t, 'a_'), (1,2,3))
test_eq(read_var(t, 'b_'), 3)

源代码

update_var

 update_var (varname, func, fn=None, code=None)

通过调用 func 并传入当前定义,更新文件 fnvarname 的定义

g = exec_new(t)
test_eq((g['a_'],g['b_']), ((1,2,3),3))
t2 = update_var('a_', lambda o:0, code=t)
exec(t2, g)
test_eq((g['a_'],g['b_']), (0,3))
t3 = update_var('b_', lambda o:0, code=t)
exec(t3, g)
test_eq((g['a_'],g['b_']), ((1,2,3),0))

源代码

ModuleMaker

 ModuleMaker (dest, name, nb_path, is_new=True, parse=True, solo_nb=False)

用于从 notebook 源代码单元格创建导出库的辅助类

为了导出 notebook,我们需要一种创建 Python 文件的方法。 ModuleMaker 承担了这个角色。传入您希望创建模块的目录、模块名称、notebook 源代码路径,如果是正在创建的新文件(而不是添加到现有文件),则将 is_new 设置为 True。保存的模块的位置将在 fname 中。最后,如果 notebook 中的源代码不应被 Python 解析(例如单元格中的部分类声明),则应将 parse 设置为 False

注意:如果这样做,那么 __all__ 的生成也将被关闭。

mm = ModuleMaker(dest='tmp', name='test.testing', nb_path=Path.cwd()/'04_export.ipynb', is_new=True)
mm.fname
Path('tmp/test/testing.py')

源代码

decor_id

 decor_id (d)

装饰器的 id 属性,无论是以函数形式调用还是直接使用


源代码

ModuleMaker.make_all

 ModuleMaker.make_all (cells)

使用 cells 中的所有导出内容创建 __all__


源代码

make_code_cells

 make_code_cells (*ss)

我们想在导出的模块顶部添加一个 __all__。此方法会从 cells 中的所有代码自动生成它。

nb = make_code_cells("from __future__ import print_function", "def a():...", "def b():...",
                      "c=d=1", "_f=1", "_g=1", "_h=1", "_all_=['_g', _h]", "@patch\ndef h(self:ca):...")
test_eq(set(mm.make_all(nb)), set(['a','b','c','d', '_g', '_h']))

源代码

relative_import

 relative_import (name, fname, level=0)

将模块 name 转换为相对于 fname 的名称

test_eq(relative_import('nbdev.core', "xyz"), 'nbdev.core')
test_eq(relative_import('nbdev.core', 'nbdev'), '.core')
_p = Path('fastai')
test_eq(relative_import('fastai.core', _p/'vision'), '..core')
test_eq(relative_import('fastai.core', _p/'vision/transform'), '...core')
test_eq(relative_import('fastai.vision.transform', _p/'vision'), '.transform')
test_eq(relative_import('fastai.notebook.core', _p/'data'), '..notebook.core')
test_eq(relative_import('fastai.vision', _p/'vision'), '.')
test_eq(relative_import('fastai', _p), '.')
test_eq(relative_import('fastai', _p/'vision'), '..')
test_eq(relative_import('fastai', _p/'vision/transform'), '...')

源代码

NbCell.import2relative

 NbCell.import2relative (cell:execnb.nbio.NbCell, libname)

源代码

update_import

 update_import (source, tree, libname, f=<function relative_import>)
ss = "from nbdev.export import *\nfrom nbdev.a.b import *"
cell = make_code_cells([ss])[0]
cell.import2relative('nbdev')
test_eq(cell.source, 'from .export import *\nfrom .a.b import *')

cell = make_code_cells([ss])[0]
cell.import2relative('nbdev/a')
test_eq(cell.source, 'from ..export import *\nfrom .b import *')

源代码

ModuleMaker.make

 ModuleMaker.make (cells, all_cells=None, lib_path=None)

写入包含 cells 的模块,其中 __all__all_cells 生成

cells = make_code_cells("from __future__ import print_function",
                        "#|export\ndef a(): ...", "def b(): ...")
mm.make(cells, L([cells[2]]))
show_src(Path('tmp/test/testing.py').read_text(encoding='utf-8'))
# AUTOGENERATED! DO NOT EDIT! File to edit: ../../04_export.ipynb.

# %% ../../04_export.ipynb 0
from __future__ import print_function

# %% auto 0
__all__ = ['b']

# %% ../../04_export.ipynb
#|export
def a(): ...

# %% ../../04_export.ipynb
def b(): ...

如果您不想添加任何 __all__,请传递 all_cells=[]parse=False

传递 parse=False 在编写 ast.parse 可能不喜欢但仍希望导出的分散函数或类时也很方便,例如包含以下内容的单元格:

#|export
class A:

请注意,这样做无法正确生成 __all__,因此我们假定这是不需要的。

am = ModuleMaker(dest='tmp', name='test.testing_noall', nb_path=Path.cwd()/'01_export.ipynb', is_new=True, parse=False)
am.fname
Path('tmp/test/testing_noall.py')
cells = make_code_cells("from __future__ import print_function", "#|export\ndef a(): ...", "#|export\nclass A:")
am.make(cells)
show_src(Path('tmp/test/testing_noall.py').read_text(encoding='utf-8'))
# AUTOGENERATED! DO NOT EDIT! File to edit: ../../01_export.ipynb.

# %% ../../01_export.ipynb
from __future__ import print_function

# %% ../../01_export.ipynb
#|export
def a(): ...

# %% ../../01_export.ipynb
#|export
class A:

如果 is_new=False,则附加定义将添加到底部,并且任何现有的 __all__ 将通过新添加的符号进行更新。

c2 = make_code_cells("def c(): ...", "def d(): ...")
mm = ModuleMaker(dest='tmp', name='test.testing', nb_path=Path.cwd()/'04_export.ipynb', is_new=False)
mm.make(c2, c2)
show_src(Path('tmp/test/testing.py').read_text(encoding='utf-8'))
# AUTOGENERATED! DO NOT EDIT! File to edit: ../../04_export.ipynb.

# %% ../../04_export.ipynb 0
from __future__ import print_function

# %% auto 0
__all__ = ['b', 'c', 'd']

# %% ../../04_export.ipynb
#|export
def a(): ...

# %% ../../04_export.ipynb
def b(): ...

# %% ../../04_export.ipynb 0
def c(): ...

# %% ../../04_export.ipynb 1
def d(): ...
try:
    g = exec_import('tmp.test.testing', '*')
    for s in "b c d".split(): assert s in g, s
    assert 'a' not in g
    assert g['b']() is None
finally: shutil.rmtree('tmp')