pytest捕获SystemExit时exc_info.value.code非真实整数退出码问题
pytest中可靠获取sys.exit真实退出码的方法
问题背景
在使用pytest验证sys.exit调用时,ExceptionInfo对象的value.code属性无法可靠获取真实的整数退出码,该属性仅存储传入sys.exit的原始值:
- 调用
sys.exit(0)时,exc_info.value.code为0(与真实退出码一致) - 调用
sys.exit()时,exc_info.value.code为None,但真实退出码为0 - 调用
sys.exit(1)时,exc_info.value.code为1(与真实退出码一致) - 调用
sys.exit('Trouble')时,exc_info.value.code为'Trouble',但真实退出码为1
只有当sys.exit传入整数时,exc_info.value.code才等于真实退出码;无参数或传入字符串时,该属性值与真实退出码不匹配。这是因为真实退出码是Python解释器后续处理SystemExit异常时确定的,并未直接存储在异常对象中(环境:Python 3.10.11,pytest 8.3.5)。
解决方案
由于SystemExit异常本身不携带最终的真实退出码,我们可以模拟Python解释器处理该异常的逻辑,编写一个辅助函数来计算真实退出码:
def get_real_exit_code(exc_info): code = exc_info.value.code if code is None: # 无参数调用sys.exit,真实退出码为0 return 0 elif isinstance(code, int): # 传入整数,直接返回该值 return code else: # 传入非整数(如字符串),真实退出码为1 return 1
修改后的测试用例
使用上述辅助函数,即可在所有场景下可靠断言真实退出码:
import re import sys import pytest def get_real_exit_code(exc_info): code = exc_info.value.code if code is None: return 0 elif isinstance(code, int): return code else: return 1 def test_sysexit_0(): with pytest.raises(SystemExit) as exc_info: sys.exit(0) # 预期退出码为0 assert get_real_exit_code(exc_info) == 0 def test_sysexit_2(): with pytest.raises(SystemExit) as exc_info: sys.exit(2) # 预期退出码为2 assert get_real_exit_code(exc_info) == 2 def test_sysexit_noargs(): with pytest.raises(SystemExit) as exc_info: sys.exit() # 预期退出码为0 assert get_real_exit_code(exc_info) == 0 def test_sysexit_msg(): with pytest.raises(SystemExit, match='cmd: bad usage') as exc_info: sys.exit('cmd: bad usage unrecognized option') # 预期退出码为1 assert get_real_exit_code(exc_info) == 1 assert re.match('cmd: bad usage', exc_info.value.code)
说明
Python解释器处理SystemExit的逻辑是:
- 如果异常的
code为None,退出码为0 - 如果
code是整数,退出码为该整数 - 如果
code是其他类型(如字符串),会先打印该值,然后退出码为1
通过模拟这一逻辑,我们就能在pytest中准确断言sys.exit对应的真实退出码。
内容的提问来源于stack exchange,提问作者ruck
相关产品推荐
相关产品推荐

