NumPy 1.20.1 版本发布详解:快速修复 distutils 与 random.shuffle 回归问题 NumPy 1.20.1 版本发布详解快速修复 distutils 与 random.shuffle 回归问题【免费下载链接】numpyThe fundamental package for scientific computing with Python.项目地址: https://gitcode.com/gh_mirrors/nu/numpyNumPy 1.20.1 是 1.20 系列的首个快速修复bugfix版本专门用于修复 1.20.0 发布后社区报告的一系列缺陷与回归问题。本篇以官方发布说明doc/source/release/1.20.1-notes.rst为主体结合本仓库源码与测试用例逐项剖析本次修复的根因、实现细节与验证方式帮助读者了解 NumPy 版本发布流程中 bugfix 分支的运作机制以及这些修复对下游用户的实际影响。版本定位面向回归的快速修复版NumPy 1.20.1 不是一个功能版本而是紧跟在 1.20.0 之后的快速 bugfix 版本rapid bugfix release。官方说明明确指出该版本的目标是修复 1.20.0 发布后报告的几个 bug 与回归问题regressions修复了导致下游项目出问题的 distutils bug修复了random.shuffle的回归问题。本次发布共合并 15 个 pull request共有 8 位贡献者参与其中 Nicholas McKibben 与 Aerysv 为首次贡献补丁的新人名字旁带 标记。从 PR 的构成看本次修复横跨构建系统distutils、threads.h探测、f2py、随机数子系统shuffle、C 链接、核心函数isclose/allclose与 timedelta以及类型注解与 CI 基础设施等多个方向下面逐一展开。Highlights 一distutils 构建问题修复问题背景迭代中修改 fake libraries 列表1.20.0 引入的 distutils 缺陷会破坏下游项目的构建。修复该问题的 PR 标题为BUG: dont mutate list of fake libraries while iterating over...PR #18326其核心是distutils 在解析依赖时维护一个伪造库fake libraries列表而原实现在遍历该列表的过程中直接对其进行了修改这违反了迭代时禁止修改容器的基本约束导致在特定链接场景下下游项目构建失败。从修复类型BUG与后续 1.20.x 系列发布说明的稳定性可以看出这类构建路径缺陷对使用 NumPy 构建链的第三方包影响最大。若下游项目恰好命中该遍历逻辑升级到 1.20.0 后会出现链接错误而 1.20.1 通过改为先收集、后修改的方式从代码结构看即遍历副本或延迟收集待处理项恢复了正常的依赖解析流程。相关构建修复signed_char与threads.h探测同属构建系统的还有两个修复PR #18351BUG: Fix missing signed_char dependency. Closes #18335修复了signed_char依赖缺失的问题该缺陷由 issue #18335 报告会导致特定平台上的类型映射不完整PR #18357BUG: threads.h existence test requires GLIBC 2.12修正了threads.h存在性探测的逻辑。原探测条件过于宽松未考虑 glibc 版本下限修复后要求GLIBC 2.12才判定threads.h可用避免在较老 glibc 环境下误判导致编译期问题。这两项修复共同保证了 1.20.1 在更多 Linux 发行版与交叉编译场景下的可构建性。Highlights 二random.shuffle回归修复random.shuffle回归是本版本另一个核心亮点。官方说明将其单独列出本仓库源码与测试为我们提供了完整的证据链。回归一无法优雅处理 memoryviewgh-18273在 numpy/random/tests/test_random.py 中可以看到专门为该回归添加的回归测试def test_shuffle_memoryview(self): # gh-18273 # allow graceful handling of memoryviews # (treat the same as arrays) rng random.RandomState(self.seed) a np.arange(5).data rng.shuffle(a) assert_equal(np.asarray(a), [0, 1, 4, 3, 2]) rng random.RandomState(self.seed) rng.shuffle(a) assert_equal(np.asarray(a), [0, 1, 2, 3, 4]) rng np.random.default_rng(self.seed) rng.shuffle(a) assert_equal(np.asarray(a), [4, 1, 0, 3, 2])该测试同时覆盖了两套 API传统 APInp.random.RandomState.shuffle新 APInp.random.default_rng().shuffle即Generator.shuffle。测试断言对 memoryview这里是np.arange(5).data即ndarray的 buffer 内存视图调用shuffle后底层的数组内容被正确、可复现地打乱且两次使用相同种子 shuffle 的结果相互还原。修复 PR #18327MAINT: gracefully shuffle memoryviews使得 memoryview 被当作数组一样优雅处理而非抛出异常或产生未定义行为。回归二随机分布函数缺少 C 链接PR #18328PR #18328BUG: Use C linkage for random distributions修复了随机分布函数缺失 C linkage 的问题。在 numpy/random/mtrand.pyx 中shuffle的实现展现了其底层机制def shuffle(self, object x): ... if isinstance(x, np.ndarray) and not x.flags.writeable: raise ValueError(array is read-only) if type(x) is np.ndarray and x.ndim 1 and x.size: # Fast, statically typed path: shuffle the underlying buffer. # Only for non-empty, 1d objects of class ndarray (subclasses such # as MaskedArrays may not support this approach). x_ptr np.PyArray_BYTES(x) stride x.strides[0] itemsize x.dtype.itemsize # As the array x could contain python objects we use a buffer # of bytes for the swaps to avoid leaving one of the objects # within the buffer and erroneously decrementing its refcount # when the function exits. buf np.empty(itemsize, dtypenp.int8) # GCd at function exit buf_ptr np.PyArray_BYTES(buf) with self.lock: # We trick gcc into providing a specialized implementation for # the most common case, yielding a ~33% performance improvement. if itemsize sizeof(np.npy_intp): self._shuffle_raw(n, sizeof(np.npy_intp), stride, x_ptr, buf_ptr) else: self._shuffle_raw(n, itemsize, stride, x_ptr, buf_ptr)要点解析只读数组保护对不可写writeableFalse的数组直接抛出ValueError一维 ndarray 快速路径仅对恰好是ndarray类型而非子类、一维、非空的输入走静态类型化的底层 buffer 交换路径因为 MaskedArray 等子类可能不支持该方案引用计数安全交换使用int8字节缓冲避免在交换过程中让 Python 对象残留在缓冲区内、函数退出时被错误地减少引用计数线程锁with self.lock保证并发场景下随机状态不被破坏性能优化当itemsize sizeof(np.npy_intp)最常见情况时借助 gcc 生成专用实现可获得约 33% 的性能提升。PR #18328 修复的正是这些 C 层分布函数含_shuffle_raw底层实现在 C 编译环境下因缺少extern C链接约定而可能出现的链接失败或符号错乱问题。该问题在混合编译C/C 混编的构建链中尤为隐蔽。相关测试对象数组不被打散、只读保护同样在 numpy/random/tests/test_random.py 中还有两类相关测试test_shuffle_no_object_unpacking对dtypeobject数组 shuffle 后数组中元素的id()必须保持不变即不误拆解对象、不误改引用计数。测试还覆盖了ndarray子类场景传统 APIRandomState对一维数组子类调用 shuffle 时会发出UserWarning提示Shuffling a one dimensional array...而新 APIGenerator则给出正确结果test_shuffle_not_writeable对只读数组 shuffle 必须抛出ValueError匹配信息为read-only。这些测试共同锁定了 1.20.1 中shuffle的行为契约防止后续版本再次引入同类回归。其余 13 个修复从核心函数到 f2py、类型注解与 CI核心函数isclose/allclose与 timedelta64 的兼容PR #18337BUG: Allow unmodified use of isclose, allclose, etc. with timedelta允许isclose、allclose等函数在不修改参数的情况下直接用于timedelta64类型。在 numpy/_core/numeric.py 与 numpy/_core/numeric.py 中可以看到这两个函数的定义def allclose(a, b, rtol1.e-5, atol1.e-8, equal_nanFalse): Returns True if two arrays are element-wise equal within a tolerance. The tolerance values are positive, typically very small numbers. The relative difference (rtol * abs(b)) and the absolute difference ... def isclose(a, b, rtol1.e-5, atol1.e-8, equal_nanFalse): Returns a boolean array where two arrays are element-wise equal within a tolerance. ... 修复前对timedelta64数组直接调用isclose/allclose会因内部类型处理不当而失败或需用户手工改 dtype修复后用户可以直接使用默认参数比较时间间隔数组无需任何预处理。这对涉及时间序列、采样间隔比较的科学计算场景是实质性的可用性改进。DType 序列化允许 pickle 所有相关 DType 类型PR #18345BUG: Allow pickling all relevant DType types/classes修复了部分 DType 类型/类无法被 pickle 的问题。在 1.20 系列中DType 系统正经历向新 DType 协议相关设计参见 doc/neps/nep-0041-improved-dtype-support.rst过渡本次修复保证了所有相关 DType 类型与类均可被正常序列化避免使用multiprocessing或pickle传递 dtype 时出现TypeError。f2py修复包装 F90 子例程的缺陷PR #18354BUG: Fix f2py bugs when wrapping F90 subroutines与 PR #18356MAINT: crackfortran regex simplify共同处理了 f2py 相关问题#18354 修复了将 Fortran 90 子例程包装为 Python 可调用模块时的多个 bug#18356 简化了crackfortran中的正则表达式逻辑crackfortran是 f2py 用于解析 Fortran 源码的核心模块实现位于 numpy/f2py/crackfortran.py。对 Fortran 用户而言升级 1.20.1 后可更可靠地包装 F90 代码。类型注解与__init__修复PR #18306MAINT: Add missing placeholder annotations补充了缺失的占位类型注解placeholder annotations完善numpy的__init__.pyi/_core相关类型桩文件如 numpy/_core/init.pyi对 mypy/pyright 用户更友好PR #18310BUG: Fix typo innumpy.__init__.py修复了 numpy/init.py 中的一个拼写错误该错误在特定导入路径下可能引发问题。CI 与文档维护PR #18336CI: fix when GitHub Actions builds trigger, and allow ci skips修正 GitHub Actions 构建的触发时机并允许通过提交信息跳过 CIPR #18353CI: CircleCI seems to occasionally time out, increase the limitCircleCI 偶发超时提高超时上限PR #18352DOC: Change license date 2020 - 2021将许可证版权年份由 2020 更新为 2021。发布收尾PR #18359REL: Prepare for the NumPy 1.20.1 release是标准的发布准备提交负责更新版本号与发布元数据标志着 1.20.1 分支冻结并进入发布流程。升级建议与验证方式对于使用 NumPy 1.20.0 的用户尤其是以下三类场景建议尽快升级到 1.20.1下游 C/C/Fortran 扩展构建者命中 distutils fake libraries 遍历缺陷、signed_char依赖缺失或threads.h误判的平台升级后可恢复稳定构建随机数重度用户对 memoryview 或dtypeobject数组调用shuffle、或在混编环境下使用RandomState/Generator的程序升级后行为恢复正确时间序列分析者需要对timedelta64数组直接使用isclose/allclose的代码。升级后可通过仓库自带测试集验证核心行为需要已构建的开发环境python -m pytest numpy/random/tests/test_random.py -k shuffle python -m pytest numpy/random/tests/test_random.py -k memoryview or not_writeable第一条命令覆盖shuffle的回归测试含 memoryview、对象数组、只读保护第二条进一步聚焦 memoryview 与只读数组场景isclose/allclose对 timedelta 的验证可参考 numpy/_core/numeric.py 的文档示例运行。小结NumPy 1.20.1 虽然规模不大15 个 PR、8 位贡献者但精准地解决了 1.20.0 带来的两个高影响回归——distutils 构建缺陷与random.shuffle回归——并顺带修复了 f2py、threads.h探测、DType pickling、timedelta 比较等一系列问题。从本仓库源码与测试可以看到每一个 BUG 修复都伴随对应的回归测试如 numpy/random/tests/test_random.py 中的test_shuffle_memoryview这正是 NumPy 版本发布流程中快速修复 回归防护机制的典型体现。对于仍停留在 1.20.0 或更早版本的用户本版本是低风险、高收益的升级目标。【免费下载链接】numpyThe fundamental package for scientific computing with Python.项目地址: https://gitcode.com/gh_mirrors/nu/numpy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考