news 2026/9/8 23:23:01

CPython errno 模块完全指南:标准系统错误符号、errorcode 字典与 OSError 异常映射

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CPython errno 模块完全指南:标准系统错误符号、errorcode 字典与 OSError 异常映射

CPython errno 模块完全指南:标准系统错误符号、errorcode 字典与 OSError 异常映射

【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython

本篇技术指南以 CPython 标准库文档 Doc/library/errno.rst 为主体,系统讲解errno模块如何对外暴露底层操作系统(Linux、macOS、Windows、WASI 等)的 errno 系统错误符号。读完本文,你将掌握:errno 符号与整数值的对应关系、errno.errorcode反向字典的使用方法、如何通过os.strerror把数值错误码翻译为可读错误消息,以及每个 errno 符号在 CPython 中如何自动映射到对应的OSError子类异常,从而写出健壮、可移植的系统级错误处理代码。

errno 模块是什么

errno是 CPython 的一个内建标准模块,作用是把操作系统底层的“errno 系统符号”原样暴露给 Python 层。每个符号对应的值就是操作系统使用的那个整数,例如在大多数系统上errno.ENOENT等于整数2。模块本身不新增任何业务语义,它是对平台 C 头文件<errno.h>中常量的一次系统化、按平台条件导出的镜像。

从实现看,该模块由 Modules/errnomodule.c 提供,其模块文档字符串(见 errnomodule.c)明确说明了这一用途:

The value of each symbol is the corresponding integer value, e.g., on most systems,errno.ENOENTequals the integer 2. The dictionaryerrno.errorcodemaps numeric codes to symbol names...

源码中的名称与英文注释“borrowed from linux/include/errno.h”(见 errnomodule.c),这一点与官方文档 errno.rst 中“names and descriptions are borrowed fromlinux/include/errno.h, which should be all-inclusive”的表述互相印证——即 errno 符号表设计上以 Linux 的 errno.h 为全集基准,力求“包罗万象”,同时兼顾 Solaris、macOS 等平台的独有符号。

核心数据对象:errorcode 字典

errno模块中最重要的数据对象是errno.errorcode,它是一个提供从 errno 整数值到字符串名称映射的字典。例如:

>>> import errno >>> errno.errorcode[errno.EPERM] # 取 EPERM 对应的字符串名称 'EPERM'

其用途与模块内的符号属性正好互补:

  • errno.EPERM(属性访问)——由符号名得到整数值;
  • errno.errorcode[errno.EPERM](字典查询)——由整数值得到符号名字符串。

在 C 实现中,errno模块的表结构正是“一个模块属性字典 + 一个 errorcode 字典”双向插入构建的。核心逻辑位于 errnomodule.c:

static int _add_errcode(PyObject *module_dict, PyObject *error_dict, const char *name_str, int code_int) { ... /* insert in modules dict */ if (PyDict_SetItem(module_dict, name, code) < 0) { ... } /* insert in errorcode dict */ if (PyDict_SetItem(error_dict, code, name) < 0) { ... } ... }

即每个符号通过add_errcode("ENOENT", ENOENT, "No such file or directory")这样的宏(见 errnomodule.c)同时写入两处,errno.errorcode则在模块执行入口errno_exec中先创建再填充(见 errnomodule.c)。

该模块在标准库中的配套测试 Lib/test/test_errno.py 对errorcode字典做了两条一致性约束:

  • errorcode 完整性errorcode中每个字符串值都必须能作为属性存在于errno模块上(见 test_errno.py);
  • 属性双向覆盖:模块__dict__中每一个全大写命名的属性,其整数值也都必须能反向在errorcode字典中查到(见 test_errno.py)。

这两条测试保证了“符号→数值→符号名”的双向查找永远不会断裂。

平台条件导出:并非所有符号在所有平台都可用

官方文档 errno.rst 明确提醒:在当前平台上未被使用的符号,模块就不会定义。也就是说errno的成员集合随操作系统/编译环境变化,同一份代码在不同平台看到的dir(errno)并不相同。

具体有哪些符号可用,可以通过下面两种方式查询:

