☰
jupyter_contrib_nbextensions 之 ExecuteTime:Jupyter 单元执行计时、耗时展示与持久化配置指南
2026/10/13 2:45:02 网站建设 项目流程
  • 开发工具
  • 前端

【免费下载链接】jupyter_contrib_nbextensions

A collection of various notebook extensions for Jupyter

项目地址:https://gitcode.com/gh_mirrors/ju/jupyter_contrib_nbextensions
点击查看免费下载

本篇技术指南围绕 jupyter_contrib_nbextensions 项目中的 ExecuteTime 前端扩展展开,系统讲解它如何在每个被执行的代码单元(Cell)下方显示"最后一次执行的时间与耗时"、如何通过菜单与双击切换显示、如何在 nbconfig 中精细配置全部选项,并深入源码剖析其基于 kernel 消息时间戳的计时原理与元数据持久化机制。读者阅读后可以独立完成 ExecuteTime 的安装启用、界面定制、相对时间刷新、UTC 时区修正等实战配置,并理解它比前端本地计时更准确的原因。

功能概述:单元下方的时间戳与耗时信息

ExecuteTime 是一个 Jupyter Notebook 前端扩展(nbextension),它的核心能力非常简单直观:展示一个代码单元最后一次执行发生在什么时间,以及这次执行花了多长时间。

具体的工作流程如下:

  1. 当用户把一个代码单元发送给 kernel 执行时,扩展会在该单元的输入区(input area)底部附加一个新区域,显示单元被送入 kernel 队列的时刻(此时显示的是"排队中/已提交"的状态消息)。
  2. 当 kernel 完成执行并返回回复后,该区域会被更新为执行的耗时,同时一并显示结束时间。
  3. 计时信息会被写入单元元数据(cell metadata),因此关闭并重新打开 notebook 后,之前记录的计时信息依然存在,扩展会在 notebook 加载完成后自动恢复显示。

下图展示了每个已执行单元下方实际渲染出的计时区域效果:

该扩展对应的实现文件为 ExecuteTime.js,声明文件为 ExecuteTime.yaml,样式定义在 ExecuteTime.css(.timing_area采用 80% 字号、顶部 1px 边框的紧凑样式)。按其 YAML 声明,兼容 Jupyter Notebook 4.x 与 5.x 两个大版本。

切换显示:双击隐藏与菜单控制

计时区域并不总是需要展示,扩展提供了两种便捷的显示切换方式:

  • 双击隐藏:在计时区域上双击即可将其隐藏(再次双击无法恢复,恢复需要通过菜单操作)。这一交互由源码中的dblclick事件绑定实现,见 ExecuteTime.js。
  • 菜单控制:Notebook 的Cell菜单下新增了Toggle timings子菜单:
    • Cell -> Toggle timings -> Selected:切换当前选中单元(可多选)的计时区域显示;
    • Cell -> Toggle timings -> All:当第一个单元当前处于"显示"状态时隐藏全部计时区域,反之则全部显示(以第一个单元的状态作为批量切换的基准)。

从源码看,这些菜单项由create_menu()函数动态创建并挂载到#cell_menu下,除了上述两个切换项之外,还额外提供了Clear (selected)与Clear (all)两个清理项,用于直接删除选中/全部单元的计时数据(删除metadata.ExecuteTime并移除计时 DOM 节点),见 ExecuteTime.js。

配置选项:默认值与完整参数说明

ExecuteTime 的全部选项都存储在服务器端 nbconfig 的notebook段下、ExecuteTime键中。前端在Jupyter.notebook.config.loaded加载完成后,通过$.extend(true, options, Jupyter.notebook.config.data['ExecuteTime'])与内置默认值深度合并,因此服务器配置会逐项覆盖默认值(见 ExecuteTime.js)。

两种配置途径

方式一:使用 jupyter_nbextensions_configurator 图形化配置

jupyter_nbextensions_configurator是随 jupyter_contrib_nbextensions 常规安装一并通过jupyter contrib nbextension install安装的配套配置器。通过它可以在浏览器界面中直接勾选/填写 ExecuteTime 的各项参数,无需手写 JSON。ExecuteTime.yaml中声明的input_type(checkbox / number / text / color)正是配置器渲染表单的依据。

