类型检查400万行Python代码的方式。 第三部分

我们向您介绍Dropbox所用路径上的材料翻译的第三部分,介绍了一种检查Python代码类型的系统。



→先前的部分: 第一第二

达到400万行键入的代码


另一个重要任务(这是使内部参与调查的人担心的第二大问题)是增加Dropbox中类型检查所覆盖的代码量。 我们尝试了几种解决此问题的方法-从键入代码库的自然增长到mypy团队致力于静态和动态自动类型推断的工作重点。 结果,似乎没有简单的制胜法宝,但通过组合多种方法,我们能够实现带注释的代码量的快速增长。

结果,在我们最大的Python存储库(带有后端代码)中,带注释的代码行数已达到近400万。 在大约三年的时间里,进行了代码的静态类型化工作。 Mypy现在支持各种类型的代码覆盖率报告,可以更轻松地监视键入进度。 特别是,我们可以针对类型不确定的代码生成报告,例如,在无法验证的注释中明确使用Any类型,或导入不具有类型注释的第三方库。 作为提高Dropbox中类型检查的准确性的项目的一部分,我们为改进集中式Python存储库中一些流行的开源库的类型定义(所谓的存根文件)做出了贡献。

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


带注释的代码行数:服务器


带注释的代码行数:客户端


带注释的代码的总行数

这是我们为增加Dropbox中带注释的代码量而执行的操作的主要功能的概述:

注释的严格性。 我们逐渐增加了对新代码进行严格注释的要求。 我们从linter技巧开始,这些技巧建议将注释添加到已经具有某些注释的文件中。 现在,我们需要在新的Python文件和大多数现有文件中添加类型注释。

键入报告。 我们每周向团队发送有关其代码键入级别的报告,并给出有关首先应注释的内容的提示。

推广mypy。 我们在各种活动中谈论mypy,并与团队沟通以帮助他们开始使用类型注释。

民意调查。 我们会定期进行用户调查,以找出主要问题。 我们已经准备好解决这些问题(为加速mypy而创建新的语言!)。

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

与编辑器集成。 我们创建了工具来支持在Dropbox上流行的编辑器中启动mypy。 这包括PyCharm,Vim和VS代码。 这大大简化了注释代码和验证其性能的过程。 注释现有代码时,此类操作通常很典型。

静态分析 我们创建了一个使用静态分析工具输出功能签名的工具。 该工具只能在相对简单的情况下工作,但是它帮助我们轻松地增加了类型的覆盖范围。

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

我们遇到的困难


对于我们来说,通往400万行键入代码的道路并不总是那么容易。 这样,我们遇到了很多漏洞并犯了一些错误。 这是我们遇到的一些问题。 我们希望有关他们的故事能够帮助其他人避免此类问题。

跳过的文件。 我们仅从检查少量文件开始。 未检查这些文件数量中未包括的所有内容。 当第一个注释出现在文件中时,文件即被添加到检查列表中。 如果从检查范围之外的模块中导入了某些内容,那么我们正在讨论使用Any类型的值,而这些值根本没有检查过。 这导致打字准确性的显着下降,尤其是在迁移的早期阶段。 到目前为止,尽管通常将文件添加到扫描区域会发现代码库其他部分的问题,但是这种方法到目前为止效果很好。 在最坏的情况下,当将两个隔离的代码区域组合在一起时,已经相互检查了类型,而这两个区域彼此独立,事实证明这些区域的类型彼此不兼容。 这使得必须对注释进行许多更改。 现在,回顾一下,我们了解到应该将基本库模块尽早添加到mypy类型检查区域。 这将使我们的工作更具可预测性。

注释旧代码。 当我们开始时,我们有大约400万行现有的Python代码。 显然,对所有这些代码进行注释并非易事。 我们创建了一个名为PyAnnotate的工具,该工具可以在测试执行期间收集类型信息,并可以基于收集的信息将类型注释添加到代码中。 但是,我们没有注意到该工具的特别广泛的介绍。 有关类型的信息键入速度很慢,自动生成的注释通常需要大量的手动编辑。 我们考虑过在每次检查代码时自动启动此工具,或者考虑根据对少量实际网络请求的分析来收集类型信息,但由于其中任何一种方法都存在风险,因此决定不这样做。

结果,可以注意到,大多数代码是由其所有者手动注释的。 为了朝正确的方向进行指导,我们正在准备有关特别重要的模块和功能的报告,需要对其进行注释。 例如,重要的是为数百个地方使用的库模块提供类型注释。 但是,由新服务替换的旧服务的注释不再那么重要。 我们还在尝试使用静态分析为旧代码生成类型注释。

循环导入。 之前,我谈到了循环导入(“依赖关系缠结”),循环导入的存在使mypy的加速变得复杂。 此外,我们必须努力为mypy提供支持,以支持这些周期性导入导致的各种成语。 我们最近完成了一个重大的系统重新设计项目,该项目修复了mypy的大多数周期性导入问题。 实际上,这些问题源于该项目的早期,从myore最初面向的教育语言Alore回来。 使用Alore语法可以轻松解决循环导入命令的问题。 现代mypy从其早期的巧妙实现继承了一些局限性(对于Alore来说效果很好)。 Python使得使用循环导入变得困难,这主要是由于表达式的含糊性。 例如,在分配操作期间,实际上可以确定类型别名。 在处理完大部分导入周期之前,Mypy始终无法检测到此类情况。 亚罗尔没有这种含糊。 在系统开发的早期阶段做出的不成功决策可能会使程序员在多年后感到不快。

简介:通往五百万行代码的道路和新视野


mypy项目已经走了很长一段路-从早期的原型到一个控制生产代码类型的系统,该系统具有400万行的生产量。 随着mypy的发展,使用Python对类型提示进行了标准化。 如今,围绕输入Python代码已经形成了强大的生态系统。 它找到了一个支持库的地方,它包含用于IDE和编辑器的辅助工具,它具有多个类型控制系统,每个系统都有其优缺点。

尽管Dropbox已经理所当然地进行了类型检查,但我确信我们仍然生活在键入Python代码的曙光中。 我认为类型检查技术将继续发展和改进。

如果您没有在大型Python项目中使用类型检查,那么您应该知道现在是开始过渡到静态类型的好时机。 我与那些进行了类似过渡的人进行了交谈。 他们都没有后悔。 类型控制将Python变成比开发大型项目的“普通Python”更好的语言。

亲爱的读者们! 您在Python项目中使用类型控制吗?


Source: https://habr.com/ru/post/zh-CN468237/


All Articles