开发扩展#

JupyterLab 应用程序由一个核心应用程序对象和一组扩展组成。JupyterLab 扩展提供了 JupyterLab 中的几乎所有功能,包括笔记本、文档编辑器和查看器、代码控制台、终端、主题、文件浏览器、上下文帮助系统、调试器和设置编辑器。扩展甚至提供了应用程序的更基本部分,例如菜单系统、状态栏以及与服务器的底层通信机制。

JupyterLab 扩展是一个包含多个 JupyterLab 插件的包。我们将讨论如何编写一个插件,然后如何将一组插件打包成一个 JupyterLab 扩展。

请参阅以下部分以获取更详细的信息,或浏览本页其余部分以获取概述。

警告

您的扩展可能会随着JupyterLab的新版本发布而中断。如向后兼容性、版本和破坏性更改中所述, JupyterLab的开发和发布周期遵循语义版本控制,因此我们建议您在开发过程中考虑到未来可能的破坏性更改,这些更改可能会影响使用您扩展的用户。 考虑在您的项目中向用户记录您的维护计划,或在项目的包元数据中设置您的扩展兼容的JupyterLab版本的上限。

其他资源#

在我们开始之前,这里有一些用于实践或更深入参考文档的资源。

教程#

通过这些指南学习如何编写JupyterLab扩展:

扩展模板#

我们提供了几个模板来创建JupyterLab扩展:

API参考文档#

这里是JupyterLab和Lumino包的一些自动生成的API文档:

扩展概述#

JupyterLab 插件是 JupyterLab 中可扩展性的基本单元。扩展是一个包含一个或多个 JupyterLab 插件的包。扩展可以通过两种方式分发:

  • 一个预构建扩展(自JupyterLab 3.0起)分发了一组从源扩展预构建的JavaScript代码,可以在不重新构建JupyterLab的情况下加载到JupyterLab中。在这种情况下,扩展开发者使用JupyterLab提供的工具将源扩展编译成一个包含非JupyterLab JavaScript依赖项的JavaScript包,然后将生成的包分发到例如Python pip或conda包中。安装预构建扩展不需要Node.js。

  • [已弃用] 一个源扩展是一个JavaScript(npm)包,它导出一个或多个插件。安装源扩展需要用户重新构建JupyterLab。这个重新构建的步骤需要Node.js,并且可能需要大量的时间和内存,因此一些用户可能无法安装源扩展。然而,与使用预构建扩展相比,交付给用户浏览器的JupyterLab代码的总大小可能会减少。有关安装源扩展时重新构建JupyterLab的技术原因,请参见依赖项去重

扩展可以同时作为源扩展发布在NPM上,也可以作为预构建扩展发布(例如,作为Python包发布)。在某些情况下,系统管理员甚至可能选择通过直接将预构建包复制到适当的目录来安装预构建扩展,从而绕过创建Python包的需要。如果在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
};

idactivate 字段是必需的,其他字段可以省略。有关如何使用 requiresoptionalprovides 字段的更多信息,请参阅 插件之间的交互

  • id 是一个必需的唯一字符串。惯例是使用 NPM 扩展包名称,后跟冒号,然后是一个标识扩展内插件的字符串。

  • description 是一个可选的字符串。它允许记录插件的用途。

  • autostart 表示您的插件是否应在应用程序启动时激活。通常这应该是 true。如果它是 false 或省略,您的插件将在任何其他插件请求您的插件提供的令牌时激活。

  • requiresoptional 是其他插件提供的服务对应的 tokens 列表。当插件被激活时,这些服务将作为参数传递给 activate 函数。如果 requires 服务未在 JupyterLab 中注册,将抛出错误并且插件不会被激活。

  • provides 是与您的插件向系统提供的服务相关联的 token。如果您的插件不向系统提供服务,请省略此字段,并且不要从您的 activate 函数返回值。

  • activate 是当您的插件被激活时调用的函数。参数依次为 应用程序对象,对应于 requires 标记的服务,然后是对应于 optional 标记的服务(如果该特定的 optional 标记未在系统中注册,则为 null)。如果给出了 provides 标记,则 activate 函数的返回值(如果返回了 promise,则为解析后的返回值)将注册为与该标记关联的服务。

