返回开发者新闻

借助 Wasabi 更快速地进行 Python 编程

2022年7月18日发布者:Omer Dunay

本文由 Omer Dunay、Kun Jiang、Nachi Nagappan、Matt Bridges 和 Karim Nakad 共同撰写


动机

在 Meta 内部,无论从代码行数还是从用户数量来看,Python 都是用得最多的编程语言之一。我们每天都有成千上万名开发者使用 Python 发布新功能,修复漏洞,开发最复杂的机器学习模型。因此,为 Python 开发者提供最先进的工具,确保他们能高效工作,这一点至关重要。

Wasabi 简介

今天,我们介绍的是 Wasabi,这是一种实现了语言服务器协议 (LSP) 的 Python 语言服务,旨在帮助开发者更轻松和更快速地使用 Python。Wasabi 提供了一系列先进功能协助开发者编写 Python 代码,具体包括:

  • Lint 和诊断:这些功能在用户输入时均可用。
  • 自动导入快速修复:用于未定义变量的提示。
  • 全局符号自动补全:当用户输入前缀时,所有在其他文件中定义并以该前缀开头的符号(例如函数名、类名)将自动出现在自动补全建议中。
  • 组织导入+删除未使用的导入:这是一个快速修复程序,可删除所有未使用的导入并根据 pep8 规则重新设置导入部分的格式。此功能由 Meta 内部构建的其他工具(例如 libCST)提供技术支持,有助于安全地重构代码。
  • Python 代码片段:当用户输入常见码型时,可以看到代码片段建议。

此外,Wasabi 服务适用于任意平台,可以部署到多个代码存储库和各种开发环境(例如 VSCode、Bento Notebook)中。自首次推出以来,Wasabi 已经被 Meta 名下的 Facebook、Instagram、Infrastructure 等团队的数万名 Python 用户所采用。

图 1:Wasabi 的全局符号自动补全功能示例

Meta 级语言服务

语言服务的主要设计要求是低延迟/用户响应性。自动补全建议、lint 和快速修复应该在开发者输入时立即显示出来。

Meta 以 monorepo(单体仓库)的形式组织代码,这意味着开发者在开发过程中可以访问所有 Python 文件。这种方法给开发者工作流程带来的主要优势在于可发现性更高、透明度更高、更易于共享库以及增强团队间的协作。它还给构建开发者工具,如需要处理数十万个文件的语言服务,带来了特殊挑战。

规模问题是我们试图避免使用业内的现成语言服务(例如 pyrightjedi)来执行这些操作的原因之一。那些工具大多都是在相对小型到中等的项目工作空间下构建的,在这些工具所假定的工作背景下,那些需要 o(repo) 信息的操作都是针对涉及数千个文件的大型项目。

以针对未定义变量的“自动导入”快速修复功能为例。为了能够提示语言服务器读取所有源文件所需的所有可用符号,快速修复功能将对它们进行解析,并在内存中缓存所有已解析的符号,以响应请求。

虽然对于中小型存储库而言,这可以扩展为在开发计算机上的单个进程中执行,但此方法在单体仓库用例中无法扩展。读取和解析数十万个文件可能需要等待几分钟,这意味着启动时间缓慢,开发者也会感到苦恼。转移到内存中进行缓存可能有助于缩短延迟,但单台计算机的内存也可能承载这些文件。

例如,假设一份普通 Python 文件需要大约 10 ms 的时间来解析和提取标准错误可恢复解析器中的符号。这意味着,完成 1,000 个文件的初始化需要 10 秒钟,这个启动时间相当合理。而完成 100 万个文件的初始化则将花费 166 分钟,这个启动时间显然太长了。

Wasabi 工作原理

离线+在线处理:

为了支持 Meta 级存储库实现低延迟,Wasabi 支持两个解析阶段,由外部索引器完成的后台处理(离线)和由本地更改的“脏文件”完成的本地处理(在线):

  1. 后台进程为所有提交的源文件编制索引,并在专门存储代码符号信息的专用数据库 (glean) 中维护已解析的符号。
  2. Wasabi 是在用户计算机上运行的本地进程,它负责计算用户当前拥有的基本修订版、diff 堆栈和未提交更改之间的差异,并且仅从这些“脏”文件中提取符号。由于这组“脏”文件相对较小,因此该操作执行得非常快。
  3. 收到自动导入等 LSP 请求后,Wasabi 会解析文件的抽象语法树 (AST),然后根据游标的上下文,为 glean 和本地更改符号创建查询,合并结果并将其返回给用户。

