对 4 万行 Python 代码进行类型检查的路径。 第3部分

我们向您展示有关 Dropbox 在为 Python 代码实现类型检查系统时所采取的路径的材料翻译的第三部分。

对 4 万行 Python 代码进行类型检查的路径。 第3部分

→ 上一部分: 第一 и 第二

达到 4 万行键入代码

另一个主要挑战(也是内部调查中第二个最常见的问题)是增加 Dropbox 中类型检查所覆盖的代码量。 我们尝试了多种方法来解决这个问题,从自然增加类型代码库的大小到将 mypy 团队的工作重点放在静态和动态自动类型推断上。 最终,似乎没有简单的获胜策略,但我们通过结合多种方法实现了注释代码量的快速增长。

因此,我们最大的 Python 存储库(包含后端代码)拥有近 4 万行带注释的代码。 静态代码类型的工作大约在三年内完成。 Mypy 现在支持各种类型的代码覆盖率报告,可以更轻松地监控输入进度。 特别是,我们可以生成有关类型不明确的代码的报告,例如显式使用类型 Any 无法验证的注释,或者导入没有类型注释的第三方库之类的内容。 作为提高 Dropbox 类型检查准确性的项目的一部分,我们致力于改进集中式 Python 存储库中一些流行开源库的类型定义(所谓的存根文件) 排版.

我们实现了(并在后续 PEP 中标准化)类型系统的新功能,允许针对某些特定的 Python 模式提供更精确的类型。 一个值得注意的例子是 TypeDict,它为类似 JSON 的字典提供类型,这些字典具有一组固定的字符串键,每个键都有自己类型的值。 我们将继续扩展类型系统。 我们的下一步可能是改进对 Python 数值功能的支持。

对 4 万行 Python 代码进行类型检查的路径。 第3部分
注释代码行数:server

对 4 万行 Python 代码进行类型检查的路径。 第3部分
注释代码行数:client

对 4 万行 Python 代码进行类型检查的路径。 第3部分
带注释的代码总行数

以下概述了我们为增加 Dropbox 中带注释的代码数量所做的事情的主要功能:

注释严谨。 我们逐渐提高了对新代码注释严谨性的要求。 我们从 linter 提示开始,建议向已经有一些注释的文件添加注释。 现在,我们需要在新的 Python 文件和大多数现有文件中使用类型注释。

打字报告。 我们每周向团队发送有关代码输入级别的报告,并就应首先注释的内容提供建议。

mypy 的普及。 我们在活动中谈论 mypy 并与团队交谈以帮助他们开始使用类型注释。

民意调查。 我们定期进行用户调查以发现主要问题。 我们已经准备好在解决这些问题方面走得更远(甚至创建一种新语言来加速 mypy!)。

表现。 我们通过使用守护进程和 mypyc 极大地提高了 mypy 的性能。 这样做是为了消除注释过程中出现的不便,并且能够处理大量代码。

与编辑器集成。 我们构建了工具来支持在 Dropbox 上流行的编辑器中运行 mypy。 这包括 PyCharm、Vim 和 VS Code。 这极大地简化了注释代码和检查其功能的过程。 在注释现有代码时,这些类型的操作很常见。

静态分析。 我们创建了一个使用静态分析工具推断函数签名的工具。 这个工具只能在相对简单的情况下工作,但它帮助我们不费吹灰之力地增加了代码类型覆盖率。

支持第三方库。 我们的许多项目都使用 SQLAlchemy 工具包。 它利用了 PEP 484 类型无法直接建模的 Python 动态功能。 我们按照PEP 561创建了相应的存根文件并为mypy编写了一个插件(开源),这改进了 SQLAlchemy 支持。

我们遇到的困难

对我们来说,通往 4 万行键入代码的道路并不总是那么容易。 在这条路上,我们遇到了很多坎坷,也犯过一些错误。 这些是我们遇到的一些问题。 我们希望讲述这些问题能够帮助其他人避免类似的问题。