应用程序对象#

一个Jupyter前端应用程序对象作为第一个参数传递给插件的activate函数。该应用程序对象具有许多属性和方法,用于与应用程序交互,包括:

  • commands - 一个可扩展的注册表,用于在应用程序中添加和执行命令。

  • docRegistry - 一个可扩展的注册表,包含应用程序能够读取和呈现的文档类型。

  • restored - 当应用程序完成加载时解析的承诺。

  • serviceManager - 用于与Jupyter REST API通信的低级管理器。

  • shell - 一个通用的Jupyter前端shell实例,它持有应用程序的用户界面。有关更多详细信息,请参见Jupyter 前端 Shell

有关JupyterFrontEnd类的更多详细信息,请参阅JupyterLab API参考文档。

插件之间的交互#

(这里解释了提供者-消费者模式)

JupyterLab插件系统的基础特性之一是,应用程序插件可以通过向系统提供服务并要求其他插件提供的服务来与其他插件交互。服务可以是任何JavaScript值,通常是一个具有方法和数据属性的JavaScript对象。例如,提供JupyterLab主菜单的核心插件向系统提供了一个主菜单服务对象,该对象具有添加新顶级菜单的方法以及与现有顶级应用程序菜单交互的属性。

在以下讨论中,向系统提供服务的插件是提供者插件,而需要并使用该服务的插件是消费者插件。请注意,这些类型的提供者消费者插件是JupyterLab的提供者-消费者模式(这是一种依赖注入模式)的基本组成部分。

令牌#

插件提供的服务由token标识,即Lumino Token类的具体实例。提供者插件在其插件元数据的provides字段中列出该token,并从其activate函数返回相关服务。

消费者插件导入令牌(例如,从提供者插件的扩展JavaScript包中,或从为提供者和消费者导出令牌的第三方包中),并在其插件元数据的requiresoptional字段中列出该令牌。当JupyterLab通过调用其activate函数实例化消费者插件时,它将把与令牌关联的服务作为参数传递。如果服务不可用(即令牌尚未在JupyterLab中注册),则JupyterLab将抛出错误并且不激活消费者(如果令牌列在requires中),或者将相应的activate参数设置为null(如果令牌列在optional中)。JupyterLab对插件激活进行排序,以确保服务的提供者在其消费者之前被激活。一个令牌只能在系统中注册一次。

消费者可能会将令牌列为optional,当它标识的服务对消费者来说不是关键时,但如果服务可用,拥有它会更好。例如,消费者可能会将状态栏服务列为可选,以便在状态栏可用时添加一个指示器,但仍然使运行没有状态栏的定制JupyterLab分发的用户能够使用消费者插件。

在TypeScript中定义的token也可以为与该token关联的服务定义一个TypeScript接口。如果使用该token的包使用TypeScript,当该包被编译为JavaScript时,服务将根据此接口进行类型检查。

注意

JupyterLab 使用令牌来识别服务(例如,而不是字符串),以防止标识符之间的冲突,并在使用 TypeScript 时启用类型检查。

发布令牌#

由于消费者需要导入提供者使用的令牌,该令牌应在一个发布的JavaScript包中导出。在JupyterLab中需要对令牌进行去重——更多详情请参阅依赖项去重

