From a4834a8c07377e12a555b8d30397356fc1398254 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 17 Feb 2026 12:23:55 +0100 Subject: [PATCH] gh-141510: Use frozendict for errno.errorcode --- Doc/library/errno.rst | 3 +++ Lib/test/test_errno.py | 4 ++++ ...-02-17-12-26-53.gh-issue-141510.tQcHWz.rst | 2 ++ Modules/errnomodule.c | 20 +++++++++---------- 4 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-02-17-12-26-53.gh-issue-141510.tQcHWz.rst diff --git a/Doc/library/errno.rst b/Doc/library/errno.rst index 48b9762d85c2e1..210ff8ac0a1a42 100644 --- a/Doc/library/errno.rst +++ b/Doc/library/errno.rst @@ -18,6 +18,9 @@ all-inclusive. underlying system. For instance, ``errno.errorcode[errno.EPERM]`` maps to ``'EPERM'``. + .. versionchanged:: next + The dictionary type is now a :class:`frozendict`. + To translate a numeric error code to an error message, use :func:`os.strerror`. Of the following list, symbols that are not used on the current platform are not diff --git a/Lib/test/test_errno.py b/Lib/test/test_errno.py index e7f185c6b1a181..ee92b6f7017cc7 100644 --- a/Lib/test/test_errno.py +++ b/Lib/test/test_errno.py @@ -19,6 +19,10 @@ def test_using_errorcode(self): for value in errno.errorcode.values(): self.assertHasAttr(errno, value) + def test_readonly_errorcode(self): + with self.assertRaises(TypeError): + errno.errorcode[1] = 'hack' + class ErrorcodeTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2026-02-17-12-26-53.gh-issue-141510.tQcHWz.rst b/Misc/NEWS.d/next/Library/2026-02-17-12-26-53.gh-issue-141510.tQcHWz.rst new file mode 100644 index 00000000000000..1804fa9355e928 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-02-17-12-26-53.gh-issue-141510.tQcHWz.rst @@ -0,0 +1,2 @@ +:data:`errno.errorcode` is now read-only: use :class:`frozendict` type +instead of :class:`dict`. Patch by Victor Stinner. diff --git a/Modules/errnomodule.c b/Modules/errnomodule.c index 9557d68e759497..97959b05b04ed1 100644 --- a/Modules/errnomodule.c +++ b/Modules/errnomodule.c @@ -1,11 +1,5 @@ /* Errno module */ -// Need limited C API version 3.13 for Py_mod_gil -#include "pyconfig.h" // Py_GIL_DISABLED -#ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030d0000 -#endif - #include "Python.h" #include // EPIPE @@ -96,10 +90,6 @@ errno_exec(PyObject *module) if (error_dict == NULL) { return -1; } - if (PyDict_SetItemString(module_dict, "errorcode", error_dict) < 0) { - Py_DECREF(error_dict); - return -1; - } /* Macro so I don't have to edit each and every line below... */ #define add_errcode(name, code, comment) \ @@ -947,7 +937,17 @@ errno_exec(PyObject *module) add_errcode("ENOTCAPABLE", ENOTCAPABLE, "Capabilities insufficient"); #endif + PyObject *frozendict = PyFrozenDict_New(error_dict); Py_DECREF(error_dict); + if (frozendict == NULL) { + return -1; + } + if (PyDict_SetItemString(module_dict, "errorcode", frozendict) < 0) { + Py_DECREF(frozendict); + return -1; + } + Py_DECREF(frozendict); + return 0; }