解读 WinPython 3.4.3.5Qt5 变更记录:PyQt4→PyQt5 迁移与包索引差异的自动化生成机制
解读 WinPython 3.4.3.5Qt5 变更记录:PyQt4→PyQt5 迁移与包索引差异的自动化生成机制
本篇技术指南围绕 changelogs/WinPythonQt5-3.4.3.5_History.md 这份历史变更记录展开,系统梳理 WinPython 3.4.3.5Qt5 相对上一版本(3.4.3.4Qt5)的全部包级变化:新增、升级、移除的完整清单,以及本次变更背后"从 PyQt4 全面切换到 PyQt5"的核心趋势。同时深入当前仓库的源码实现,讲清楚
_History.md这类差异文档是如何由构建流程(winpython/build_winpython.py)与wppm工具链(wppm/diff.py、wppm/wppm.py)自动生成、如何被测试用例(tests/test_diff_versions.py)验证的。读完你既能完整读懂这一版本升级的来龙去脉,也能掌握用wppm -diff自己对比任意两个 WinPython 版本包索引的方法。
一、这份文档是什么:一个版本的"包索引"与"变更记录"
在 WinPython 仓库的 changelogs/ 目录里,每个发布版本通常对应两类文档:
- 发布说明(包索引):如 changelogs/WinPythonQt5-3.4.3.5.md,以
Name | Version | Description表格列出该版本最终包含的全部内容,包括 Tools 与 Python packages; - 变更记录(History):如本文档
WinPythonQt5-3.4.3.5_History.md,只记录"从上一版本到本版本之间发生了什么变化",即新增(New)、升级(Upgraded)、移除(Removed)三类条目。
本文档开头的两行就是它的定位声明:
History of changes for WinPython 3.4.3.5Qt5 The following changes were made to WinPython distribution since version 3.4.3.4Qt5.
也就是说,这是一份严格的版本间差异(diff)文档:对比基准是 3.4.3.4Qt5,目标版本是 3.4.3.5Qt5。它不重复列出未变化的包,这正是它与发布说明的分工所在。从源码看,这一结构并非手工维护,而是由 wppm/diff.py 的 compare_package_indexes 自动生成的(详见第五节),文档标题、引言、三节差异、结尾 * * * 分隔线都对应源码里的固定模板(wppm/diff.py#L169-L182)。
二、本次版本变更总览
把本文档三节内容汇总,3.4.3.5Qt5 相对 3.4.3.4Qt5 的变化规模如下:
| 变更类型 | 数量 | 涉及范围 |
|---|---|---|
| Tools 移除 | 2 | MinGW32、gettext |
| Python 包新增 | 15 | Markdown、PyAudio、PyQt5、blaze、bokeh、greenlet、holoviews、locket.py、mingwpy、netCDF4、param、partd、pystache、twitter、xray |
| Python 包升级 | 32 | scipy、numba、ipython、Keras、pip、spyder、SQLAlchemy、tables、Pillow 等 |
| Python 包移除 | 11 | PyQt4、PyQtdesignerplugins、PyQwt、VPython、ViTables、formlayout、ggplot、guidata、mahotas、runipy、vispy |
整体来看,本次升级是一次幅度相当大的换代:既引入了 15 个新包,又移除了 11 个旧包,其中 GUI 与可视化栈的变动最引人注目——PyQt4 全家(PyQt4、PyQtdesignerplugins、PyQwt)被移除,同时 PyQt5 5.5 加入,这与该版本 "Qt5" 风味(flavor)的定位完全吻合(下文第四节详述)。
三、Tools 层变更:编译器与翻译工具退出默认发行
文档的 "### Tools" 一节记录了 2 个随发行版分发的工具被移除:
| 工具 | 版本 | 说明 |
|---|---|---|
| MinGW32 | 4.9.2 | C/C++ 和 Fortran 编译器(Mingwpy 静态工具链版本) |
| gettext | 0.14.4 | GNU gettext 的 Win32 移植版,即 GNU 翻译工具(对标准库模块 pygettext 有用的辅助工具) |
值得注意的关联变化是:Python 包清单中新增了 mingwpy 0.1.0b3(描述为 "the python friendly windows compiler toolchain",即对 Python 友好的 Windows 编译器工具链)。从变更记录的进出关系可以推断,本次版本用新的 mingwpy 打包工具链替换了旧的 MinGW32 静态工具链,属于同一能力域内的一次换代,而不是简单地"砍掉编译能力"。
四、Python 包变更详解
4.1 新增的 15 个包
文档 "New packages" 一节完整列出了 15 个首次进入本版本的包,按其用途可归为几类:
| 包名 | 版本 | 定位 |
|---|---|---|
| PyQt5 | 5.5 | Qt 跨平台 GUI 工具包的 Python 绑定(本次 Qt5 迁移的主角) |
| Markdown | 2.6.2 | Python 实现的 Markdown |
| PyAudio | 0.2.8 | PortAudio v19 的绑定,跨平台音频输入/输出流库 |
| blaze | 0.8.2 | 面向大数据分析的统一接口(Blaze 项目) |
| bokeh | 0.9.2 | 面向统计与交互式 HTML 绘图的 Python 库 |
| holoviews | 1.3.2 | 可组合、声明式的数据结构,用于轻松构建复杂可视化 |
| xray | 0.5.2 | N 维带标签数组与数据集(xray,即后来 xarray 的前身) |
| netCDF4 | 1.1.9 | python/numpy 对 netCDF 库(3、4 版)的接口 |
| greenlet | 0.4.7 | 轻量级进程内并发编程 |
| param | 1.3.2 | 使用 Parameters 进行声明式 Python 编程 |
| partd | 0.3.2 | 可追加的键值存储 |
| locket.py | 0.2.0 | 面向 Linux 与 Windows 的基于文件的锁 |
| pystache | 0.5.4 | Python 版 Mustache 模板 |
| 1.17.0 | Twitter 的 API 与命令行工具集 | |
| mingwpy | 0.1.0b3 | 对 Python 友好的 Windows 编译器工具链 |
这批新增包有明显的"数据科学 + 可视化"取向:blaze、dask/partd、bokeh、holoviews、xray 构成了当时新兴的"大数据分析栈",而 PyQt5 则承担了 GUI 底座的角色。对照发布说明 changelogs/WinPythonQt5-3.4.3.5.md 可以确认,这些包在 3.4.3.5Qt5 最终包索引中全部存在,属于"新增即随版本发布"的正常状态。
4.2 升级的 32 个包
文档 "Upgraded packages" 一节记录了 32 个包的版本前进,完整清单如下(旧版 → 新版):
| 包名 | 版本变化 | 包名 | 版本变化 |
|---|---|---|---|
| Keras | 0.1.1 → 0.1.2 | nltk | 3.0.2 → 3.0.4 |
| Pillow | 2.8.2 → 2.9.0 | numba | 0.19.2 → 0.20.0 |
| SQLAlchemy | 1.0.5 → 1.0.8 | oct2py | 3.2.0 → 3.3.0 |
| alabaster | 0.7.4 → 0.7.6 | odo | 0.3.2 → 0.3.3 |
| bcolz | 0.9.0 → 0.10.0 | patsy | 0.3.0 → 0.4.0 |
| beautifulsoup4 | 4.3.2 → 4.4.0 | pip | 7.0.3 → 7.1.0 |
| click | 4.0 → 4.1 | psutil | 3.0.0 → 3.1.1 |
| dask | 0.5.0 → 0.6.1 | pymongo | 3.0.2 → 3.0.3 |
| datashape | 0.4.5 → 0.4.6 | pyodbc | 3.0.9 → 3.0.10 |
| dill | 0.2.2 → 0.2.3 | rpy2 | 2.6.0 → 2.6.1 |
| ipython | 3.2.0 → 3.2.1 | scipy | 0.15.1 → 0.16.0 |
| llvmlite | 0.5.1 → 0.6.0 | seaborn | 0.5.1 → 0.6.0 |
| logilab_common | 0.63.2 → 1.0.2 | simplejson | 3.7.3 → 3.8.0 |
| mistune | 0.5.1 → 0.7 | spyder | 2.3.5.2 → 3.0.0.dev0 |
| multipledispatch | 0.4.7 → 0.4.8 | tables | 3.2.0 → 3.2.1 |
| tornado | 4.2 → 4.2.1 | xlrd | 0.9.3 → 0.9.4 |
其中几个值得单独说明:
- scipy 0.15.1 → 0.16.0:科学计算核心库的大版本前进,直接影响整个发行版的计算能力基线;
- numba 0.19.2 → 0.20.0 与 llvmlite 0.5.1 → 0.6.0:JIT 编译栈同步升级,两者版本通常是配套推进的;
- spyder 2.3.5.2 → 3.0.0.dev0:IDE 升级到 3.0 的开发版分支,属于较大幅度的版本跳跃;
- pip 7.0.3 → 7.1.0:包管理器的自我升级,保证发行版后续可继续从 PyPI 拉取依赖;
- Keras 0.1.1 → 0.1.2:基于 Theano 的深度学习库继续迭代(对应发布说明中 Theano 0.7.0 的搭载)。
这些升级在 changelogs/WinPythonQt5-3.4.3.5.md 的最终包索引中都能找到对应版本号,属于"升级后落盘"的确定事实。
4.3 移除的 11 个包:PyQt4→PyQt5 迁移的信号
文档 "Removed packages" 一节记录了 11 个包退出本版本:
| 包名 | 版本 | 说明 |
|---|---|---|
| PyQt4 | 4.11.4 | Qt 跨平台 GUI 工具包的 Python 绑定(PyQt4 版) |
| PyQtdesignerplugins | 1.1 | 为 PyQt4 安装 Python Qt designer 插件(Matplotlib、guiqwt 等) |
| PyQwt | 5.2.1 | 2D 绘图库(Qwt 库的 Python 绑定,主打快速绘图) |
| VPython | 5.74 | 用 Python 实时生成 3D 场景的开源模块 |
| ViTables | 2.1 | 浏览/编辑 HDF5 与 PyTables 格式文件的图形工具 |
| formlayout | 1.0.15 | 无需编写 GUI 代码即可编辑各类参数的对话框/控件模块 |
| ggplot | 0.6.5 | Python 版 ggplot |
| guidata | 1.6.2 | 自动生成 GUI,便于数据集编辑与显示 |
| mahotas | 1.2.4 | 计算机视觉库 |
| runipy | 0.1.3 | 从命令行运行 IPython notebook |
| vispy | 0.4.0 | Python 交互式可视化 |
把新增与移除两节放在一起看,本次变更的主线非常清晰:PyQt4 生态(PyQt4、PyQtdesignerplugins、PyQwt、guidata、formlayout、ViTables)整体退出,被 PyQt5 5.5 取代——这正是发行版名称中 "Qt5" 风味的含义。而从包索引生成机制的语义(见下节)看,"Removed" 意味着这些包仍存在于 3.4.3.4Qt5 的包索引中,但不在 3.4.3.5Qt5 中,这是由 wppm/diff.py 的 compare_packages 按"旧有新无"规则判定出来的(wppm/diff.py#L61-L74),信息可靠。
五、机制揭秘:_History.md 是怎么被自动生成的
这份变更记录并非手工撰写,而是 WinPython 构建流水线的标准产物。整条生成链路可以拆成三段,全部有源码可查。
5.1 第一步:生成"当前版本包索引"(发布说明 .md)
构建脚本在最后阶段调用 wppm.Distribution().generate_package_index_markdown()(winpython/build_winpython.py#L321-L330)。该方法(wppm/wppm.py#L116-L132)先通过 get_package_index_data 收集三类数据:
- Tools:由
utils.get_installed_tools枚举随发行版安装的工具; - Python packages:由
get_installed_packages调用piptree.PipData.pip_list(full=True)读取目标解释器的已安装包(wppm/wppm.py#L64-L71); - WheelHouse packages:扫描 wheelhouse 目录中额外收录的 wheel。
然后 render_markdown_for_list(wppm/wppm.py#L73-L81)把每一类渲染成标准的 Name | Version | Description 表格,包名带链接、按名称排序,最终产出 WinPythonQt5-3.4.3.5.md 这样的发布说明。也就是说,发布说明是"实况快照"——直接来自构建完成后的真实安装环境。
5.2 第二步:找出"上一个版本"作为对比基准
生成差异文档前,必须先确定"和谁比"。find_previous_version(wppm/diff.py#L153-L160)扫描 changelogs/ 目录里同一风味(flavor)、同一架构(architecture)的发布说明文件名,用 packaging.version.parse 做 PEP 440 语义化排序,取小于目标版本的最大版本作为基准。
这里有个重要的细节(也是测试用例重点钉死的行为,见 tests/test_diff_versions.py):
- 带发布级别(release level)的版本,如
3.14.7.1b1,按 PEP 440 排序位于3.14.7.0与3.14.7.1之间,能被正确识别并作为对比基准; _History.md这类伴生文档不会被当作版本(test_history_companions_are_never_the_answer专门验证了这一点);- 不同风味(dot/slim/Qt5 等)、不同架构(32/64 位)之间互不干扰,避免跨风味对比产生噪音(
test_flavors_do_not_see_each_other)。
对本文档而言,基准就是版本号小于 3.4.3.5Qt5 的最近版本——文档开头的 "since version 3.4.3.4Qt5" 正是这一查找结果。
5.3 第三步:逐节 diff 并拼接成 History 文档
compare_package_indexes(wppm/diff.py#L169-L182)加载新旧两份发布说明,按 ### Tools、### Python packages、### WheelHouse packages 三个分区逐一比对:
PackageIndex._parse_index用正则\name\ | version | description解析每个分区的表格行,得到以规范名(-换成_并小写)为键的包字典(wppm/diff.py#L46-L59);compare_packages(wppm/diff.py#L61-L74)按集合关系分类:- 新有旧无 →
New packages,输出* name version (description); - 新旧都有但版本不同 →
Upgraded packages,输出* name oldversion → newversion (description); - 旧有新无 →
Removed packages,输出* name version (description);
- 新有旧无 →
- 拼接固定模板:标题
## History of changes for WinPython-{arch}bit {version}、引言、三个分区的差异、结尾* * *。
对比基准版本决定后,这个生成过程是完全确定性的:同一对发布说明永远产生同一份 History 文档。
5.4 与构建流水线的衔接:Step 7 的定位
在 winpython/build_winpython.py#L312-L347 的 Step 7(generate changelog)中,构建脚本按 release_level 拼出版本名,先写发布说明 .md,再调用 diff.compare_package_indexes 生成 _History.md。源码注释明确说明:History 文档的受众是正在跑构建的人,用于发布前快速确认"这次到底动了哪些包",因此当前构建逻辑不会把它复制进 changelogs/ 目录(配套测试 tests/test_changelog_files.py 的 test_changelog_pr_is_whole_cycle_only 也断言 changelogs/ 中不应出现新的 _History.md);本仓库 changelogs/ 下现存的大量 _History.md 是历史版本构建的遗留产物,本文档即其中之一。
六、自己动手:用 wppm -diff 对比任意两个版本
即使不跑构建,你也可以直接用仓库自带的 wppm 工具对比任意两个包索引,这正是 wppm CLI 的 -diff 参数(wppm/wppm.py#L348-L370)与 wppm/diff.py 命令行入口(wppm/diff.py#L208-L262)提供的功能。支持的用法如下:
# 对比两个包索引文件(默认对比全部三个分区,任意方向均可)
wppm -diff WinPythonslim-64bit-3.13.5.0.md WinPythonslim-64bit-3.14.7.0.md
# 等价写法:直接调用模块
python -m wppm.diff file1.md file2.md
# 只对比指定分区(如 python 分区)
python -m wppm.diff file1.md file2.md --section python python
# 按分区对逐一对比(例如 python 对 wheelhouse、tools 对 tools)
python -m wppm.diff file1.md file2.md --pairs python wheelhouse tools tools
# 按版本对比:从 changelogs 目录自动定位两个版本的发布说明
python -m wppm.diff <version2> <version1> [searchdir] [flavor] [architecture]
# 把对比结果写入 _History.md 文件(可选复制到 basedir)
python -m wppm.diff --write-changelog <version2> <version1> [searchdir] [flavor] [architecture] [basedir]
几点使用须知(均有源码依据):
-diff是纯文件操作,不依赖任何 WinPython 发行环境,changelogs/目录中任意两个发布说明都能比,不必是相邻版本(见 wppm/wppm.py#L360-L370 的注释);- 升级行统一写作
旧版 → 新版,因此对比时文件顺序(新旧方向)不能弄反,否则所有升级都会显示成降级(这是 wppm/diff.py#L97-L107index_title中明确指出的陷阱); - Windows 控制台默认 cp1252 编码无法输出
→箭头,模块入口会用sys.stdout.reconfigure(encoding="utf-8", errors="replace")处理(wppm/diff.py#L224-L227),所以建议在 UTF-8 终端下使用。
七、总结
通过解读 changelogs/WinPythonQt5-3.4.3.5_History.md,我们得到了关于 3.4.3.5Qt5 的完整事实图景:
- 工具层:MinGW32 4.9.2 与 gettext 0.14.4 退出默认发行,编译器能力由新引入的 mingwpy 0.1.0b3 承接;
- 包层:新增 15 个(PyQt5、bokeh、blaze、holoviews、xray、netCDF4、greenlet 等),升级 32 个(scipy、numba、ipython、pip、spyder、Keras 等),移除 11 个(PyQt4 全家、VPython、ViTables、ggplot、vispy、mahotas 等);
- 主线:这是一次以 PyQt4→PyQt5 切换为核心的 GUI 栈换代,同时伴随数据科学与可视化新包的大规模引入;
- 机制:这类变更记录不是手写产物,而是由构建流水线(winpython/build_winpython.py Step 7)+
wppm工具链(wppm/diff.py、wppm/wppm.py)从两个版本的包索引自动 diff 出来的确定性输出,其版本查找、分区解析、差异分类均有测试(tests/test_diff_versions.py、tests/test_changelog_files.py)兜底验证。
如果你需要追踪 WinPython 任意两个版本之间的包变化,掌握 wppm -diff 这一个命令就足够了。