JupyterLab 核心中的一个模式是从第三方包创建并导出一个令牌,供提供者和消费者扩展导入,而不是在提供者的包中定义令牌。这使得用户可以用提供相同令牌但具有不同服务实现的其他扩展替换提供者扩展。例如,核心 JupyterLab filebrowser 包导出一个表示文件浏览器服务的令牌(允许与文件浏览器进行交互)。filebrowser-extension 包包含一个插件,该插件在 JupyterLab 中实现文件浏览器,并向 JupyterLab 提供文件浏览器服务(通过从 filebrowser 包导入的令牌进行标识)。因此,JupyterLab 中希望与文件浏览器交互的扩展不需要对 filebrowser-extension 包有 JavaScript 依赖,而只需要从 filebrowser 包导入令牌。这种模式使用户能够通过编写自己的扩展来无缝更改 JupyterLab 中的文件浏览器,该扩展从 filebrowser 包导入相同的令牌,并使用自己的替代文件浏览器服务将其提供给系统。

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资源被特别打包在一个扩展中(参见主题路径),以便在主题激活时可以卸载或加载。由于由stylestyleModule键引用的CSS文件会自动打包并加载到页面上,因此不应通过这些键引用主题文件。

包含主题插件的扩展包必须包含其主题CSS文件中通过@import引用的所有静态资源。可以使用本地URL来引用相对于引用兄弟CSS文件位置的文件。例如,可以使用url('images/foo.png')url('../foo/bar.css')来引用主题中的本地文件。可以使用绝对URL(以/开头)或外部URL(例如https:)来引用外部资源。

查看JupyterLab Light Theme以获取示例。

查看TypeScript扩展模板(选择theme作为kind)以快速开始开发主题插件。

源扩展#

源扩展是一个导出(export)一个或多个插件的JavaScript(npm)包。所有的JupyterLab扩展都是作为源扩展开发的(例如,预构建的扩展是从源扩展构建的)。

一个源扩展在其package.json文件的jupyterlab字段中包含元数据。这些元数据的JSON schema@jupyterlab/builder包中分发。

我们将在下面讨论每个jupyterlab元数据字段在package.json中的源扩展。

JupyterLab 扩展必须至少设置 jupyterlab.extensionjupyterlab.mimeExtension 中的一个。

应用程序插件#

jupyterlab.extension 字段表示该包导出了一个或多个 JupyterLab 应用程序插件。如果主包模块的默认导出(即 package.jsonmain 键列出的文件)是一个应用程序插件或应用程序插件列表,请将值设置为 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 模式文件指定。jupyterlab.schemaDir 字段在 package.json 中给出了包含插件设置模式文件的目录的相对位置。

设置系统依赖于遵循约定:的插件ID。插件plugin_name的设置模式文件是/.json

例如,JupyterLab filebrowser-extension 包导出了 @jupyterlab/filebrowser-extension:browser 插件。在 @jupyterlab/filebrowser-extensionpackage.json 中,我们有:

"jupyterlab": {
  "schemaDir": "schema",
}

文件浏览器设置模式文件(指定了文件浏览器的一些默认键盘快捷键和其他设置)位于 schema/browser.json(参见 这里)。

查看 fileeditor-extension 以获取另一个使用设置的扩展示例。

请确保模式文件包含在package.json中的files元数据中。

在声明对JupyterLab包的依赖时,请在包版本前使用^操作符,以便构建系统为给定的主版本安装最新的补丁或次版本。例如,^4.0.0将安装版本4.0.0、4.0.1、4.1.0等。

系统管理员或用户可以使用overrides.json文件覆盖插件设置模式文件中提供的默认值。

禁用其他扩展#

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 为预构建扩展的依赖去重提供了一个合理的默认策略。扩展的 package.json 中的 jupyterlab.sharedPackages 对象允许扩展作者通过三个布尔选项修改给定依赖的默认去重策略。该对象的键是依赖包名称,值要么是 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"
        ]
      }
    ]
  }
}

目前支持的包管理器是 pipconda

扩展CSS#

如果你的扩展在package.json中有一个顶层的style键,它指向的CSS文件将自动包含在页面中。

