python 2 vs python 3,究竟谁是性能之王?前段时间,hackermoon 上一位叫 anthony shaw 的作者为我们做了一些测试,最终得出结论,虽然 python 2 在加密和启动时间测试过程中,比 python 3 的速度更胜一筹,但整体而言,python 3 更快。而这是否就意味着我们还是将项目代码迁移到 python 3.0 的好?接下来,本文来自全球著名的桌面应用之一的 dropbox 将分享他们要弃用 python 2.0 的真实原因,以及如何将百万行的代码成功迁移至 python 3。
dropbox 是世界上流行的桌面应用之一,你可以安装在 windows、macos 和部分的 linux 发行版上。但你可能不知道,这个应用大部分是用 python 写的。实际上,drew 给 dropbox 写下的第一行代码就是用的 windows 版 python,用的是老牌的 pywin32 等库。
虽然我们靠着 python 2 支撑了这么多年(我们用过的最新版本是 python 2.7),但我们从 2015 年就开始向 python 3 转换了。今天我们终于完成了转换,你现在再装 dropbox 的话,那么它用的是 dropbox 定制版本的 python 3.5。本文将介绍这次史无前例的 python 3 转换的计划、执行和发布过程。
为什么选择 python 3?
python 3 的接受度在 python 社区一直是热门话题。现在虽然 python 3 已经广为接受()。它并不只是个 api,而是个完整的应用程序包(.appex),有自己的生存中崛起规则(即它由 os 启动),而且对于进程间通信的要求更严格。换句话说,使用 xcode 就很容易集成这些扩展,但 py2app 根本不支持它们。
因此,我们面临着两个问题:
由于我们使用 python 2,因此无法使用新的工具链,所以集成新的 api 的代价更高(比如使用windows 10的windows runtime)。我们的冻结脚本使得部署原生代码的代价更高(例如在 macos 上构建应用扩展)。当我们计划转换成 python 3 时,我们面临着两个选择:一是改进冻结脚本中的依赖,以支持 python 3(从而支持现代编译器)和平台相关的功能(如应用程序扩展),二是不再使用以 python 为中心的构建系统,完全放弃冻结脚本。我们选择了后者。
关于 pyinstaller 的一点:我们认真地思考过在项目早期使用 pyinstaller,但当时它不支持 python 3,而且更重要的是,它和其他冻结脚本有类似的限制。不管怎样,这个项目本身很不错,我们只是觉得不适合我们而已。
嵌入 python
为了解决构建和部署的问题,我们决定使用新的架构,在原生应用中嵌入 python 运行时。我们不再将构建过程交给冻结脚本处理,而是使用各个平台自己的工具链(比如 windows 下使用 visutal studio)来构建各种入口点。进一步,我们将 python 代码抽象到一个库中,从而为多种语言“混合”的方式提供更直接的支持。
这样我们就可以直接使用各个平台的 ide 和工具链了(例如可以直接添加原生的构建目标,如 macos 上的 findersync),同时保留使用 python 编写大部分应用程序逻辑的能力。
我们最后采用了下面的结构:
原生入口点:这些与各个平台的应用程序模型兼容。其中包括应用程序扩展,如 windows 下的 com 组件和 macos 下的应用程序扩展。共享库可以使用多种语言编写(包括 python)。表面上,这个应用能够更接近平台的要求,而在各个库的背后,我们可以有更大的灵活性来选择自己喜欢的语言和工具。
这种架构能提高模块性,同时还带来一个关键的副作用:现在可以同时部署 python 2 库和 python 3 库了。联系到 python 3 转换工作,我们的转换过程就需要两步:第一,给 python 2 实现新的架构;第二,利用它将 python 2 替换成 python 3。
第一步:“解冻”
第一步就是停止使用冻结脚本。目前,bbfreeze 和 pywin32 都不支持 python 3,所以我们别无选择。我们从 2016 年开始逐步进行这项改变。
首先,我们将配置 python 运行时的工作抽象化,将 python 的东西放到一个新的库中,名为 libdropbox_bootstrap。这个库会代替一些冻结脚本提供的功能。尽管我们不再需要这些脚本,但它们仍然提供了一些运行 python 代码所需的最基本的东西:
打包代码以便在设备上执行
这样我们才能发布编译好的 python 字节码,而不用发布 python 源代码。由于以前的每个冻结脚本在各个平台上有各自的格式,我们利用这个机会引入了一种新的格式,用于在所有平台上打包代码使用:
所有 python 模块的 python 的字节码 .pyc 都放在单一的 zip 文档中(如 python-packages-35.zip)。原生扩展. pyd / .so 由于是平台相关的原生动态链接库,他们必须安装在特定的位置,保证应用程序能毫无障碍地加载。windows 下,这些文件与入口点(即 dropbox.exe)放在一起。打包通过优秀的 modulegraph(作者是 py2app 和 pyobjc 的作者 ronald oussoren)实现。隔离 python 解释器
这样能阻止我们的应用程序在设备上运行其他的 python 源代码。有意思的是,python 3 使得这种嵌入变得容易得多了。例如,新的 py_setpath 函数(#c.py_setpath)能够让我们将代码隔离,不需要再像 python 2 时代在冻结脚本中进行某种复杂的隔离操作了。为了在 python 2 中支持这一功能,我们在定制版本的 python 2 中向下移植了这一功能。
其次,我们使用了平台相关的入口点dropbox.exe、dropbox.app和dropboxd 来使用这个库。这些入口点都是用各自平台的“标准”工具编译的,即 visual studio、xcode 和 make,没有使用 distutils。这样我们就可以去掉冻结脚本带来的大量修补工作了。例如,在 windows 下,这一步大大简化,只需为 dropbox.exe 配置 dep/nx 即可,就能将应用程序装箱单和资源嵌入了。
关于 windows 的一点说明:现在,继续使用 visual studio 2008 的代价已经非常高了。为了正确地转换,我们需要一个能同时支持 python 2 和 python 3 的版本,最终我们采用了 visual studio 2013。为支持它,我们进一步修改了定制版本的 python 2,使之能正确在 visual studio 2013 下编译。这些修改的代价进一步证明,我们转换到 python 3 的决定是正确的。
第二步:混合
成功地转换如此之大(包含大约 100 万行 python 代码)、安装量如此之高(大约有几亿安装)的应用程序需要逐步进行。我们不能简单地在某次发布中“改变一个开关”来实现转换,特别是我们的发布过程要求每两个星期给所有用户发布一个新版本。因此,必须找到一种办法,将 python 3 的部分转换发布给一小部分用户,以便检测并修改 bug。
为达到这一点,我们决定实现用 python 2 和 python 3 同时编译 dropbox。这要求做到以下两点:
能够同时发布 python 2 和 python 3 的“包”,包括字节码和扩展,两者必须能够并存。在转换过程中强制使用混合的 python 2 / 3 语法。我们采用上一步引入的嵌入式设计来实现:将 python 代码抽象到库和包中,就能很容易地引入另一个版本。这样入口点程序(即 dropbox.exe)就可以在初始化的早期控制选择哪个 python 版本了。
我们通过手动连接入口点程序到 libdropbox_bootstrap 来实现这一点。例如在 macos 和 linux 下,我们在 python 版本确定之后使用 dlopen/dlsym 来加载。在 windows 下,使用 loadlibrary 和 getprocaddress。
对 python 解释器的选择必须在 python 加载之前完成,因此为了使之更顺畅,我们实现了命令行参数 /py3 用于开发,和一个保存在硬盘上的永久设置,以便通过我们的功能切换系统stormcrow(https://blogs.dropbox/tech/2017/03/introducing-stormcrow/)来控制。
有了这些,我们就能在启动 dropbox 客户端时动态选择 python 版本了。这样就可以在 ci 基础设施中设置额外的任务来针对 python 3 运行单元测试和集成测试。我们还在提交队列中增加了自动检查,以防止提交会破坏 python 3 支持的改动。
通过自动测试确保没问题之后,我们就开始将 python 3 的改动推送给真正的用户。我们通过远程的功能开关来将新功能逐渐开放给用户。首先对 dropbox 推送改动,这样我们就能找出并改正大部分主要的底层问题。然后将范围扩大到 beta 用户,他们的 os 版本问题更加芜杂。然后最终扩展到稳定版。7 个月之后,所有的 dropbox 都已经在运行 python 3 了。为了尽可能提高质量,我们要求所有与转换相关的 bug 必须进行深入调查并彻底修复,才能扩大推送的范围。
逐渐推送到 beta 版
逐渐推送到稳定版
到了版本 52 时,这个过程终于完成了。我们可以完全从 dropbox 的桌面客户端中删掉 python 2 了。
写在最后
一篇文章很难完整概括我们将代码迁移至 python 3.0 的完整过程,这其中还有许多可以讨论的东西。接下来,我们还会在以后的文章中讨论:
我们怎样在 windows 和 macos 上报告崩溃,并利用这些信息调试原生和 python 代码;怎样维护 python 2 和 python 3 混合代码,用到了哪些工具?整个 python 3 转换过程中最值得讨论的 bug 和故事。敬请期待,也欢迎在下方留言分享你对迁移过程的看法。
原文:https://blogs.dropbox/tech/2018/09/how-we-rolled-out-one-of-the-largest-python-3-migrations-ever/作者:max bélanger和damien deville译者:弯月,责编:屠敏
微信改版了,
想快速看到csdn的热乎文章,
赶快把csdn公众号设为星标吧,
打开公众号,点击“设为星标”就可以啦!