手把手教你将一个旧的大型项目迁移到 Python 3

发布时间:2020-07-14 10:28:25 作者:Python小咖
来源:网络 阅读:291

一年半前,我们就决定使用 Python 3 了。我们已经讨论了很长时间,现在是时候使用了!现在这个过程已经结束了,我们已经把生产环境的最后部署都迁移到了 Python 3

关于修改 Python 3 的一些基本统计数据,是基于对 git 提交历史的粗略过滤产生的:

我发现有 109 个 jira 问题与这个项目相关。

Py2 → six → py3

我们的理念一直是 py2 →py2/py3 → py3 因为我们实在无法在实际生产中实现巨变,这种直觉也以令人惊讶的方式被证明是正确的。这意味着 2 到 3 是不可能的,我认为这很常见。我们尝试过使用 2 to 3 来检测 Python 3 的兼容性问题,但很快这也被发现无法成立。基本上,这样的更改意味着在 Python 2 中的代码将被破坏。这样的改变不可行。

结论是使用 six, 这是一个库,可以方便的构建一个在 Python 2 和 3 中都有效的代码库。

首当其冲的就是更新之前的依赖关系。这项工作需要立刻启动,因为之后会有更多的内容要更新。

现代化

Python-modernize 是我们选择进行迁移的工具。它是一个可以自动将 Py 2 代码库转换为可兼容 six 代码库的工具。我们首先引入一个测试,作为 CI 的一部分,来检查基于 modernize 的新代码是否已经准备好兼容 py3 了。这样做最大的效果的是让那些仍使用 Py 2 语法的人意识到新的处理方法,但这显然对将现有的 240 k 行代码转化到 six 作用不大。我们都有使用旧语法的坏习惯,这可以说是教学上的成功了,即使它对代码行的计数没有什么不同,它也被我们用于实验分支:

实验分支

我新建了一个名为“Python 3 ”的分支,并做了以下操作:

这里的想法是“run ahead”,即看看如果我们没有使用过时的依赖项,我们会遇到什么问题。这个分支允许我在超级中断状态下可以非常快速地启动应用程序,至少可以运行一些单元测试。 这个分支有很大的不同,但我还是找到了把它应用在适当场景的方法。我使用优秀的 GitUp 来拆分、组合和提交。当一个提交看起来不错的时候,我会把它挑选到一个新的分支,然后发给代码审查。

没有人可以在这个分支上工作,因为它被不断地 rebase ,强制推送,滥用,但是它确实让项目向前推进了,而不用等待所有的依赖项被更新。我强烈推荐使用这种方法!

静态分析

我们添加了预提交钩子,所以如果您编辑了一个文件,就会收到建议将 Python 3 全部进行 modernize 更新的提示。

quote_plus 的手动静态分析: 在处理 quote_plus 和 six 上有一些细微差别。最后,我们创建了自己的包装器,默认代码强制执行使用这个包装器,而不是使用标准库中的包装器,也不使用 six 中包装器。我们还静态检查了您从未给 quote_plus 发送过的字节。

我们修复了每个 diango 应用程序中所有的 python 3 问题,并在 CI 环境中使用一个白名单强制执行了这一点,所以您无法破坏一个曾经修复过的应用程序。

依赖

对于我们来说,解决依赖是最困难的部分。我们有很多依赖,所以花了很多时间,其中有两个依赖关系比较棘手:

测试

我们的代码测试覆盖率大约有 65% 包括:单元、集成, 以及 UI 合并。 我们确实编写了更多的测试,但总体数量并没有发生太大的变化。考虑将覆盖率从 65% 提高到 66% ,意味着编写将近2000 行代码的测试,这一点也不奇怪。

我们必须跳过需要 Cassandra 的测试,同时修复这个依赖项。 我发明了一个有趣的小 hack 来使它发挥作用, 并写了这方面的文章.

代码更改

关于代码更改的说明,在如何将 py2 迁移到 six 的文档中并未提及 (也许是我们错过了):

StringIO

我们在代码中大量使用 StringIO 。第一反应就是使用 six。但对于 StringIO 来说,这在几乎所有情况下 (但不是全部!)都被证明是错。基本上,我们必须非常仔细地考虑每一个我们使用 StringIO 的地方,并试图弄清楚我们是否应该用 io.StringIO, io.BytesIO 或者 six.StringIO 来替代它。这里犯错的表现通常为看起来像兼容 py3 的代码准备好了,在 py2 中可以正常运行,却实际上在 py3 中是失效的。

从 future 中导入unicode_literals

这是一件好坏参半的事情。您可以通过将它添加到许多文件中来发现 bug,但是有时会在 py2 中引入 bug。 当日志突然在奇怪的地方,比如在字符串前写"u"时,它也会变得令人困扰。总的来说,这显然不是我所期望的效果。

str/bytes/unicode

这在很大程度上是您所期望的。我感到惊讶的是,在 py2 和 py3 中需要 str 。如果将来您使用 unicode_literals 导入,那么一些字符串需要从 'foo' 修改为 str('foo')。

six.moves

six.moves 的实现是一个非常奇怪的***行为,因此它不像它假装的普通 Python 模块那样运行。 我也不同意他们在 six.moves 中不包含 mock 的选择。我们必须使用他们的 API 来自己添加它,但这让我们很难开始工作,而且它要求我们将 from mock import patch 改为 from six.moves import mock 这也意味着 patch 现在变成了 mock.patch 。

CSV 的解析是不同的

如果你使用 csv 模块,你需要了解 csv342。在我看来,这应该是 six 的一部分。否则就意味着你没有意识到有问题。不过我们在许多地方都没有使用 csv342,所以您这里要做的工作可能会有所不同。

发布顺序

我们首先进行测试:

接下来就是产品本身了。我们建立一台拥有能一次性切换到 py3 的能力的批处理机器,并且至关重要地是将其切换回来。当在 py3 上发生中断时,这一点就显得很重要了。这对我们来说是很好的,因为我们可以重新排队那些中断的任务,但是我们不能中断太多或者任何实际上是很关键的任务。我们使用 Sentry 来收集奔溃日志,所以很容易查看迁移到 py3 时遇到的所有问题,而且当我们修复了所有的问题时,我们需要再次迁移到 py3,直到我们得到一些问题,如此反复。

我们有如下环境:

我们按照以下顺序将 Python 3 发布到这些环境中:

负载机器暴露了与 Python 3 不兼容的客户数据配置,因此我们必须在 Python 2 中实现对这些情况的警告,并确保再次打开 Python 3 之前已经修复了它们。这花了几天时间,因为我们每天都会收到客户数据,所以每次都会有一个警告,这又让我们不得不再等一天。

生产中的惊喜

结论

最后,我们觉得在这件事上我们真的别无选择: Python 2 的维护将在某个时刻停止,我们的依赖项仅限于 py3,最明显的就是 Django。但是,无论如何,我们还是想要进行这种转换,因为我们经常会被 bytes/Unicode 问题困扰,并且Python 3 仅仅是修复了 Python 2 中的许多小麻烦。这次迁移过程,我们已经在生产过程中发现了一些实际的漏洞/错误配置。我们也期待在任何地方都可以使用 f-string 和有序字典。


推荐阅读:
  1. 手把手教你跑 Larave 框架实战笔记系列之一
  2. 手把手教你写网络爬虫(4)Scrapy入门

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

python 架构 项目迁移

上一篇:如何安装python的pil库

下一篇:C++之继承访问权限

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》