import errno # 方式一:errorcode 的键集合即当前平台定义的全部符号值 print(errno.errorcode.keys()) # 方式二:直接列出模块中定义的全大写符号名 names = [k for k in errno.__dict__ if k.isupper()] print(len(names), "symbols defined on this platform")

这种按平台裁剪的机制在源码中体现得十分直接:C 代码中每个符号都包裹在条件编译#ifdef/#endif内(例如#ifdef ENODEV#ifdef EHOSTUNREACH,见 errnomodule.c),只有当前平台的系统头文件定义了该 errno 常量才会被注册进模块。因此:

  • 在 Linux 上可看到完整的EPERM~ERFKILL及 Linux 特有的ELOCKUNMAPPEDENOTACTIVE等;
  • macOS 额外携带EAUTHEBADARCHEBADEXECEBADMACHOEBADRPCEPROCLIM等“MacOSX specific errnos”(见 errnomodule.c);
  • Solaris 平台补充ECANCELEDENOTSUPEOWNERDEADENOTRECOVERABLE等“Solaris-specific errnos”(见 errnomodule.c);
  • WASI 平台特有的ENOTCAPABLE(“Capabilities insufficient”)在有#ifdef ENOTCAPABLE时也会注册(见 errnomodule.c)。

Windows 平台:WSA 套接字错误码的桥接

Windows 上errno.h与 Winsock 的错误码体系并不一致。为了让 Python 的 errno 符号保持跨平台一致语义,源码在 MS_WINDOWS 分支下做了特殊处理(见 errnomodule.c):先#undef掉那些在 VS2010 之后才被塞进errno.h、但实际应优先使用 WSA 等价值的常量(包括EADDRINUSEECONNRESETETIMEDOUTEWOULDBLOCK等约 25 个),随后在这些符号不存在于errno.h时改用对应WSAE*错误码来注册,例如:

#ifdef EHOSTUNREACH add_errcode("EHOSTUNREACH", EHOSTUNREACH, "No route to host"); #else #ifdef WSAEHOSTUNREACH add_errcode("EHOSTUNREACH", WSAEHOSTUNREACH, "No route to host"); #endif #endif

(见 errnomodule.c)。因此 Windows 上errno.EHOSTUNREACH的值实际来自 Winsock 的WSAEHOSTUNREACH,从而保证 Python 层网络代码只需面对统一的 errno 命名。

符号速查:errno 成员及含义总表

官方文档 errno.rst 给出了当前版本的完整符号列表。由于符号是按平台条件导出的,下表中的可用性以当前平台为准,可通过errno.errorcode.keys()实测。表中凡是文档标注了对应异常类型的条目,CPython 在抛出该 errno 时会自动转换为相应的OSError子类(详见下一节“异常自动映射”)。

通用核心符号(几乎所有 POSIX 平台可用)

errno 符号整型值含义(文档原文描述)自动映射的内置异常
EPERMOperation not permitted(操作不被允许)PermissionError
ENOENTNo such file or directory(无此文件或目录)FileNotFoundError
ESRCHNo such process(无此进程)ProcessLookupError
EINTRInterrupted system call(系统调用被中断)InterruptedError
EIOI/O error(I/O 错误)
ENXIONo such device or address(无此设备或地址)
E2BIGArg list too long(参数列表过长)
ENOEXECExec format error(可执行文件格式错误)
EBADFBad file number(文件描述符错误)
ECHILDNo child processes(无子进程)ChildProcessError
EAGAINTry again(资源暂不可用,请重试)BlockingIOError
ENOMEMOut of memory(内存不足)
EACCESPermission denied(权限被拒绝)PermissionError
EFAULTBad address(非法地址)
ENOTBLKBlock device required(需要块设备)
EBUSYDevice or resource busy(设备或资源忙)
EEXISTFile exists(文件已存在)FileExistsError
EXDEVCross-device link(跨设备链接)
ENODEVNo such device(无此设备)
ENOTDIRNot a directory(不是目录)NotADirectoryError
EISDIRIs a directory(是目录)IsADirectoryError
EINVALInvalid argument(参数无效)
ENFILEFile table overflow(系统文件表溢出)
EMFILEToo many open files(打开的文件过多)
ENOTTYNot a typewriter(不是终端设备)
ETXTBSYText file busy(文本文件正被占用)
EFBIGFile too large(文件过大)
ENOSPCNo space left on device(设备空间不足)
ESPIPEIllegal seek(非法定位)
EROFSRead-only file system(只读文件系统)
EMLINKToo many links(链接数过多)
EPIPEBroken pipe(管道破裂)BrokenPipeError
EDOMMath argument out of domain of func(数学参数超出定义域)
ERANGEMath result not representable(数学结果无法表示)
EDEADLKResource deadlock would occur(将发生资源死锁)
ENAMETOOLONGFile name too long(文件名过长)
ENOLCKNo record locks available(无可用记录锁)
ENOSYSFunction not implemented(功能未实现)
ENOTEMPTYDirectory not empty(目录非空)
ELOOPToo many symbolic links encountered(符号链接层数过多)
EWOULDBLOCKOperation would block(操作会阻塞)BlockingIOError

