创建新的插件来扩展 CLI#

虽然 JupyterLite 应用程序的大部分行为可以通过 扩展 进行配置或修改,但这可能不足以满足所有需求。还可以通过插件来扩展底层的 jupyter lite CLI

自定义插件可以对构建的 lite 应用程序的输出文件夹进行任何操作,还可以修改其他插件的行为,包括构成核心 API 的插件。

一些用例

  • 提供复杂的 前端扩展

  • 可预测地修补构建的应用程序中的文件

  • 代码风格检查、测试、压缩或其他验证和优化技术

注意

选择插件是为了将这些部分与 前端 的基于浏览器的插件扩展区分开来,所有 jupyter lite 核心行为都是作为插件实现的。

CLI 架构#

在深入研究插件构建之前,值得了解它们在 CLI 的整体结构中的位置。

为了从多个来源下载、解压缩和更新静态文件和配置,CLI 使用了多个层。

组件

示例

角色

应用

LiteBuildApp

加载配置并解析 CLI 参数

管理器

LiteManager

加载 Addons,运行 doit

Addon

StaticAddon

生成任务计划,并实现操作

钩子

init

收集逻辑生命周期任务

阶段

pre_init

对任务进行细粒度排序

任务

init:static:unpack

具有任务和文件依赖关系的操作集

操作

_unpack_stdlib

实际移动和更新文件

Addon 结构#

最简单的情况下,Addon 使用类似于以下签名的方式进行初始化

class MyAddon:
    __all__ = ["status"]

    def status(self, maanger):
        yield dict(name="hello", actions=[lambda: print("world")])
  • __all__ 成员列出了 Addon 实现的钩子

    • 钩子也可以以 pre_post_ 阶段为前缀

  • 钩子实现,如广告所示

值得注意的是

  • status 阶段不应该有任何副作用

  • init 阶段主要保留用于“黄金母版”内容

  • build 主要保留用于用户编写的內容

提示

请参阅此 JupyterLite 存储库中的现有示例,以了解其他钩子实现。

生成任务#

每个钩子实现都应该返回一个可迭代的 doit 任务,其最小形式为

def post_build(manager):
    yield dict(
        name="a:unique:name", # will have the Addon, and maybe a prefix, prepended
        actions=[["things", "to", "do"]],
        file_dep=["a-file", Path("another-file")],
        targets=["an-output-file"],
    )

应用程序级任务已经根据其 钩子父级 配置了 doit.create_after,这意味着任务可以自信地依赖于其父级(由任何其他 addons)中已存在的文件。

虽然不是必需的,但拥有准确的 file_deptargets 有助于确保构建的应用程序始终处于一致状态,无需大量返工。

BaseAddon#

一个便利类,jupyterlite_core.addons.base.BaseAddon 可以扩展以提供许多有用的功能。它扩展了 traitlets.LoggingConfigurable,并将 LiteManager 设为Addonparent,允许它通过 jupyter_lite_config.json 按名称配置

{
  "LiteBuildConfig": {
    "ignore_sys_prefix": true
  },
  "MyAddon": {
    "enable_some_feature": true
  }
}

… 或者通过命令行。

jupyter lite build --MyAddon.enable_some_feature=True

简短 CLI#

继承自 BaseAddon(或以其他方式继承自 traitlets.Configurable)的Addon可以告诉父应用程序它公开了额外的 CLI 别名标志,用于执行和使用 --help 查询。

提示

Addons 作者鼓励使用通用前缀对别名和标志进行分组。

别名#

别名将 CLI 参数映射到单个特征。

from traitlets import Int

class MyFooAddon(BaseAddon):
    __all__ = ["status"]
    aliases = {
      "how-many-foos": "MyFooAddon.foo",
    }
    foo = Int(0, help="The number of foos").tag(config=True)
    # ...

警告

Addons 不能覆盖核心别名或先前加载的 addons 的别名。

标志#

标志将 CLI 参数映射到任何数量的 traitlets.Configurable 类上的任何数量的特征

from traitlets import Int, Bool

class MyFooBarAddon(BaseAddon):
    __all__ = ["status"]
    flags = {
      "foo-bar": (
        {"MyFooBarAddon": {"foo": 1, "bar": True}},
        "Foo once, and bar",
      )
    }
    foo = Int(0, help="The number of foos").tag(config=True)
    bar = Bar(False, help="Whether to bar").tag(config=True)
    # ...

注意

Addons 可以增强现有标志的行为,但不能覆盖先前注册的配置值。帮助文本将附加一个换行符。

打包#

扩展 通过 entry_points 进行广告,例如在 pyproject.toml 中。

[project.entry-points."jupyterlite.addon.v0"]
my-unique-addon = "my_module:MyAddon"

一般指南#

  • 值得看看 BaseAddon 及其子类如何处理某些任务。

  • 牢记可重复性,自由地使用缓存,并利用 file_depstargetsuptodate 来保持构建速度。