方式二:用 Python 直接写入 nbconfig

也可以通过几行 Python 代码直接更新配置。例如,想修改显示消息模板、改用相对时间戳、并让相对时间每 5 秒刷新一次,可以执行以下片段:

from notebook.services.config import ConfigManager ConfigManager().update('notebook', {'ExecuteTime': { 'display_absolute_timestamps': False, 'relative_timing_update_period': 5, 'template': { 'executed': 'started ${start_time}, finished in ${duration}', } }})

注意:示例片段中的display_absolute_timestamps是旧版文档沿用的参数名,实际生效的选项名是display_absolute_timings(详见下文参数表),使用时请以display_absolute_timings为准。

全部可配置选项

下表完整列出 ExecuteTime 支持的所有选项、默认值及其作用(默认值与 ExecuteTime.js 和 ExecuteTime.yaml 中的声明一致):

选项默认值类型说明
ExecuteTime.clear_timings_on_clear_outputfalse布尔当单元的输岀被清空时(例如使用Kernel > Restart & Clear Output菜单),同时清空其计时数据
ExecuteTime.clear_timings_on_kernel_restartfalse布尔在任何 kernel 重启事件发生时,清空所有单元的计时数据
ExecuteTime.display_absolute_timingstrue布尔以绝对时间显示执行的开始/结束时间;设为false时改用相对时间戳,如a few seconds ago(相对时间格式基于 moment.js 的fromNow()函数)
ExecuteTime.display_absolute_format'YYYY-MM-DD HH:mm:ss'字符串绝对时间的显示格式,模板令牌语法参考 moment.js 的format()函数
ExecuteTime.relative_timing_update_period10数字使用相对时间戳时的刷新间隔(秒);YAML 中约束范围为 1~600,步进 1
ExecuteTime.display_in_utcfalse布尔以 UTC 而非浏览器本地时区显示时间
ExecuteTime.default_kernel_to_utctrue布尔对未指明时区的 kernel 时间戳,按 UTC 处理
ExecuteTime.display_right_alignedfalse布尔计时区域内的文本右对齐
ExecuteTime.highlight.usetrue布尔单元执行完成后,对显示的耗时做高亮动画提示
ExecuteTime.highlight.color'#00BB00'颜色高亮动画使用的颜色
ExecuteTime.template.executed'executed in ${duration}, finished ${end_time}'字符串已执行单元的计时消息模板,支持${start_time}、${end_time}、${duration}三个替换令牌
ExecuteTime.template.queued'execution queued ${start_time}'字符串排队中(尚未收到 kernel 回复)单元的计时消息模板,只替换${start_time}(若定义了${end_time}、${duration}也会被替换)
ExecuteTime.hide_shorter_than_threshold_in_ms0数字隐藏耗时短于该阈值(毫秒)的计时显示;设为 0 表示显示所有计时

上表中各选项的字段类型、默认值、步进与取值范围均可在 ExecuteTime.yaml 中逐条核对——该文件同时也是 jupyter_nbextensions_configurator 自动生成配置表单的元数据来源。

内部实现原理:基于 kernel 消息时间的精准计时

ExecuteTime 的计时之所以比"前端按下运行键时本地记一个时间"更准确,是因为它直接复用 kernel 消息中携带的时间戳,而不是客户端本地时钟。下面按源码调用链逐层展开。

1. 排队时刻的捕获:监听execute.CodeCell事件

当用户执行单元时,Notebook 会触发execute.CodeCell事件。扩展通过events.on('execute.CodeCell', excute_codecell_callback)捕获该事件(见 ExecuteTime.js),在回调中把cell.metadata.ExecuteTime = {start_time: moment().toISOString()}写入元数据,并立即渲染出"执行已排队"的计时区域(见 ExecuteTime.js)。此时 start_time 是客户端本地时刻,仅作为占位,稍后会被更准确的 kernel 时间覆盖。

2. 完成时刻的捕获:patchCodeCell.prototype.get_callbacks