因此,所有 Wasabi 功能均具有较低延迟,用户可以在输入代码时无缝使用这些功能。

注意:Wasabi 目前不处理 glean 编入索引(每隔几小时发生一次)的修订版与用户当前拥有的本地基本修订版之间的潜在差异。我们计划将来再增加此功能。

图 2:Wasabi 的高层体系架构

对结果进行排名

某些情况下,受存储库的规模影响,结果集中可能有许多有效建议。以针对“utils”符号的“自动导入”建议为例。可能有许多模块在整个存储库中都定义了一个名为“utils”的类,因此我们新增了结果排名,确保用户在顶部能看到最相关的建议。

例如,根据以下因素对自动导入内容进行排名:

  • 本地性:
    • 建议的模块目录路径与已导入此文件的模块的目录路径之间的距离。
    • 建议的模块目录路径与本地文件的当前目录路径之间的距离。
    • 文件是否已在本地更改(“脏”文件排名更高)。
  • 使用情况:存储库中其他文件使用导入语句的次数。

为了评估排名是否有用,我们测量了被接受建议在建议列表中的次序,我们发现几乎在所有情况下,被接受的建议均为排名前三的建议之一。

来自开发者的积极反馈

在 Meta 内部发布 Wasabi 进行了几次试运行后,我们收到了来自开发者的大量积极反馈。以下是 Instagram 软件工程师提交给我们的一条反馈:

“如今我已经使用 Wasabi 好几个月了,它大大提升了我的工作效率!在 Instagram 服务器上工作,尤其是处理较大文件时,pyre 的警告发出的相当缓慢。用了 Wasabi 后,它们的速度快得像闪电 😃!”

“我每小时都要使用好几次拼写错误检查和自动导入等功能。这可能使我的开发工作流程平均快了 10%(这只是粗略猜测,实际提速可能更多,但肯定不会低于 10%),这真是相当巨大的改进!”

如上所述,Wasabi 带来了重大改变,让开发者能够高效愉快地工作。

衡量编程速度的指标

为了定量评估 Wasabi 为 Python 开发者创造了多大价值,我们曾考虑过许多可以用来衡量 Wasabi 影响力的指标。最终,我们决定使用名为“编程速度”的指标来评估开发者编写代码的速度。实质上,编程速度是在编程阶段中对某个特定 diff(代码更改集合)所用时间的反函数。编程阶段的开始时间为开发者从源代码控制存储库签出时的时间戳,截止时间为创建 diff 时的时间戳。我们还根据 diff 中更改的代码行数(diff 大小指标)对编程速度进行了规范化,以抵消任何潜在差异。我们认为,“编程速度”的值越大,开发者编写代码的速度就越快。

图 3:编程速度指标公式

成效

定义了指标后,我们开展了一项实验来评估 Wasabi 给开发者带来了哪些变化。具体而言,我们选择了约 700 名之前从未用过 Wasabi 的开发者,然后按 50:50 的比例将他们随机分成两组。实验组的开发者使用 Python 编程时启用了 Wasabi,而对照组的开发者编程时则没启用 Wasabi。然后,我们比较了两组开发者启用 Wasabi 前后的相对指标值的变化情况。根据两组的对比结果,我们发现实验组的开发者在开始使用 Wasabi 后,编程速度的中位数增加了 20%。与此同时,对照组的编程速度则全程无任何显著变化,这也在我们意料之中。

图 4:实验组和控制组在实验组启用 Wasabi 前后的实测编程速度对比。

总结

随着 Python 的空前发展,能够投身此领域让 Python 变得更完善、更易于使用真是令人振奋不已。Wasabi 凭借其先进的功能,有效提升了 Meta 开发者的工作效率,使他们能够更快、更轻松地用 Python 编程,给他们带来了良好的开发者体验。我们希望此原型和开发成果能够让 Python 社群的更多人受益。

如需了解有关 Meta Open Source 的更多信息,请访问我们的 Open Source 网站、订阅我们的 YouTube 频道,或在 TwitterFacebookLinkedIn 上关注我们。