--- title: deploy.py / manage.py 兼容 Python 3.6 的改法与陷阱 type: experience permalink: main/projects/d1932ed6-07d3-4463-a224-8534ffd69a07/deploy.py-manage.py-兼容-python-3.6-的改法与陷阱 stable_id: df7b18f9-490f-415f-a388-2332d1b7df87 scope: project project_id: d1932ed6-07d3-4463-a224-8534ffd69a07 memory_type: experience status: active revision: 1 restored_from_commit: 6a6a895eafcca6052e81a14fca103a42635dd1c2 created_at: '2026-09-23T15:05:30.775086+00:00' updated_at: '2026-09-23T15:05:30.775148+00:00' tags: - nextoffice - python36 - compatibility - deploy - manage.py --- 背景:目标服务器 CentOS 8 系统 Python 仅 3.6.8,原脚本在其上直接 SyntaxError。提交 27fc41b 完成兼容,并在真实 3.6.8 上验证。 必须改的点(以后新代码也要守): - 不能用 `from __future__ import annotations`(3.7+);因此函数参数/返回注解会在定义时求值,不能用 PEP 585/604 写法(dict[str,str]、str | None),改用 typing.Dict/Optional/Union/Tuple/FrozenSet。局部变量注解不求值,但统一改成 `x = {} # type: Dict[...]` 注释形式。 - `subprocess.CompletedProcess[str]` 不可下标(3.9+),去掉 [str]。 - `subprocess.run(text=True)` 是 3.7+,改 `universal_newlines=True`;cwd 传 str(ROOT)。 - `Path.write_text(newline=...)` 是 3.10+,改 `with path.open('w', encoding='utf-8', newline='\n')`。 - `argparse.add_subparsers(required=True)` 是 3.7+,去掉 required,在 main() 中 `if not hasattr(args, 'func'): parser.print_help(); return 2`。 - f-string、secrets、ipaddress、pathlib、dict 有序(CPython 3.6 实际有序)均可用。 验证方法:本地高版本 Python 用 `ast.parse(src, feature_version=(3, 6))` 先拦语法;但运行时 API 差异(text=、newline=、required=)只能靠真实 3.6 跑:把 mock 了 docker/occ 的行为测试脚本 sftp 到服务器 /tmp 用系统 python3 执行。manage.py 现从 deploy 导入 write_env,reconfigure 与安装共用 .env 写入(含 chmod 600)。