我想我会为 Wim 的回答所倡导的策略添加一些内容 - 首先让适当版本的 Django 在 2.7 和 3.x 上工作 - 并概述一些策略 对我有用。
Python 2.7 是你的逃生舱,直到你在 3.x 上扣动扳机
- 您的测试应该在两者上运行
- 不要使用任何 3.x 特定功能,例如 f-strings
- 首先是 Python 3.x,然后是不能在 2.7 上运行的后来的 Django 2.x
- 尽早开始,不要过度分析,但要避免大爆炸式的方法
- 首先逐个文件。
- 从您拥有测试套件的最低级别代码开始,例如实用程序库。
- 如果可能,请尝试逐步将您的更改合并到 2.7 生产分支中,并使您的 3.x 移植代码与产品更改保持同步。
从哪个小版本的 Django 开始?
我的标准是 Django 迁移可以相当参与(实际上需要更多的思考而不是 2=>3 的工作)。所以我会转向最新最好的 1.11,这样你就已经为你的 2.7 用户提供了一些价值。 1.11 上可能有大量 2.x 之前的兼容性填充程序并且您将收到 2.x 弃用警告。
从哪个次要版本的 Python 3.x 开始?
最好从各个角度考虑,例如您的第 3 方库的可用性、CI/devops 套件的支持以及您选择的服务器操作系统映像的可用性。例如,您始终可以安装 3.8 并尝试单独安装您的 requirements.txt。
利用 git(或您使用的任何 scm)和 virtualenv。
- 单独的
requirement.txt 文件,但是...
- 如果您有一个基于文件的 git 存储库,您可以使用
pip install -e <your directory> 将每个 venv 指向 相同的代码行。这意味着,在 2 个不同的终端中,您可以针对相同的单元测试运行 2.7 和 3.x。
- 您甚至可以在不同的端口上并行运行 2.7 和 3.x Django 服务器,然后将 Firefox 和 Chrome 指向它们。
- 经常提交(至少在移植分支上)并了解 git bisect。
利用2to3
是的,如果你允许它,它将破坏 2.7 代码和 Django。所以...
这就是我的节流命令的样子:
2to3 $tgt -w -f except -f raise -f next -f funcattrs -f print
使用 sed 或 awk 而不是您的编辑器进行批量转换。
优势在于,随着您对应用程序的具体问题越来越了解,您可以构建一套可以在一个文件或多个文件上运行的更改,并在不破坏 2.7 或 Django 的情况下完成大部分工作。 在您适当节流的 2to3 通行证后应用此方法。这会让您在编辑器中进行剩余清理,并让您的测试通过。
(可选)在 2.7 代码上开始运行 black。
black 是一种代码格式化程序,它使用 Python 3 AST 来运行其分析。它不会尝试运行代码,但会标记阻止其进入 AST 阶段的语法错误。但是,您必须使用一些 pip install 全局魔法才能到达那里,并且您必须相信黑色的有用性。
其他人已经做到了 - 向他们学习。
收听#155 Practical steps for moving to Python 3 应该会给你一些关于工作的想法。查看它的显示链接。他们喜欢谈论 Instagram(?)的举动,该举动涉及在通用代码库和同一 git 分支上逐步调整运行 2.7 代码到 3.x 语法,直到触发日。
另见The Conservative Python 3 Porting Guide
和Instagram Makes a Smooth Move to Python 3 - 新堆栈
结论
您到 Django 1.11 EOL(2020 年 4 月)的时间很短,所以如果您有 2 个以上的开发资源可以投入使用,我会考虑并行执行以下操作:
看看这两个任务是如何进行的,评估与 Django 相关的项目风险是什么以及 Python 3 的痛苦是什么样的。您已经错过了 Python 2.7 EOL,但过时的 Web 框架可能比传统的 Python 2.7 更危险,至少在几个月内是这样。所以我不会等太久就开始从 Django 1.9 迁移,你这样做的工作不会被浪费。随着您看到进展,您将开始更好地看到项目风险。
您最初的 2to3 进度会很慢,但工具和指导已经足够好,您会很快加快速度,因此在开始积累经验之前不要想太多。 Django 方面取决于您的 暴露于框架中的重大变化,这就是为什么我认为最好尽早开始。
附注(有争议/个人意见)我没有使用 six 或其他罐装 2-to-3 桥接库。
不是,因为我不信任它——它对于 3rd 方库来说非常棒——而是我不想添加复杂的永久依赖项(而且我懒得阅读它文档)。很长一段时间以来,我一直在用 3.x 兼容的语法编写 2.7 代码,所以我真的觉得没有必要使用它们。 您的里程可能会有所不同,如果看起来工作量很大,请不要走这条路。
相反,我创建了一个包含此类内容的 py223.py(57 LOC,包括 cmets),其中大部分内容涉及标准库中弃用和名称更改的变通方法。 p>
try:
basestring_ = basestring
except (NameError,) as e:
basestring_ = str
try:
cmp_ = cmp
except (NameError,) as e:
# from http://portingguide.readthedocs.io/en/latest/comparisons.html
def cmp_(x, y):
"""
Replacement for built-in function cmp that was removed in Python 3
"""
return (x > y) - (x < y)
然后从该 py223 导入以解决这些特定问题。稍后我将放弃导入并将那些奇怪的 isinstance(x, basestr_) 移动到 isinstance(x, str) 但我事先知道没什么好担心的。