消息队列 / 流 / STREAMS 相关符号

errno 符号整型值含义备注
ENOMSGNo message of desired type
EIDRMIdentifier removed
ECHRNGChannel number out of range
EL2NSYNCLevel 2 not synchronized
EL3HLTLevel 3 halted
EL3RSTLevel 3 reset
ELNRNGLink number out of range
EUNATCHProtocol driver not attached
ENOCSINo CSI structure available
EL2HLTLevel 2 halted
EBADEInvalid exchange
EBADRInvalid request descriptor
EXFULLExchange full
ENOANONo anode
EBADRQCInvalid request code
EBADSLTInvalid slot
EDEADLOCKFile locking deadlock errorEDEADLK语义相近
EBFONTBad font file format
ENOSTRDevice not a stream
ENODATANo data available
ETIMETimer expired
ENOSROut of streams resources
ENONETMachine is not on the network
ENOPKGPackage not installed
EREMOTEObject is remote
ENOLINKLink has been severed
EADVAdvertise error
ESRMNTSrmount error
ECOMMCommunication error on send
EPROTOProtocol error
EMULTIHOPMultihop attempted
EDOTDOTRFS specific error
EBADMSGNot a data message
EOVERFLOWValue too large for defined data type
ENOTUNIQName not unique on network
EBADFDFile descriptor in bad state
EREMCHGRemote address changed
ELIBACCCan not access a needed shared library
ELIBBADAccessing a corrupted shared library
ELIBSCN.lib section in a.out corrupted
ELIBMAXAttempting to link in too many shared libraries
ELIBEXECCannot exec a shared library directly
EILSEQIllegal byte sequence(非法字节序列)
ERESTARTInterrupted system call should be restarted
ESTRPIPEStreams pipe error
EUSERSToo many users

网络 / 套接字相关符号

errno 符号整型值含义自动映射的内置异常
ENOTSOCKSocket operation on non-socket
EDESTADDRREQDestination address required
EMSGSIZEMessage too long
EPROTOTYPEProtocol wrong type for socket
ENOPROTOOPTProtocol not available
EPROTONOSUPPORTProtocol not supported
ESOCKTNOSUPPORTSocket type not supported
EOPNOTSUPPOperation not supported on transport endpoint
ENOTSUPOperation not supported3.2 起加入
EPFNOSUPPORTProtocol family not supported
EAFNOSUPPORTAddress family not supported by protocol
EADDRINUSEAddress already in use(地址已被占用)
EADDRNOTAVAILCannot assign requested address
ENETDOWNNetwork is down
ENETUNREACHNetwork is unreachable
ENETRESETNetwork dropped connection because of reset
ECONNABORTEDSoftware caused connection abortConnectionAbortedError
ECONNRESETConnection reset by peer(对端重置连接)ConnectionResetError
ENOBUFSNo buffer space available
EISCONNTransport endpoint is already connected
ENOTCONNTransport endpoint is not connected
ESHUTDOWNCannot send after transport endpoint shutdownBrokenPipeError
ETOOMANYREFSToo many references: cannot splice
ETIMEDOUTConnection timed out(连接超时)TimeoutError
ECONNREFUSEDConnection refused(连接被拒绝)ConnectionRefusedError
EHOSTDOWNHost is down
EHOSTUNREACHNo route to host
EALREADYOperation already in progressBlockingIOError
EINPROGRESSOperation now in progressBlockingIOError