在JupyterLab中,用于在页面上去重CSS的约定是,如果你的扩展在package.json中有一个顶层的styleModule键,指定了一个可以导入的JavaScript模块,那么它将作为JavaScript模块导入,而不是将style键的CSS文件作为CSS文件导入。

预构建扩展#

package.json 元数据#

除了源扩展的包元数据外,预构建的扩展还有额外的jupyterlab元数据。

输出目录#

当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..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中的依赖项不同。因此,您需要在tsconfig.json中添加路径选项paths,以指定搜索这些依赖项的路径:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
        "@jupyterlab/*": ["../jupyterlab/packages/*"],
        "*": ["node_modules/*"]
    }
  },
}

在添加路径以查找JupyterLab依赖项时,可能会因为JupyterLab包从其node_modules文件夹中获取依赖项,而您的包从您的node_modules文件夹中获取依赖项,从而导致与项目中其他依赖项(如lumino或react)的冲突。为了解决这个问题,您需要将有冲突的依赖项添加到您的package.json中的resolutions部分。这样,JupyterLab和您的扩展项目都将使用相同版本的重复依赖项。

我们提供了一个扩展模板,它处理了扩展作者所需的所有脚手架工作,包括适当数据文件的交付,因此当用户安装包时,预构建的扩展最终会出现在share/jupyter/labextensions

分发预构建的扩展#

预构建的扩展可以通过任何能够将预构建资源复制到JupyterLab可以找到的适当位置的系统进行分发。官方扩展模板展示了如何通过Python pip或conda包分发预构建的扩展。系统包管理器,甚至只是一个复制目录的管理脚本,也可以使用。

要分发一个预构建的扩展,将其输出目录复制到JupyterLab可以找到的位置,通常是/share/jupyter/labextensions/,其中package.json中的JavaScript包名称。例如,如果你的JavaScript包名称是@my-org/my-package,那么合适的目录将是/share/jupyter/labextensions/@my-org/my-package

JupyterLab 服务器通过 /labextensions/ 服务器处理程序提供 static/ 文件。服务器中的设置和主题处理程序也从预构建的扩展目录加载设置和主题。如果预构建的扩展与源扩展具有相同的名称,则优先使用预构建的扩展。

包装信息#

由于预构建的扩展以多种方式分发(Python pip 包、conda 包,以及可能在其他许多打包系统中),预构建扩展目录可以包含一个额外的文件,install.json,该文件帮助用户了解预构建扩展是如何安装的以及如何卸载它。分发预构建扩展的打包系统应将此文件复制到顶级目录中,例如 /share/jupyter/labextensions//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.pysetup.cfgpyproject.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重新构建,它将重新安装这些包。

你也可以同时链接其他你正在开发的本地npm包,使用jupyter labextension link;它们将被重新安装但不被视为扩展。本地扩展和链接的包包含在jupyter labextension list中。

当使用本地扩展和链接包时,您可以运行命令

jupyter lab --watch

这将导致应用程序在其中一个链接的包更改时逐步重建。请注意,WebPack 进程仅监视编译后的 JavaScript 文件(和 CSS 文件)。这意味着如果您的扩展是用 TypeScript 编写的,您将需要在更改反映到 JupyterLab 之前运行 jlpm run build。为了避免这一步,您还可以监视扩展中的 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 ,其中是发布的npm包的名称。您也可以提供一个脚本,在用户的机器上或提供的tarball上运行jupyter labextension install。任何有效的npm install指定符都可以在jupyter labextension install中使用(例如foo@latestbar@3.0.0.0path/to/folder,和path/to/tar.gz)。

我们鼓励扩展作者将jupyterlab-extension GitHub主题添加到任何GitHub扩展仓库中。

测试你的扩展#

注意

我们强烈推荐使用扩展模板来设置测试配置。

在这个仓库中(这是一个名为@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 中,您将指定使用 babel 处理 js 文件,并忽略除 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');