开发扩展#
JupyterLab 应用程序由一个核心应用程序对象和一组扩展组成。JupyterLab 扩展提供了 JupyterLab 中的几乎所有功能,包括笔记本、文档编辑器和查看器、代码控制台、终端、主题、文件浏览器、上下文帮助系统、调试器和设置编辑器。扩展甚至提供了应用程序更基础的部分,例如菜单系统、状态栏以及与服务器的底层通信机制。
JupyterLab 扩展是一个包含多个 JupyterLab 插件的包。我们将讨论如何编写插件,然后如何将一组插件打包成一个 JupyterLab 扩展。
有关更详细的信息,请参阅以下章节,或浏览本页的其余部分以获取概述。
其他资源#
在我们开始之前,这里有一些用于实际操作练习或更深入参考文档的资源。
教程#
通过这些指南学习如何编写 JupyterLab 扩展
扩展教程:学习如何制作一个简单的 JupyterLab 扩展的教程。
使扩展兼容多个应用程序教程:一个用于制作在 JupyterLab、Jupyter Notebook 7+ 及更多应用程序中都能工作的扩展的教程
JupyterLab 扩展示例仓库:一系列简短的教程,通过示例学习如何为 JupyterLab 开发扩展。
常见扩展点:JupyterLab 最常见扩展点列表。
用应用程序插件扩展 JupyterLab 文档部件的另一个常见模式在文档中有所涵盖。
扩展模板#
我们提供了一些模板来创建 JupyterLab 扩展
extension-template:使用 copier 创建 JupyterLab 扩展
[已弃用] extension-cookiecutter-ts:使用 cookiecutter 创建 JupyterLab 扩展(请改用 copier 模板)。
API 参考文档#
这里有一些 JupyterLab 和 Lumino 包的自动生成 API 文档
扩展概述#
JupyterLab 插件是 JupyterLab 可扩展性的基本单元。扩展是一个包含一个或多个 JupyterLab 插件的包。扩展可以通过两种方式分发
一个 预构建扩展 (自 JupyterLab 3.0 起) 分发一个从源扩展预构建的 JavaScript 代码包,该包无需重新构建 JupyterLab 即可加载到 JupyterLab 中。在这种情况下,扩展开发人员使用 JupyterLab 提供的工具将源扩展编译成一个 JavaScript 包,其中包含非 JupyterLab 的 JavaScript 依赖项,然后将生成的包分发到例如 Python pip 或 conda 包中。安装预构建扩展不需要 Node.js。
[已弃用] 一个 源扩展 是一个导出一个或多个插件的 JavaScript (npm) 包。安装源扩展需要用户重新构建 JupyterLab。此重新构建步骤需要 Node.js,并且可能占用大量时间和内存,因此一些用户可能无法安装源扩展。但是,与使用预构建扩展相比,交付给用户浏览器的 JupyterLab 代码的总大小可能会减少。有关安装源扩展时重新构建 JupyterLab 的技术原因,请参阅依赖项去重。
一个扩展可以同时作为 NPM 上的源扩展和预构建扩展(例如,作为 Python 包发布)发布。在某些情况下,系统管理员甚至可以选择通过将预构建包直接复制到适当的目录来安装预构建扩展,从而避免创建 Python 包。如果 JupyterLab 中安装了同名的源扩展和预构建扩展,则预构建扩展优先。
由于预构建扩展不需要重新构建 JupyterLab,因此它们在 JupyterLab 安装在系统级别的多用户系统中具有独特的优势。在此类系统上,只有系统管理员才有权限重新构建 JupyterLab 和安装源扩展。由于预构建扩展可以按用户级别、按环境级别或系统级别安装,因此每个用户都可以拥有自己独立的预构建扩展集,这些扩展会在其浏览器中动态加载到系统范围的 JupyterLab 之上。
警告
您的扩展可能会因 JupyterLab 的新版本而损坏。正如向后兼容性、版本和破坏性更改中所述,JupyterLab 的开发和发布周期遵循语义版本控制,因此我们建议您规划开发过程以应对可能在将来发生的破坏性更改,这些更改可能会影响您的扩展用户。请考虑在您的项目中向用户记录您的维护计划,或在您的项目包元数据中设置您的扩展兼容的 JupyterLab 版本的上限。
提示
我们建议以 Python 包的形式发布预构建扩展,以方便用户。
插件#
JupyterLab 插件是 JupyterLab 可扩展性的基本单元。JupyterLab 支持多种类型的插件
应用程序插件: 应用程序插件是 JupyterLab 功能的基本构建块。应用程序插件通过请求其他插件提供的服务并可选地向系统提供自己的服务来与 JupyterLab 和其他插件交互。核心 JupyterLab 中的应用程序插件包括主菜单系统、文件浏览器以及笔记本、控制台和文件编辑器组件。
MIME 渲染器插件: MIME 渲染器插件是一种简化、受限的方式,用于扩展 JupyterLab 以在笔记本和文件中渲染自定义 MIME 数据。当这些插件加载时,JupyterLab 会自动将它们转换为等效的应用程序插件。核心 JupyterLab 中提供的 MIME 渲染器插件示例包括 PDF 查看器、JSON 查看器和 Vega 查看器。
主题插件: 主题插件通过更改可主题化值(即 CSS 变量值)并为 JupyterLab 提供额外的字体和图形来定制 JupyterLab 的外观。JupyterLab 附带浅色和深色主题插件。
应用程序插件#
应用程序插件是一个具有许多元数据字段的 JavaScript 对象。一个典型的应用程序插件在 TypeScript 中可能如下所示
const plugin: JupyterFrontEndPlugin<MyToken> = {
id: 'my-extension:plugin',
description: 'Provides a new service.',
autoStart: true,
requires: [ILabShell, ITranslator],
optional: [ICommandPalette],
provides: MyToken,
activate: activateFunction
};
id
和 activate
字段是必需的,其他字段可以省略。有关如何使用 requires
、optional
或 provides
字段的更多信息,请参阅插件之间的交互。
id
是一个必需的唯一字符串。约定是使用 NPM 扩展包名,一个冒号,然后是一个标识扩展内部插件的字符串。description
是一个可选字符串。它允许记录插件的用途。autostart
指示您的插件是否应在应用程序启动时激活。通常这应该为true
。如果为false
或省略,您的插件将在任何其他插件请求您的插件提供的令牌时激活。requires
和optional
是对应于其他插件提供的服务的令牌列表。当插件激活时,这些服务将作为参数传递给activate
函数。如果requires
服务未在 JupyterLab 中注册,将抛出错误并且插件不会被激活。provides
是与您的插件向系统提供的服务关联的令牌。如果您的插件不向系统提供服务,请省略此字段并且不要从您的activate
函数返回值。activate
是当您的插件激活时调用的函数。参数依次是应用程序对象、对应于requires
令牌的服务,然后是对应于optional
令牌的服务(如果特定optional
令牌未在系统中注册,则为null
)。如果给定provides
令牌,则activate
函数的返回值(如果返回 promise,则为已解析的返回值)将注册为与该令牌关联的服务。
应用程序对象#
Jupyter 前端应用程序对象作为其第一个参数传递给插件的 activate
函数。应用程序对象具有许多用于与应用程序交互的属性和方法,包括
commands
- 用于在应用程序中添加和执行命令的可扩展注册表。docRegistry
- 包含应用程序能够读取和渲染的文档类型的可扩展注册表。restored
- 当应用程序加载完成时解析的 promise。serviceManager
- 用于与 Jupyter REST API 通信的低级管理器。shell
- 一个通用的 Jupyter 前端 shell 实例,它包含应用程序的用户界面。有关更多详细信息,请参阅Jupyter 前端 Shell。
有关 JupyterFrontEnd
类的更多详细信息,请参阅 JupyterLab API 参考文档。
插件之间的交互#
(此处解释了生产者-消费者模式)
JupyterLab 插件系统的基本功能之一是应用程序插件可以通过向系统提供服务和请求其他插件提供的服务来与其他插件交互。服务可以是任何 JavaScript 值,通常是一个具有方法和数据属性的 JavaScript 对象。例如,提供 JupyterLab 主菜单的核心插件向系统提供一个主菜单服务对象,该对象具有添加新顶级菜单的方法和与现有顶级应用程序菜单交互的属性。
在接下来的讨论中,向系统提供服务的插件是 提供者 插件,请求和使用服务的插件是 消费者 插件。请注意,这些 提供者 和 消费者 插件是 JupyterLab 生产者-消费者模式(一种依赖注入模式)的基本组成部分。
令牌#
插件提供的服务由一个 令牌 标识,即 Lumino Token 类的一个具体实例。提供者插件在其插件元数据 provides
字段中列出令牌,并从其 activate
函数返回关联的服务。
消费者插件导入令牌(例如,从提供者插件的扩展 JavaScript 包,或从为提供者和消费者导出令牌的第三方包),并在其插件元数据 requires
或 optional
字段中列出令牌。当 JupyterLab 通过调用其 activate
函数实例化消费者插件时,它将把与令牌关联的服务作为参数传递。如果服务不可用(即,令牌未在 JupyterLab 中注册),则 JupyterLab 将抛出错误并且不激活消费者(如果令牌列在 requires
中),或者将相应的 activate
参数设置为 null
(如果令牌列在 optional
中)。JupyterLab 会对插件激活进行排序,以确保服务的提供者在其消费者之前激活。一个令牌只能在系统中注册一次。
当服务对其消费者不关键,但如果可用则会很好时,消费者可能会将令牌列为 optional
。例如,消费者可能会将状态栏服务列为可选,以便在状态栏可用时向其添加指示器,但仍然使运行没有状态栏的自定义 JupyterLab 分布的用户能够使用消费者插件。
在 TypeScript 中定义的令牌也可以为与令牌关联的服务定义一个 TypeScript 接口。如果使用令牌的包使用 TypeScript,则在将包编译为 JavaScript 时,将根据此接口对服务进行类型检查。
注意
JupyterLab 使用令牌(例如,而不是字符串)来标识服务,以防止标识符之间发生冲突并在使用 TypeScript 时启用类型检查。
发布令牌#
由于消费者需要导入提供者使用的令牌,因此令牌应在已发布的 JavaScript 包中导出。JupyterLab 中需要对令牌进行去重——有关更多详细信息,请参阅依赖项去重。
JupyterLab 核心中的一种模式是,从提供者和消费者扩展都导入的第三方包中创建并导出令牌,而不是在提供者包中定义令牌。这使得用户可以将提供者扩展替换为提供相同令牌并具有替代服务实现的不同扩展。例如,核心 JupyterLab filebrowser
包导出表示文件浏览器服务(启用与文件浏览器的交互)的令牌。filebrowser-extension
包包含一个在 JupyterLab 中实现文件浏览器并向 JupyterLab 提供文件浏览器服务(由从 filebrowser
包导入的令牌标识)的插件。因此,希望与文件浏览器交互的 JupyterLab 扩展不需要对 filebrowser-extension
包具有 JavaScript 依赖,而只需要从 filebrowser
包导入令牌。此模式使用户能够通过编写自己的扩展,该扩展从 filebrowser
包导入相同的令牌并使用其自己的替代文件浏览器服务将其提供给系统,从而无缝地更改 JupyterLab 中的文件浏览器。
MIME 渲染器插件#
MIME 渲染器插件是一种方便的机制,用于创建一个可以在笔记本和给定 MIME 类型文件中渲染 MIME 数据的插件。MIME 渲染器插件比标准插件更具声明性且更受限制。MIME 渲染器插件是一个具有 rendermime-interfaces IExtension 对象中列出的字段的对象。
例如,JupyterLab 有一个 PDF MIME 渲染器扩展。在核心 JupyterLab 中,它用于查看 PDF 文件和在笔记本中查看 PDF 数据 MIME 数据。
我们有一个MIME 渲染器示例,它详细介绍了如何创建 MIME 渲染器扩展,该扩展将 MP4 视频渲染功能添加到 JupyterLab。扩展模板支持 MIME 渲染器扩展。
MIME 渲染器可以通过调用给定模型上的 .setData()
来更新其数据。例如,这可以用于添加动态图的 png
表示,该表示将被笔记本模型获取并添加到笔记本文档中。当使用 IDocumentWidgetFactoryOptions
时,您可以通过调用 .setData()
并提供渲染 MIME 类型的更新数据来更新文档模型。然后用户可以以通常的方式保存文档。
主题插件#
主题是一种特殊的应用程序插件,它使用 ThemeManager
服务注册主题。主题 CSS 资产专门捆绑在扩展中(参见主题路径),以便在主题激活时可以卸载或加载。由于 style
或 styleModule
键引用的 CSS 文件会自动捆绑并加载到页面上,因此主题文件不应由这些键引用。
包含主题插件的扩展包必须包含其主题 CSS 文件中 @import
引用的所有静态资产。局部 URL 可用于引用相对于引用兄弟 CSS 文件位置的文件。例如,url('images/foo.png')
或 url('../foo/bar.css')
可用于引用主题中的局部文件。绝对 URL(以 /
开头)或外部 URL(例如 https:
)可用于引用外部资产。
有关示例,请参阅JupyterLab 浅色主题。
有关快速开始开发主题插件,请参阅TypeScript 扩展模板(选择 theme
作为 kind
)。
服务管理器插件#
警告
这是一个高级主题。如果您是 JupyterLab 扩展的新手,可以跳过此部分。
服务管理器是 JupyterLab 应用程序的核心组件,也是 Jupyter Server REST API 的接口。在 JupyterLab 4.4.0 之前,服务管理器必须创建为单例对象,并在创建 JupyterLab 应用程序对象时传递。如果某些扩展需要更改服务管理器提供的一些核心服务的行为,这将不方便,因为它们将不得不从头开始构建一个新的 JupyterLab 应用程序。
4.4 版本新增: 服务管理器本身现在是一个插件,可以通过第三方扩展使用 IServiceManager
令牌提供。其底层服务(例如内核管理器和内容管理器)现在也作为插件可用。
服务管理器插件可以通过以下令牌由第三方扩展提供
IConnectionStatus
:连接状态服务。IContentsManager
:内容管理器服务,负责管理文件和目录。IDefaultDrive
:默认驱动器服务,负责提供内容管理器操作的默认驱动器。IServerSettings
:服务器设置服务,定义一组默认服务器设置。IEventManager
:事件管理器服务,用于发出由 Jupyter Server 管理的事件总线广播的事件。IKernelManager
:内核管理器服务。IKernelSpecManager
:内核规范管理器服务。INbConvertManager
:nbconvert 管理器服务,用于各种格式的导出。ISessionManager
:会话管理器服务。ISettingManager
:设置管理器服务,用于管理用户设置。ITerminalManager
:终端管理器服务。IUserManager
:用户管理器服务。IWorkspaceManager
:工作区管理器服务,用于与工作区 API 交互。
以下示例展示了如何提供自定义内容管理器服务,该服务会在控制台中记录所请求内容的路径
import {
Contents,
ContentsManager,
IContentsManager,
ServiceManagerPlugin
} from '@jupyterlab/services';
class CustomContents extends ContentsManager {
async get(
path: string,
options?: Contents.IFetchOptions
): Promise<Contents.IModel> {
console.log('CustomContents.get', path);
return super.get(path, options);
}
}
const plugin: ServiceManagerPlugin<IContentsManager> = {
id: 'my-extension:contents-manager',
autoStart: true,
provides: IContentsManager,
description: 'A JupyterLab extension providing a custom contents manager',
activate: (_: null): Contents.IManager => {
return new CustomContents();
}
};
export default plugin;
警告
请注意使用 ServiceManagerPlugin
来声明插件。ServiceManagerPlugin
与 JupyterFrontEndPlugin
不同之处在于它提供了一个服务管理器插件,该插件将在应用程序设置之前激活。因此,activate
函数的第一个参数为 null
。ServiceManagerPlugin<T>
等效于 IPlugin<null, T>
,其中 T
是插件提供的服务。
源扩展#
源扩展是一个 JavaScript (npm) 包,它导出一个或多个插件。所有 JupyterLab 扩展都以源扩展的形式开发(例如,预构建扩展是从源扩展构建的)。
源扩展在其 package.json
文件的 jupyterlab
字段中包含元数据。JSON schema 用于元数据在 @jupyterlab/builder
包中分发。
我们将在下面讨论源扩展的 package.json
中每个 jupyterlab
元数据字段。
extension
: 应用程序插件mimeExtension
: MIME 渲染器插件themePath
: 主题路径schemaDir
: 插件设置disabledExtensions
: 禁用其他扩展sharedPackages
: 依赖项去重discovery
: 配套包
一个 JupyterLab 扩展必须至少设置 jupyterlab.extension
或 jupyterlab.mimeExtension
中的一个。
应用程序插件#
jupyterlab.extension
字段表示该包导出一个或多个 JupyterLab 应用程序插件。如果主包模块(即 package.json
的 main
键中列出的文件)的默认导出是一个应用程序插件或一个应用程序插件列表,则将值设置为 true
。如果您的插件从不同的模块作为默认导出,请将其设置为模块的相对路径(例如,"lib/foo"
)。示例
"jupyterlab": {
"extension": true
}
MIME 渲染器插件#
jupyterlab.mimeExtension
字段表示包导出 mime 渲染器插件。与 jupyterlab.extension
字段类似,其值可以是布尔值(表示 mime 渲染器插件或 mime 渲染器插件列表是 main
字段的默认导出),也可以是字符串,该字符串是导出(作为默认导出)一个或多个 mime 渲染器插件的模块的相对路径。
主题路径#
主题插件资产(例如,CSS 文件)需要与典型应用程序插件的资产单独捆绑,以便在主题激活或停用时可以加载和卸载。如果扩展导出主题插件,则应在 jupyterlab.themePath
字段中给出主题资产的相对路径
"jupyterlab": {
"extension": true,
"themePath": "style/theme.css"
}
一个扩展不能捆绑多个主题插件。
确保主题路径文件包含在 package.json
的 files
元数据中。如果要使用 SCSS、SASS 或 LESS 文件,则必须将它们编译为 CSS,并将 jupyterlab.themePath
指向 CSS 文件。
插件设置#
JupyterLab 开放了一个插件设置系统,可用于提供默认设置值和用户覆盖。插件的设置由 JSON schema 文件指定。package.json
中的 jupyterlab.schemaDir
字段给出包含插件设置 schema 文件的目录的相对位置。
设置系统依赖于遵循 <source_package_name>:<plugin_name>
约定的插件 ID。plugin_name
插件的设置 schema 文件是 <schemaDir>/<plugin_name>.json
。
例如,JupyterLab 的 filebrowser-extension
包导出了 @jupyterlab/filebrowser-extension:browser
插件。在 @jupyterlab/filebrowser-extension
的 package.json
中,我们有
"jupyterlab": {
"schemaDir": "schema",
}
文件浏览器设置架构文件(指定文件浏览器的一些默认键盘快捷键和其他设置)位于 schema/browser.json
中(参见此处)。
有关使用设置的扩展的另一个示例,请参阅fileeditor-extension。
请确保将 schema 文件包含在 package.json
的 files
元数据中。
在声明对 JupyterLab 包的依赖时,请在包版本前使用 ^
运算符,以便构建系统为给定的主要版本安装最新的补丁或次要版本。例如,^4.0.0
将安装版本 4.0.0、4.0.1、4.1.0 等。
系统管理员或用户可以使用 overrides.json 文件覆盖插件设置 schema 文件中提供的默认值。
禁用其他扩展#
jupyterlab.disabledExtensions
字段给出了当此扩展安装时要禁用的扩展或插件列表,其语义与 page_config.json 的 disabledExtensions
字段相同。如果您的扩展覆盖了内置扩展,则此功能很有用。例如,如果一个扩展替换了 @jupyterlab/filebrowser-extension:share-file
插件以覆盖文件浏览器中的“复制可共享链接”功能,则它可以自动禁用 @jupyterlab/filebrowser-extension:share-file
插件
"jupyterlab": {
"disabledExtensions": ["@jupyterlab/filebrowser-extension:share-file"]
}
要禁用扩展中的所有插件,请给出扩展包名,例如,上述示例中的 "@jupyterlab/filebrowser-extension"
。
依赖项去重#
jupyterlab.sharedPackages
字段控制依赖项如何与预构建扩展捆绑、共享和去重。
JupyterLab 扩展系统中一个重要的问题和挑战是去重扩展的依赖项,而不是让扩展使用自己的捆绑依赖项副本。例如,JupyterLab 依赖的用于应用程序之间通信的 Lumino 部件系统要求所有包使用 @lumino/widgets
包的相同副本。令牌识别插件服务也需要在服务的提供者和消费者之间共享,因此导出令牌的依赖项需要去重。
JupyterLab 在源扩展安装期间重新构建自身时,会自动去重源扩展之间的整个依赖树。源扩展和预构建扩展之间,或预构建扩展本身之间的去重是一个更细微的问题(对于那些好奇实现细节的人来说,JupyterLab 中的这种去重由 Webpack 5.0 模块联邦系统提供支持)。JupyterLab 为预构建扩展的依赖项去重提供了合理的默认策略。jupyterlab.sharedPackages
对象在扩展的 package.json
中,允许扩展作者使用三个布尔选项修改给定依赖项的默认去重策略。此对象的键是依赖项包名,值要么是 false
(表示不应共享/去重该依赖项),要么是具有最多三个字段的对象
bundled
:如果为true
(默认值),则依赖项与扩展捆绑在一起,并作为 JupyterLab 可用的副本之一提供。如果为false
,则依赖项不与扩展捆绑在一起,因此扩展将使用来自不同扩展的依赖项版本。singleton
:如果为true
,则扩展将始终优先使用其他扩展正在使用的依赖项副本,而不是使用可用的最高版本。默认值为false
。strictVersion
:如果为true
,扩展将始终确保其使用的依赖项副本满足其所需的依赖项版本范围。
默认情况下,JupyterLab 对预构建扩展的直接依赖项与其他源和预构建扩展的直接依赖项进行去重,选择 JupyterLab 可用的最高版本的依赖项。JupyterLab 在使用核心 JupyterLab 包中的令牌和服务时选择合理的默认选项。我们建议在使用核心 JupyterLab 包以外的包提供的令牌时使用以下 sharedPackages
配置(有关使用令牌的更多详细信息,请参阅插件之间的交互)。
提供服务#
当一个扩展(“提供者”)正在提供一个由从依赖项 token-package
导入的令牌标识的服务时,提供者应该将该依赖项配置为单例。这确保提供者使用与其他人导入的令牌相同的令牌来标识服务。如果 token-package
不是核心包,它将与提供者捆绑在一起,并可供消费者在需要该服务时导入。
"jupyterlab": {
"sharedPackages": {
"token-package": {
"singleton": true
}
}
}
请求服务#
当一个扩展(“消费者”)需要另一个扩展(“提供者”)提供的服务时,该服务由从一个包(token-package
,可能与提供者相同)导入的令牌标识,消费者应该将依赖项 token-package
配置为单例,以确保消费者获取与提供者用于标识服务的令牌完全相同的令牌。此外,由于提供者提供了 token-package
的副本,消费者可以将其从自己的包中排除。
"jupyterlab": {
"sharedPackages": {
"token-package": {
"bundled": false,
"singleton": true
}
}
}
可选使用服务#
当一个扩展(“消费者”)可选地使用由从一个包(token-package
)导入的令牌标识的服务时,不能保证提供者会可用并捆绑 token-package
。在这种情况下,消费者只应将 token-package
配置为单例
"jupyterlab": {
"sharedPackages": {
"token-package": {
"singleton": true
}
}
}
配套包#
如果您的扩展依赖于内核中的一个或多个包,或者依赖于笔记本服务器扩展,您可以添加元数据以向扩展管理器指示这一点,方法是将元数据添加到您的 package.json 文件中。可用的完整选项是
"jupyterlab": {
"discovery": {
"kernel": [
{
"kernel_spec": {
"language": "<regexp for matching kernel language>",
"display_name": "<regexp for matching kernel display name>" // optional
},
"base": {
"name": "<the name of the kernel package>"
},
"overrides": { // optional
"<manager name, e.g. 'pip'>": {
"name": "<name of kernel package on pip, if it differs from base name>"
}
},
"managers": [ // list of package managers that have your kernel package
"pip",
"conda"
]
}
],
"server": {
"base": {
"name": "<the name of the server extension package>"
},
"overrides": { // optional
"<manager name, e.g. 'pip'>": {
"name": "<name of server extension package on pip, if it differs from base name>"
}
},
"managers": [ // list of package managers that have your server extension package
"pip",
"conda"
]
}
}
}
例如,一个基于 jupyter-widget 的包的典型设置将是
"keywords": [
"jupyterlab-extension",
"jupyter",
"widgets",
"jupyterlab"
],
"jupyterlab": {
"extension": true,
"discovery": {
"kernel": [
{
"kernel_spec": {
"language": "^python",
},
"base": {
"name": "myipywidgetspackage"
},
"managers": [
"pip",
"conda"
]
}
]
}
}
目前支持的包管理器是 pip
和 conda
。
扩展 CSS#
如果您的扩展在 package.json
中有一个顶层 style
键,则它指向的 CSS 文件将自动包含在页面中。
JupyterLab 中用于页面 CSS 去重的一种约定是,如果您的扩展在 package.json
中有一个顶层 styleModule
键,给出一个可以导入的 JavaScript 模块,则将导入它(作为 JavaScript 模块),而不是将 style
键 CSS 文件作为 CSS 文件导入。
预构建扩展#
package.json 元数据#
除了源扩展的包元数据之外,预构建扩展还具有额外的 jupyterlab
元数据。
outputDir
: 输出目录webpackConfig
: 自定义 webpack 配置
输出目录#
当 JupyterLab 构建预构建扩展时,它会创建一个文件目录,然后可以将这些文件复制到适当的安装位置。jupyterlab.outputDir
字段给出了这些 JavaScript 和其他文件应放置的目录的相对路径。一个包含额外构建元数据的 package.json
文件的副本将放置在 outputDir
中,并且将提供服务的 JavaScript 和其他文件将放置在 static
子目录中。
"jupyterlab": {
"outputDir": "mypackage/labextension"
}
自定义 webpack 配置#
警告
此功能是 实验性的,可能会在不另行通知的情况下更改,因为它暴露了内部实现细节(即 webpack)。请谨慎使用,因为配置错误可能会破坏预构建扩展系统。
预构建扩展系统使用 Webpack 模块联邦系统。通常这是一个实现细节,预构建扩展作者不需要担心,但偶尔扩展作者会希望调整用于构建其扩展的配置,以启用各种 webpack 功能。扩展作者可以指定一个自定义 webpack 配置文件,该文件将与预构建扩展系统生成的 webpack 配置合并,使用 package.json
中的 jupyterlab.webpackConfig
字段。该值应该是配置文件的相对路径
"jupyterlab": {
"webpackConfig": "./webpack.config.js"
}
自定义 webpack 配置可用于启用 webpack 功能、配置额外的文件加载器以及许多其他用途。这是一个 webpack.config.js
自定义配置示例,它启用了 webpack 的异步 WebAssembly 和顶级 await
实验性功能
module.exports = {
experiments: {
topLevelAwait: true,
asyncWebAssembly: true,
}
};
此自定义配置将在构建预构建扩展时与预构建扩展配置合并。
开发预构建扩展#
使用 jupyter labextension build
命令构建预构建扩展。此命令使用活动 JupyterLab 中的依赖项元数据,从源扩展生成一组文件,这些文件构成了预构建扩展。这些文件包括一个主入口点 remoteEntry.<hash>.js
、捆绑到 JavaScript 文件中的依赖项、package.json
(带有一些额外的构建元数据),以及所需的插件设置和主题目录结构。
在编写预构建扩展时,您可以使用 labextension develop
命令创建指向您的预构建输出目录的链接,类似于 pip install -e
jupyter labextension develop . --overwrite
然后重新构建您的扩展并在浏览器中刷新 JupyterLab 应该会接收到您的预构建扩展源代码中的更改。
如果使用 Windows,您可能需要配置操作系统以使上述 develop
命令工作,请参阅注释:Windows 用户须知
如果您正在针对 JupyterLab 源代码仓库开发预构建扩展,您可以使用 jupyter lab --dev-mode --extensions-in-dev-mode
运行 JupyterLab,以使开发版本的 JupyterLab 加载预构建扩展。最好您记住您的扩展所依赖的 JupyterLab 包可能与已发布的包不同;这意味着您的扩展不是使用您的 node_modules 文件夹中的 JupyterLab 依赖项构建的,而是使用 JupyterLab 源代码中的依赖项构建的。
如果您正在使用 TypeScript,TypeScript 编译器会抱怨,因为您的扩展依赖项可能与 JupyterLab 中的不同。因此,您需要通过添加 paths 选项,将搜索这些依赖项的路径添加到您的 tsconfig.json
中
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@jupyterlab/*": ["../jupyterlab/packages/*"],
"*": ["node_modules/*"]
}
},
}
添加路径以查找 JupyterLab 依赖项时,可能会导致项目中其他依赖项(如 lumino 或 react)出现问题,因为 JupyterLab 包将从 JupyterLab 的 node_modules
文件夹获取其依赖项。相比之下,您的包将从您的 node_modules
文件夹获取。为了解决这个问题,您需要将冲突的依赖项添加到 package.json
中的 resolutions
。这样,两个项目(JupyterLab 和您的扩展)都使用相同版本的重复依赖项。
我们提供了一个扩展模板,它为扩展作者处理所有脚手架,包括分发适当的数据文件,以便当用户安装包时,预构建扩展最终位于 share/jupyter/labextensions
分发预构建扩展#
预构建扩展可以通过任何能够将预构建资产复制到 JupyterLab 可以找到的适当位置的系统进行分发。官方的扩展模板展示了如何通过 Python pip 或 conda 包分发预构建扩展。系统包管理器,甚至只是一个复制目录的管理脚本,也可以使用。
要分发预构建扩展,请将其输出目录复制到 JupyterLab 将找到它的位置,通常是 <sys-prefix>/share/jupyter/labextensions/<package-name>
,其中 <package-name>
是 package.json
中的 JavaScript 包名。例如,如果您的 JavaScript 包名是 @my-org/my-package
,则相应的目录将是 <sys-prefix>/share/jupyter/labextensions/@my-org/my-package
。
JupyterLab 服务器通过 /labextensions/
服务器处理程序提供 static/
文件。服务器中的设置和主题处理程序也从预构建扩展目录加载设置和主题。如果预构建扩展与源扩展同名,则预构建扩展优先。
打包信息#
由于预构建扩展以多种方式分发(Python pip 包、conda 包,以及可能以许多其他打包系统分发),因此预构建扩展目录可以包含一个额外的文件 install.json
,它有助于用户了解预构建扩展是如何安装的以及如何卸载它。此文件应由分发预构建扩展的打包系统复制到顶层目录中,例如 <sys-prefix>/share/jupyter/labextensions/<package-name>/install.json
。
这个 install.json
文件被 JupyterLab 用来帮助用户了解如何管理扩展。例如,jupyter labextension list
包含此文件中的信息,jupyter labextension uninstall
可以打印有用的卸载说明。这是一个 install.json
文件的示例
{
"packageManager": "python",
"packageName": "mypackage",
"uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package mypackage"
}
packageManager
:这是用于安装预构建扩展的包管理器,例如python
、pip
、conda
、debian
、system administrator
等。packageName
:这是上述包管理器中预构建扩展的包名,可能与package.json
中的包名不同。uninstallInstructions
:这是一小段文本,为用户提供卸载预构建扩展的说明。例如,它可能指示他们使用系统包管理器或与系统管理员交谈。
PyPI Trove 分类器#
作为 Python 包分发的扩展可以以 trove 分类器的形式声明附加元数据。这些分类器改进了用户在 PyPI 上的浏览体验。虽然包含许可证、开发状态、支持的 Python 版本和其他主题分类器对许多受众都很有用,但以下分类器特定于 Jupyter 和 JupyterLab。
Framework :: Jupyter
Framework :: Jupyter :: JupyterLab
Framework :: Jupyter :: JupyterLab :: 1
Framework :: Jupyter :: JupyterLab :: 2
Framework :: Jupyter :: JupyterLab :: 3
Framework :: Jupyter :: JupyterLab :: 4
Framework :: Jupyter :: JupyterLab :: Extensions
Framework :: Jupyter :: JupyterLab :: Extensions :: Mime Renderers
Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt
Framework :: Jupyter :: JupyterLab :: Extensions :: Themes
在您的 setup.py
、setup.cfg
或 pyproject.toml
中包含每个相关的分类器(及其父级),以帮助向潜在用户描述您的包提供了什么。特别是 Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt
被扩展管理器用来从 PyPI.org 获取可用扩展。
提示
例如,一个仅与 JupyterLab 3 兼容并作为即用型预构建扩展分发的主题可能如下所示
# setup.py
setup(
# the rest of the package's metadata
# ...
classifiers=[
"Framework :: Jupyter",
"Framework :: Jupyter :: JupyterLab",
"Framework :: Jupyter :: JupyterLab :: 3",
"Framework :: Jupyter :: JupyterLab :: Extensions",
"Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt",
"Framework :: Jupyter :: JupyterLab :: Extensions :: Themes",
]
)
例如,这可以通过PyPI 主题扩展搜索发现。
源扩展的开发工作流程#
开发预构建扩展通常要容易得多,因为它们不需要重新构建 JupyterLab 即可查看更改。如果您需要开发源扩展,这里有一些开发工作流程的技巧。
在编写源扩展时,您可以使用命令
jlpm install # install npm package dependencies
jlpm run build # optional build step if using TypeScript, babel, etc.
jupyter labextension install # install the current directory as an extension
这会导致构建器在构建应用程序文件之前重新安装源文件夹。您可以随时使用 jupyter lab build
重新构建,它将重新安装这些包。
您还可以使用 jupyter labextension link
链接您正在同时处理的其他本地 npm
包;它们将被重新安装,但不会被视为扩展。本地扩展和链接包包含在 jupyter labextension list
中。
当使用本地扩展和链接包时,您可以运行命令
jupyter lab --watch
这将导致应用程序在其中一个链接包发生更改时进行增量重新构建。请注意,只有已编译的 JavaScript 文件(和 CSS 文件)受 WebPack 进程监视。这意味着如果您的扩展是 TypeScript,您必须先运行 jlpm run build
,更改才能反映在 JupyterLab 中。为了避免此步骤,您还可以在扩展中监视 TypeScript 源,这通常分配给 tsc -w
快捷方式。如果 webpack 似乎没有检测到更改,这可能与可用监视器数量不足有关。
请注意,应用程序是根据核心 JupyterLab 扩展的 已发布 版本构建的。您应该使用 ^
运算符指定版本,例如 ^4.0.0
,以便构建系统可以使用特定主要版本的较新次要版本和补丁版本。如果您的扩展依赖于 JupyterLab 包,则它应与 jupyterlab/static/package.json
文件中的依赖项兼容。请注意,构建将始终使用满足 JupyterLab 本身和任何已安装扩展的依赖项要求的最新 JavaScript 包。如果您希望针对核心 JupyterLab 包之一的特定补丁版本进行测试,您可以暂时将该要求固定到您自己的依赖项中的特定版本。
如果您想针对 JupyterLab 的未发布版本测试源扩展,您可以运行命令
jupyter lab --watch --splice-source
此命令会将本地 packages
目录拼接进应用程序目录,从而允许您根据当前开发源构建源扩展。要静态构建拼接源,请使用 jupyter lab build --splice-source
。一旦创建了拼接构建,后续对 jupyter labextension build 的调用将默认处于拼接模式。可以通过调用 jupyter labextension build --splice-source
来强制进行拼接构建。请注意,针对开发版本的 JupyterLab 开发预构建扩展通常比源包构建容易得多。
该包应该导出与 ECMAScript 6 兼容的 JavaScript。它可以使用 require('foo.css')
语法导入 CSS。CSS 文件还可以使用 @import url('~foo/index.css')
语法从其他包导入 CSS,其中 foo
是包的名称。
还支持以下文件类型(JavaScript 和 CSS 中均支持):json
、html
、jpg
、png
、gif
、svg
、js.map
、woff2
、ttf
、eot
。
如果您的包使用任何其他文件类型,则必须将其转换为上述类型之一,或者在导入语句中包含加载器。如果您包含加载器,则该加载器必须在构建时可导入,因此如果它尚未由 JupyterLab 安装,则必须将其添加为您的扩展的依赖项。
如果您的 JavaScript 是用 EMCAScript 6 (2015) 以外的方言编写的,则应使用适当的工具进行转换。您可以使用 Webpack 预构建您的扩展,以使用其构建配置中未启用的任何功能。要构建兼容包,请在您的 Webpack 配置中将 output.libraryTarget
设置为 "commonjs2"
。(参见此示例仓库)。
如果您在 npm.org
上发布您的扩展,用户将能够通过简单的 jupyter labextension install <foo>
来安装它,其中 <foo>
是已发布 npm
包的名称。您也可以提供一个脚本,针对用户机器上的本地文件夹路径或提供的 tarball 运行 jupyter labextension install
。任何有效的 npm install
指定符都可以在 jupyter labextension install
中使用(例如 foo@latest
、bar@3.0.0.0
、path/to/folder
和 path/to/tar.gz
)。
我们鼓励扩展作者将 jupyterlab-extension GitHub 主题 添加到任何 GitHub 扩展仓库中。
测试您的扩展#
注意
我们强烈建议使用扩展模板来设置测试配置。
此仓库中的 testutils
中有许多辅助函数(这是一个名为 @jupyterlab/testutils
的公共 npm
包),可以在为扩展编写测试时使用。有关运行测试所需的基础设施示例,请参阅 tests/test-application
。
如果您正在使用 jest 来测试您的扩展,您将需要将 jupyterlab 包转译为 commonjs
,因为它们使用的是 node
不支持的 ES6 模块。
要转译 jupyterlab 包,您需要安装以下包
jlpm add --dev jest @types/jest ts-jest @babel/core@^7 @babel/preset-env@^7
然后在 jest.config.js
中,您将指定对 js 文件使用 babel,并忽略除 ES6 模块之外的所有 node 模块
const jestJupyterLab = require('@jupyterlab/testutils/lib/jest-config');
const esModules = ['@jupyterlab/'].join('|');
const baseConfig = jestJupyterLab(__dirname);
module.exports = {
...baseConfig,
automock: false,
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
'!src/**/.ipynb_checkpoints/*'
],
coverageReporters: ['lcov', 'text'],
testRegex: 'src/.*/.*.spec.ts[x]?$',
transformIgnorePatterns: [
...baseConfig.transformIgnorePatterns,
`/node_modules/(?!${esModules}).+`
]
};
最后,您需要使用包含以下内容的 babel.config.js
文件来配置 babel
module.exports = require('@jupyterlab/testutils/lib/babel.config');