为了拿到真正准确的执行结束时间,扩展在加载时对 Jupyter 的类原型CodeCell.prototype.get_callbacks(来自notebook/js/codecell.js)做了 monkey-patch(见 ExecuteTime.js):

  • 先调用原始get_callbacks取得callbacks对象;
  • 再将其中的callbacks.shell.reply函数包装一层:当收到msg.msg_type === 'execute_reply'的 kernel 回复时,
    • 用msg.metadata.started作为开始时间(这个值由 kernel 记录,与单元真正开始执行的时间一致,可能和排队时间相差很大,因为前面可能还有其他单元在排队执行);
    • 用msg.header.date作为结束时间(kernel 消息头自带的时间戳,本身就是 ISO8601 字符串,无需转换);
    • 两者合并写入cell.metadata.ExecuteTime,随后调用update_timing_area刷新界面。

由于msg.header.date是 kernel 侧记录的消息产生时刻,不受客户端网络延迟、浏览器性能等"客户端可变性"影响,这正是文档中强调其更准确的原因。

3. 时区修正:add_utc_offset

kernel 的时间戳并不总是带时区信息。add_utc_offset函数用正则Z|[+\-−]\d\d(?::?\d\d)?$检测字符串末尾是否已含时区标记(ISO8601 的Z后缀或±hh[:mm]形式的偏移);若default_kernel_to_utc为true且字符串不含时区标记,则在末尾追加Z,将其显式解释为 UTC(见 ExecuteTime.js)。这正对应下文中"时区限制"一节所述的默认处理策略。

4. 模板渲染与显示控制:update_timing_area

渲染函数update_timing_area(见 ExecuteTime.js)完成以下工作:

  • 若单元还没有ExecuteTime.start_time,直接返回;
  • 查找单元输入区下的.timing_area节点,不存在则新建并绑定双击切换事件,同时按display_right_aligned决定是否追加text-right类;
  • 根据是否已有end_time选择template.executed或template.queued模板;
  • 依次替换${start_time}、${end_time}令牌(由format_moment负责:display_in_utc为真时先.utc()转换,再按display_absolute_timings决定走.format(display_absolute_format)还是.fromNow());
  • 用-start_time.diff(end_time)计算耗时毫秒数,替换${duration},并按exec_time >= hide_shorter_than_threshold_in_ms决定是否隐藏计时;
  • 最后写入文本。

5. 耗时的自定义格式化:humanized_duration

由于作者未找到满意的现成时长格式化实现,耗时的可读化由自定义函数humanized_duration完成(见 ExecuteTime.js),规则为:

  • 小于 1 秒:直接显示毫秒(如500ms);
  • 大于等于 1 秒:分解为Xd Xh Xm X.Xs的组合,秒保留 0~2 位小数(天数存在时不显示秒,分钟>1 时不保留小数,秒>10 时保留 1 位,否则保留 2 位),例如2m 3.4s一类格式。

6. 启动流程与可选清理钩子

扩展加载入口load_jupyter_extension(见 ExecuteTime.js)的完整流程为:

  1. 若启用了高亮且$.ui未定义,则尝试按jquery-ui/jqueryui两个名字加载 jQuery UI(用于effect('highlight')动画),加载失败则仅降级为无动画;
  2. 注入ExecuteTime.css;
  3. 等待 notebook 配置加载完成后依次执行:patchget_callbacks、绑定execute.CodeCell、创建菜单、监听notebook_loaded.Notebook恢复历史计时(若 notebook 已完全加载则直接刷新全部计时区域);
  4. 按选项决定是否 patchclear_output或绑定kernel_restarting.Kernel事件来联动清理计时;
  5. 若启用了相对时间显示,则用setInterval以max(1, relative_timing_update_period)秒为周期刷新全部计时区域(见 ExecuteTime.js)。

元数据持久化:计时信息如何随 notebook 保存

计时数据以 ISO8601 字符串形式保存在单元元数据的ExecuteTime键下。一个典型示例为:

{ "ExecuteTime": { "start_time": "2016-02-11T18:51:18.536796", "end_time": "2016-02-11T18:51:35.806119" } }