文件系统高级特性 / 现代平台新增符号

errno 符号整型值含义版本 / 平台备注
ESTALEStale NFS file handle(NFS 文件句柄失效)NFS 相关
EUCLEANStructure needs cleaning
ENOTNAMNot a XENIX named type file
ENAVAILNo XENIX semaphores available
EISNAMIs a named type file
EREMOTEIORemote I/O error
EDQUOTQuota exceeded(磁盘配额超限)
EQFULLInterface output queue is full3.11 起加入
ENOMEDIUMNo medium found
EMEDIUMTYPEWrong medium type
ENOKEYRequired key not available
EKEYEXPIREDKey has expired
EKEYREVOKEDKey has been revoked
EKEYREJECTEDKey was rejected by service
ERFKILLOperation not possible due to RF-kill
ELOCKUNMAPPEDLocked lock was unmappedLinux 特有
ENOTACTIVEFacility is not activeLinux 特有
EAUTHAuthentication error3.2 起加入,macOS/BSD
EBADARCHBad CPU type in executable3.2 起加入,macOS
EBADEXECBad executable (or shared library)3.2 起加入,macOS
EBADMACHOMalformed Mach-o file3.2 起加入,macOS
EBADRPCRPC struct is bad3.2 起加入,macOS
EDEVERRDevice error3.2 起加入,macOS
EFTYPEInappropriate file type or format3.2 起加入
ENEEDAUTHNeed authenticator3.2 起加入,macOS
ENOATTRAttribute not found3.2 起加入,macOS
ENOPOLICYPolicy not found3.2 起加入,macOS
EPROCLIMToo many processes3.2 起加入,macOS
EPROCUNAVAILBad procedure for program3.2 起加入,macOS
EPROGMISMATCHProgram version wrong3.2 起加入,macOS
EPROGUNAVAILRPC prog. not avail3.2 起加入,macOS
EPWROFFDevice power is off3.2 起加入,macOS
ERPCMISMATCHRPC version wrong3.2 起加入,macOS
ESHLIBVERSShared library version mismatch3.2 起加入,macOS
ECANCELEDOperation canceled3.2 起加入
EOWNERDEADOwner died3.2 起加入
ENOTRECOVERABLEState not recoverable3.2 起加入
ENOTCAPABLECapabilities insufficient3.11.1 起加入;WASI、FreeBSD
EHWPOISONMemory page has hardware error(内存页硬件故障)3.14 起加入

各符号的.. versionadded::标注与其在源码中的注释一致:3.11 引入EQFULL,3.14 引入EHWPOISON,3.11.1 引入 WASI/FreeBSD 专用的ENOTCAPABLE,而 macOS/BSD 系列(EAUTHECANCELEDEOWNERDEADENOTRECOVERABLE等)大多在 3.2 起随平台支持加入。

errno 与 OSError 子类的自动映射

文档中大量条目标注了“This error is mapped to the exception :exc:PermissionError”等映射说明,这是errno模块最具工程价值的部分:CPython 会在解释器内部维护 errno 整数值 →OSError子类的映射表(errnomap),当底层 C 调用因某个 errno 失败时,Python 层抛出的不是笼统的OSError,而是更具体的异常子类,便于精细化except

该映射表在 Objects/exceptions.c 的_PyOSError_Init中通过ADD_ERRNO(TYPE, CODE)宏逐一建立,与文档的标注一一对应:

