ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

pytest 2.8.3 发布解析:unittest 集成修复、收集器健壮性与 traceback 过滤改进

pytest 2.8.3 发布解析:unittest 集成修复、收集器健壮性与 traceback 过滤改进 pytest 2.8.3 发布解析unittest 集成修复、收集器健壮性与 traceback 过滤改进【免费下载链接】pytestThe pytest framework makes it easy to write small tests, yet scales to support complex functional testing项目地址: https://gitcode.com/GitHub_Trending/py/pytestpytest 2.8.3 是一个以修复 bug 为主的维护版本宣称与 2.8.2 完全向后兼容drop-in compatible。本文基于 release-2.8.3.rst 官方发布说明结合当前仓库源码与测试用例逐条剖析本次发布的关键变更TestCaseFunction的__name__属性修复、带__getattr__模块的收集问题、不可报告测试的收集拦截、traceback 过滤的内部错误、失败测试标题标红等。读完本文你将理解这些历史 bug 的成因、对应修复在源码中的落点以及它们对日常测试编写的影响。说明2.8.3 是历史发布版本当前仓库源码已演进到更新的 pytest 主分支。文中引用的源码路径、行号与实现以当前仓库为准用以佐证当年修复思路的延续与沉淀命令行与配置行为部分以当前仓库实现为准。一、发布背景与兼容性承诺2.8.3 的发布说明开门见山地强调了几点核心信息pytest 是一个成熟的 Python 测试工具当时已拥有超过 1100 个自测用例并能在多种解释器与平台上通过本次发布应与 2.8.2 无缝兼容drop-in compatible即升级不需要改动现有测试代码升级方式与以往一致直接从 PyPI 安装pip install -U pytest发布说明还按惯例列出了本次贡献者名单包括 Bruno Oliveira、Gabriel Reis、Lee Kamentsky、Ronny Pfannschmidt 等人。这说明 2.8.x 时代 pytest 已经形成稳定的社区协作流程——每个修复对应一个 GitHub issue/PR并归功到具体贡献者。从变更内容看2.8.3 的定位是典型的稳定化补丁不引入新的大功能而是集中修复 unittest 集成、测试收集、错误报告三个方向的缺陷外加若干文档与构建集成改进。二、修复 unittest 集成TestCaseFunction缺失__name__Issue #1169为TestCaseFunction增加__name__属性以支持在函数和方法上使用unittest.skip装饰器。该修复由 Lee Kamentsky 提交 PR。问题成因在 Python 的unittest中unittest.skip(reason)装饰器会调用被装饰函数/方法的__name__属性来构造跳过的表示TestLoader.getTestCaseNames与SkipTest机制依赖方法名。当 pytest 以TestCaseFunction形式收集unittest.TestCase子类中的测试方法时如果该节点对象上不存在__name__属性unittest.skip在部分路径下会抛出AttributeError导致跳过无法正确登记。源码落点在 src/_pytest/unittest.py 中TestCaseFunction继承自Function并通过from_parent从UnitTestCase收集器创建见 L117 与 L125 的yield TestCaseFunction.from_parent(...)。当前实现里TestCaseFunction额外提供了_getinstance()、_testcase兼容属性等而函数对象的__name__语义则由基类Function通过 nodeid/名称构造来保证。测试验证当前仓库 testing/test_unittest.py 保留着针对该 issue 的回归测试test_unittest_skip_issue1169pytester def test_unittest_skip_issue1169(pytester: Pytester) - None: ... pytester.makepyfile( import unittest class MyTestCase(unittest.TestCase): unittest.skip(skipping due to reasons) def test_skip(self): pass ) result pytester.runpytest(-v) ... result.stdout.fnmatch_lines([*SKIP*[1]*skipping due to reasons*]) result.stdout.fnmatch_lines([*1 skipped*])它验证unittest.skip装饰的unittest.TestCase方法能够被正确报告为SKIP。同一文件中还有test_unittest_skip_issue148testing/test_unittest.py等一组 skip 场景回归测试共同守护 unittest 跳过语义不被破坏。对用户的意义在 2.8.2 及更早版本中在unittest.TestCase的方法上直接写unittest.skip(...)可能无法被 pytest 正确识别升级到 2.8.3 后该写法可正常工作pytest 会按预期输出SKIP并统计为1 skipped。这也体现了 pytest 作为 unittest 运行器的兼容策略既要支持 pytest 自己的pytest.mark.skip也要完整支持标准库unittest的跳过机制。三、修复收集逻辑模块级__getattr__不再导致误判Issue #1035修复测试模块顶层对象定义了__getattr__()时收集测试出错的问题。报告来自 Suor修复 PR 由 Bruno Oliveira / Tom Viner 提交。问题成因pytest 收集测试函数/类时会通过属性访问如getattr(module, name)来获取模块中的对象。若测试模块本身定义了自定义__getattr__通常用于动态属性或对象是带有自定义__getattr__的类那么任意属性名都可能返回一个看起来像测试的可调用对象——即便它根本不是测试。这会导致收集器把不该收集的东西当成测试收集进来或在遍历dir()与真实属性不一致时抛出异常。源码落点当前 src/_pytest/python.py 的isnosetest注释明确记录了这条修复思路的延续# We explicitly check for is True here to not mistakenly treat # classes with a custom __getattr__ returning something truthy (like a # function) as test classes. return safe_getattr(obj, __test__, False) is TruePyCollector.collect()也遵循类似原则src/_pytest/python.py# Avoid random getattrs and peek in the __dict__ instead. dicts [getattr(self.obj, __dict__, {})]即收集模块/类成员时优先窥探__dict__避免触发随机的__getattr__副作用。这是对 #1035 的架构级加固——判断一个名字是否为测试不再依赖调属性取回对象这一可能被__getattr__劫持的路径。对用户的意义如果你的测试模块顶层或测试类出于业务需要定义了__getattr__例如动态代理模块属性升级到 2.8.3 后收集过程会稳定得多__getattr__返回的值不会再被错误当作测试函数或测试类收集也避免了因此引发的收集期报错。四、收集拦截无法正确报告失败的测试不再被收集Issue #331不收集失败无法被正确报告的测试——典型场景是类的可调用实例callable instance of a class。问题成因如果一个模块级对象的实例例如定义了__call__的类的实例恰好满足 pytest 的函数名过滤规则如以test_开头pytest 会尝试把它当作测试项收集。但当它执行失败时由于它不是一个真正的函数其 traceback/代码定位__code__、__name__、源码位置等缺失或不完整pytest 无法构造正确的失败报告甚至会因此产生内部错误把测试失败变成pytest 自身崩溃。设计思路2.8.3 采取的策略是宁可少收集也不要产生无法解释的崩溃当 pytest 判断一个对象被收集后无法得到正确失败报告时例如它是可调用实例而非普通函数直接跳过收集。这一防御性设计保证了收集结果与报告能力的自洽是测试框架健壮性的重要一环——任何被报告的测试都必须是框架有能力讲清楚为什么失败的测试。五、traceback 过滤的内部错误文件已不存在的 traceback 条目Issue #1133修复过滤 traceback 时某个条目所属文件已不可用时出现内部错误。修复 PR 由 Bruno Oliveira 提交。问题成因测试执行过程中pytest 需要对 traceback 做过滤隐藏框架内部_pytest、pluggy等的栈帧只展示用户代码。过滤时要对每个栈帧做路径判断。若某条 traceback 条目指向的源文件在执行时已被删除、移动或不再存在旧的路径处理逻辑会得到字符串而非Path对象后续的父目录判断/存在性操作就可能抛出TypeError/AttributeError把测试失败升级成pytest 内部错误。源码落点当前 src/_pytest/_code/code.py 的filter_traceback完整继承了这条防御逻辑并在注释中直接点名该 issuedef filter_traceback(entry: TracebackEntry) - bool: ... We hide traceback entries of: * dynamically generated code (no code to show up for it); * internal traceback from pytest or its internal libraries, py and pluggy. # entry.path might point to a non-existing file, in which case it will # also return a str object. See #1133. p Path(entry.path) ...实现要点动态生成代码raw_filename形如...的条目如exec/eval产生的帧直接过滤掉L1619-L1622文件不存在的条目将entry.path统一转换为PathL1626避免字符串/路径类型不一致引发的内部错误内部库路径凡父目录命中pluggy或_pytest安装目录的条目一律隐藏L1629-L1632保证最终展示的 traceback 干净聚焦。filter_traceback会被_filter_tracebackexception递归应用到异常链__cause__/__context__上src/_pytest/_code/code.py因此即使异常层层包装例如ExceptionGroup、from ... raise链过滤逻辑也能在每一层安全执行。对用户的意义在动态生成/临时清理的代码场景如测试中动态exec出文件后删除、临时目录中的测试文件被中途清理中失败报告不再抛出过滤 traceback 时二次崩溃的内部错误而是给出正常的失败摘要。这一修复让 pytest 在文件消失这类边缘环境下仍能优雅收场。六、可读性改进失败测试名称以红色高亮本次发布还包含一个交互增强PR by Gabriel Reis在输出中把失败测试的名称标红使其在大量输出中一眼可辨。源码落点当前 src/_pytest/terminal.py 的失败摘要中每个失败测试的标题行通过write_sep以红色加粗输出for rep in reports: msg self._getfailureheadline(rep) self.write_sep(_, msg, redTrue, boldTrue) self._outrep_summary(rep) self._handle_teardown_sections(rep.id)错误摘要summary_errors同样采用红色标题src/_pytest/terminal.py并在收集错误前追加ERROR collecting 前缀、在 setup/teardown 错误前追加ERROR at when of 前缀方便区分失败阶段。终局统计中failed/error等类别也统一映射为红色src/_pytest/terminal.py。从 src/_pytest/terminal.py 可以看到 pytest 通过颜色映射markup与TerminalWriter的样式能力实现高亮——颜色渲染依赖终端支持 ANSI 色彩--colorno等选项可以关闭。对于在 CI 日志或不支持颜色的终端中阅读输出红色标题与分隔线让哪里失败了一目了然这正是当年该增强的初衷。七、配套改进文档、--ignore说明与构建集成除上述功能修复外2.8.3 还包含若干配套变更文档增加更多演讲资料talks补充了社区技术分享的收录扩展--ignore命令行选项的文档让用户更清楚如何排除特定路径。当前 src/_pytest/main.py 中--ignore与--ignore-glob依然存在其实际生效逻辑在pytest_ignore_collect钩子中实现src/_pytest/main.py--ignore精确排除给定路径通过absolutepath归一化后比对--ignore-glob则按 glob 模式匹配排除二者也可与配置中的collect_ignore/collect_ignore_glob协同改用 pytest-runner 做 setuptools 集成发布/安装流程的工程化改进适配 OS X El Capitan 的系统完整性保护SIP对 macOS 新安全机制的兼容性修复。这些条目虽然不直接改变测试语义但共同体现了维护型版本全维度打磨的特征文档、安装、平台兼容与核心功能同步推进。八、小结pytest 2.8.3 是一个高质量的小版本四个核心 bug 修复全部指向真实世界的边界场景——unittest.skip的属性缺失、模块级__getattr__对收集的干扰、可调用实例的不可报告失败、已消失源文件引发的 traceback 过滤崩溃。它们在后继版本中演化为 src/_pytest/unittest.py、src/_pytest/python.py、src/_pytest/_code/code.py 等处的稳定实现并被 testing/test_unittest.py 等回归测试长期守护。如果你正在阅读 pytest 的历史版本发布说明可以按此思路继续追踪相邻版本如 release-2.8.2 与 release-2.9.0的变更观察收集可靠性 → 报告可靠性 → 输出可读性 → 平台兼容性这条维护脉络如何逐步夯实。对于今天的 pytest 用户而言理解这些小版本修复背后的防御性设计窥探__dict__而非盲信__getattr__、统一Path化再判路径、失败必可报告也能帮助你在编写自定义插件或 hook 时规避同类陷阱。【免费下载链接】pytestThe pytest framework makes it easy to write small tests, yet scales to support complex functional testing项目地址: https://gitcode.com/GitHub_Trending/py/pytest创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表