因为元数据会随.ipynb文件一起保存,所以 notebook 关闭再打开后,历史计时仍然存在;扩展在notebook_loaded.Notebook事件触发时遍历所有单元并恢复计时区域(见 ExecuteTime.js)。

nbconvert 支持:离线执行时同步更新计时元数据

除了浏览器前端扩展,仓库还提供了配套的 nbconvert 预处理器ExecuteTimePreprocessor(实现于 execute_time.py,并已从 nbconvert_support 包 导出)。它的作用是在用 nbconvert 执行 notebook 时,同样把每次单元执行的计时信息写入元数据:

  • 在run_cell中记录执行前的datetime.utcnow()作为兜底开始时间;
  • 执行完成后读取exec_reply:优先取exec_reply['metadata']['started'](已是字符串,直接使用),否则回退到父消息的date;
  • 结束时间取exec_reply['header']['date'],缺失时回退为 UTC 当前时刻并isoformat()。

这样经过 nbconvert 执行得到的 notebook 文件,其ExecuteTime元数据格式与前端扩展完全一致,后续打开即可直接显示。仓库的 tests/test_preprocessors.py 中保留了针对该预处理器行为(执行前后metadata['ExecuteTime']的有无)的测试注释,可作为功能预期参考。

已知限制与规避策略

时区问题

虽然按规范 kernel 应当在时间戳中指明时区,但实际并非所有 kernel 都这样做。当 moment.js 把未带时区的时间戳按浏览器本地时区解析、而 kernel 实际使用的是 UTC 时,就会出现显示偏差。为此:

  • 将ExecuteTime.default_kernel_to_utc保持为true(默认),让未声明时区的时间戳被当作 UTC 处理——这是最常见的修复手段;
  • 或将ExecuteTime.display_in_utc设为true,统一以 UTC 显示,避免与本地时区换算混淆。

执行队列问题

当多个单元被排队连续执行时,kernel 并不会在每个单元结束的瞬间立即回发回复,部分回复会延迟到与后续回复同时到达。其外在表现是:某个单元的输出已经显示了最终结果,但扩展的计时区域尚未收到该单元的 execute_reply;同理,队列执行期间"正在执行"的星标可能同时停留在多个单元旁。

由于 ExecuteTime 的时间取自 kernel 消息(msg.header.date与msg.metadata.started),这些时间本身是正确的,因此最终展示的计时依然准确,只是显示更新可能会因为 kernel 的这一行为而延迟。

安装与启用

ExecuteTime 属于 jupyter_contrib_nbextensions 集合的一部分,无需单独安装。按仓库 安装文档 中的说明安装 jupyter_contrib_nbextensions 主版本(也可使用 scripts/jupyter-contrib-nbextension 提供的命令行入口完成安装/启用管理)后,即可通过 jupyter_nbextensions_configurator 对全部 notebook 启用或禁用本扩展;启用后即可在上文所述的Cell菜单中找到Toggle timings子菜单。

值得留意的是,仓库的 migrate.py 中记录了旧版安装路径usability/execute_time/ExecuteTime到新版execute_time/ExecuteTime的迁移映射。如果你是从旧版本升级而来,迁移工具会负责修正配置中的 require 路径,确保扩展能够正常加载。

小结

ExecuteTime 通过"前端捕获排队时刻 + kernel 消息时间戳修正完成时刻 + 元数据持久化"三者的组合,提供了一套准确、可复现、可配置的单元执行计时方案。无论是想在演示中展示执行耗时、排查慢单元,还是希望批量离线执行后保留执行档案,都可以通过本文所述的菜单操作与ExecuteTime.*配置项组合出适合自己的使用方式。配置的最终效果可在刷新页面后即时生效,无需修改任何前端代码。

  • 开发工具
  • 前端

【免费下载链接】jupyter_contrib_nbextensions

A collection of various notebook extensions for Jupyter

项目地址:https://gitcode.com/gh_mirrors/ju/jupyter_contrib_nbextensions
点击查看免费下载

相关推荐

上一篇:如何用VisualCppRedist AIO一键修复Windows软件兼容性问题?终极解决方案来了!
下一篇:3个必学技巧掌握暗黑2存档编辑器d2s-editor

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询