errno映射到的异常子类异常含义
EAGAIN/EALREADY/EINPROGRESS/EWOULDBLOCKBlockingIOError操作会阻塞(非阻塞 I/O 场景)
EPIPEESHUTDOWN(有定义时)BrokenPipeError管道破裂/已关闭连接后发送
ECHILDChildProcessError无子进程可等待
ECONNABORTEDConnectionAbortedError连接被中止
ECONNREFUSEDConnectionRefusedError连接被拒绝
ECONNRESETConnectionResetError连接被对端重置
EEXISTFileExistsError文件已存在
ENOENTFileNotFoundError文件/目录不存在
EISDIRIsADirectoryError目标是目录
ENOTDIRNotADirectoryError不是目录
EINTRInterruptedError系统调用被信号中断
EACCES/EPERMENOTCAPABLE(有定义时)PermissionError权限不足
ESRCHProcessLookupError进程不存在
ETIMEDOUT(含 WindowsWSAETIMEDOUTTimeoutError操作超时

需要注意的工程要点:

  1. 映射仅对OSError本身生效errnomap的查找逻辑位于 OSError 的类型初始化与构造路径(见 Objects/exceptions.c),只有以OSError(...)方式构造、并且 type 恰好是OSError时才会把errno数值“翻译”成最匹配的子类。而异常机制每次最终抛出时都会经过这一映射。
  2. errnomap是逐解释器(interpreter)独立的,由struct _Py_exc_state持有(见 Objects/exceptions.c),多解释器环境下互不干扰。
  3. 文档中“symbols available can include”是保守表述——最终以errno.errorcode.keys()实测结果为准,切勿在代码里硬编码某个平台可能没有的符号(如直接在 Windows 上依赖 Linux 的ELOCKUNMAPPED),应先hasattr(errno, 'ELOCKUNMAPPED')判断。

从数值错误码到异常/消息的完整翻译链路

把三者串起来,一次完整的“底层错误 → Python 异常”翻译链路是:

  1. C 层系统调用失败后设置errno(如ENOENT = 2);
  2. 解释器依据 Objects/exceptions.c 的errnomap找到对应OSError子类(FileNotFoundError)并抛出;
  3. 若代码想拿到可读消息,可用os.strerror(errno)os.strerror(code)定义在 Doc/library/os.rst,对应 C 的strerror(),在未知错误码可能返回NULL的平台上会回退处理。
import errno, os print(errno.ENOENT) # 2(大多数系统) print(errno.errorcode[errno.ENOENT]) # 'ENOENT' print(os.strerror(errno.ENOENT)) # 'No such file or directory'(随系统 locale)

官方文档强调,把数值错误码翻译成错误消息应当使用os.strerror,而不是errno模块本身——errno只负责“符号⇄数值”的映射,消息文本由操作系统提供。

实战:用 errno 写出可移植的错误处理

场景一:区分“文件不存在”和“权限不足”

import errno import os try: with open("/etc/shadow") as f: # 典型权限受限文件 print(f.read()) except PermissionError: print("没有权限读取该文件") except FileNotFoundError: print("文件不存在")

这正是errnomap自动映射的价值:你不需要每次比较e.errno == errno.EACCESexcept PermissionError就能精准命中EACCESEPERM引发的失败。

场景二:非阻塞 I/O 的“资源暂时不可用”判断

网络编程中EAGAIN/EWOULDBLOCK(非阻塞模式下暂时无数据可读)在 CPython 中被统一映射为BlockingIOError

import errno import socket sock = socket.socket() sock.setblocking(False) try: sock.connect(("example.com", 80)) except BlockingIOError as e: # connect 尚未完成,errno 可能是 EINPROGRESS / EALREADY if e.errno in (errno.EINPROGRESS, errno.EALREADY): print("连接进行中,可交由事件循环稍后检查")

场景三:按 errno 数值做跨平台兜底

当目标异常子类覆盖不全、或需要处理“无专有子类”的 errno(如ENOSPCENOMEM)时,仍可退回检查e.errno

import errno try: with open("/tmp/bigfile", "wb") as f: f.write(b"\0" * (10**12)) except OSError as e: if e.errno == errno.ENOSPC: print("磁盘空间不足,请清理后再试") elif e.errno == errno.EDQUOT: print("超出磁盘配额") else: print(f"其他 I/O 错误 errno={e.errno}: {e.strerror}")

跨平台前务必用hasattr做能力探测,例如 WASI/FreeBSD 之外的平台没有ENOTCAPABLE

CAP = getattr(errno, "ENOTCAPABLE", None) if CAP is not None and e.errno == CAP: print("capability 不足")

场景四:EINTR 中断后的自动重试

EINTR(系统调用被信号中断)映射为InterruptedError。早期 Python 代码常需手动重试,如今多数系统调用会自动重启,但显式处理仍常见于信号密集的程序:

import errno import os while True: try: data = os.read(fd, 4096) break except InterruptedError: continue # errno == EINTR,重试系统调用

errorcode反向查名,输出可读日志

记录日志时把数值还原为符号名,便于排障阅读:

import errno def describe(e): code = getattr(e, "errno", None) if code is not None and code in errno.errorcode: return f"errno={code} ({errno.errorcode[code]}) {e.strerror}" return repr(e) try: 1 / len([]) except OSError as e: print(describe(e)) # 例如:errno=13 (EACCES) Permission denied

errno 模块的构建与运行机制(进阶)

从 Modules/errnomodule.c 可以进一步看到该模块在 CPython 内部的设计细节:

  • 无方法、纯数据模块:模块方法表errno_methods为空(见 errnomodule.c),全部内容都是初始化期写入的属性与字典;
  • 使用多阶段初始化(multi-phase init):通过PyModuleDef_Slot声明Py_mod_exec钩子,真正填表工作放在errno_exec中完成(见 errnomodule.c),从而支持子解释器场景;
  • 自由线程(free-threaded)适配:slot 表中显式声明了Py_mod_gil = Py_MOD_GIL_NOT_USED,表明该模块可在无 GIL 构建下安全运行;模块在Py_GIL_DISABLED构建下不强制Py_LIMITED_API(见 errnomodule.c);
  • 单条代码路径双向插入_add_errcode同时维护“模块符号属性”和“errorcode 字典”,从根上保证二者永不失配,配套测试 Lib/test/test_errno.py 进一步守护这一不变式。

总结

errno模块虽小,却是 CPython 打通“操作系统错误码 ↔ Python 层符号 ↔ OSError 异常子类”三方的关键枢纽:

  • 符号⇄数值errno.EPERMerrno.errorcode[errno.EPERM]双向映射,随平台条件导出(见 Modules/errnomodule.c);
  • 数值→消息:交给os.strerror(见 Doc/library/os.rst);
  • 数值→异常子类:由解释器内部errnomap(见 Objects/exceptions.c)完成,让except FileNotFoundErrorexcept PermissionError等写法成为可能。

理解这三层关系后,你写出的错误处理代码既能在 Linux/macOS/Windows/WASI 之间平滑移植,又能准确、可读地向调用方表达失败原因——这正是标准库把操作系统细节“翻译”给 Python 程序员的完整故事。

【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 23:20:50

2026大模型学习路线图:从零基础到项目实战,程序员小白必看指南,存一下吧很难找全的!_ai大模型学习路线图

本文提供AI大模型系统学习路线&#xff0c;分四个阶段&#xff1a;入门阶段掌握Python、数学基础及机器学习&#xff1b;中级阶段深入学习算法并实践项目&#xff1b;进阶阶段学习自然语言处理、计算机视觉等&#xff1b;高级阶段探索深度强化学习和生成模型。文章还包含学习资…

作者头像 李华
网站建设 2026/9/8 23:20:15

免费抓包嗅探下载视频号与抖音资源:res-downloader 快速上手指南

免费抓包嗅探下载视频号与抖音资源&#xff1a;res-downloader 快速上手指南 【免费下载链接】res-downloader 视频号、小程序、抖音、快手、小红书、直播流、m3u8、酷狗、QQ音乐等常见网络资源下载! 项目地址: https://gitcode.com/GitHub_Trending/re/res-downloader …

作者头像 李华
网站建设 2026/9/8 23:17:49

Isaac Lab 3.0.0实战:齿轮插入任务Sim2Real迁移全攻略

把机械臂从仿真搬到真实产线&#xff0c;中间隔着的那道鸿沟到底有多大&#xff0c;没做过的人真的很难体会。我见过太多团队&#xff0c;仿真里跑得飞快的策略&#xff0c;一上真机就像丢了魂一样——位姿偏一点就怼不进去&#xff0c;光照变一下就抓不准孔位&#xff0c;摩擦…

作者头像 李华