丢失文件。 我们从仅检查少量文件开始我们的工作。 未检查这些文件中未包含的任何内容。 当第一个注释出现在文件中时,文件就被添加到扫描列表中。 如果某些内容是从位于验证范围之外的模块导入的,那么我们正在讨论使用诸如 Any,根本没有经过测试。 这导致打字准确性显着下降,尤其是在迁移的早期阶段。 到目前为止,这种方法的效果出人意料地好,尽管典型的情况是将文件添加到审查范围会揭示代码库其他部分的问题。 在最坏的情况下,当合并两个独立的代码区域时,其中彼此独立地检查了类型,结果发现这些区域的类型彼此不兼容。 这导致需要对注释进行许多更改。 现在回想起来,我们意识到我们应该早点将核心库模块添加到 mypy 的类型检查区域。 这将使我们的工作更加可预测。

注释旧代码。 当我们开始时,我们有大约 4 万行现有的 Python 代码。 很明显,注释所有这些代码并不是一件容易的事。 我们创建了一个名为 PyAnnotate 的工具,它可以在测试运行时收集类型信息,并可以根据收集的信息向代码添加类型注释。 然而,我们还没有注意到该工具得到特别广泛的采用。 收集类型信息的速度很慢,并且自动生成的注释通常需要许多手动编辑。 我们考虑过在每次检查代码时自动运行此工具,或者根据分析少量实际网络请求来收集类型信息,但最终决定不这样做,因为这两种方法都风险太大。

因此,可以注意到大部分代码都是由其所有者手动注释的。 为了引导这个过程朝着正确的方向发展,我们准备了关于需要注释的特别重要的模块和功能的报告。 例如,为在数百个地方使用的库模块提供类型注释非常重要。 但是,被新服务取代的旧服务注释不再那么重要。 我们还尝试使用静态分析来为遗留代码生成类型注释。

循环导入。 上面,我谈到了循环导入(“依赖缠结”),它的存在使得 mypy 的加速变得困难。 我们还必须努力让 mypy 支持由这些循环导入引起的各种习惯用法。 我们最近完成了一个主要的系统重新设计项目,解决了 mypy 的大部分有关循环导入的问题。 这些问题实际上源于项目的早期阶段,来自 Alore,mypy 项目最初关注的教育语言。 Alore 语法可以轻松解决循环导入命令的问题。 现代 mypy 继承了早期简单实现的一些限制(这非常适合 Alore)。 Python 使循环导入变得困难,主要是因为表达式不明确。 例如,赋值操作实际上可以定义类型别名。 在处理大部分导入循环之前,Mypy 并不总是能够检测到这样的事情。 阿洛尔没有这样的含糊之处。 系统开发早期阶段做出的错误决策可能会在多年后给程序员带来不愉快的意外。

结果:通往 5 万行代码和新视野的道路

mypy 项目已经走过了漫长的道路——从早期的原型到控制 4 万行生产代码类型的系统。 随着 mypy 的发展,Python 的类型提示被标准化。 如今,围绕输入 Python 代码已经形成了一个强大的生态系统。 它有一个库支持的地方,它包含 IDE 和编辑器的辅助工具,它有几个类型控制系统,每个系统都有自己的优点和缺点。

尽管 Dropbox 已经提供了类型检查,但我相信我们仍处于输入 Python 代码的早期阶段。 我认为类型检查技术将继续发展和改进。

如果您尚未在大型 Python 项目中使用类型检查,那么现在是开始转向静态类型的好时机。 我曾与那些经历过类似转变的人交谈过。 他们都没有后悔。 类型检查使 Python 成为一种比“常规 Python”更适合开发大型项目的语言。

亲爱的读者! 你在 Python 项目中使用类型检查吗?

对 4 万行 Python 代码进行类型检查的路径。 第3部分
对 4 万行 Python 代码进行类型检查的路径。 第3部分

来源: habr.com

添加评论