From 575391f727b7f8d05c86d5020c9b14bb094f17a2 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 23 Aug 2026 11:05:09 +0300 Subject: [PATCH] gh-156261: Use the uid_t and gid_t converters in the pwd and grp modules They are moved to Argument Clinic, so that pwd.getpwuid() and grp.getgrgid() can declare the type of their parameter instead of converting it in the "impl" function. pwd.getpwuid() now raises OverflowError instead of KeyError for a user id out of the range of uid_t, as grp.getgrgid() already did. --- Lib/test/test_clinic.py | 2 ++ Lib/test/test_pwd.py | 4 ++-- ...-08-22-19-00-00.gh-issue-156261.Nq4tWv.rst | 3 +++ ...-08-23-18-10-00.gh-issue-156261.Rw8pKd.rst | 6 +++--- Modules/clinic/grpmodule.c.h | 12 ++++++----- Modules/clinic/pwdmodule.c.h | 20 ++++++++++++++++++- Modules/grpmodule.c | 10 +++------- Modules/posixmodule.c | 10 +--------- Modules/pwdmodule.c | 13 +++--------- Tools/clinic/libclinic/converters.py | 10 ++++++++++ 10 files changed, 53 insertions(+), 37 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-08-22-19-00-00.gh-issue-156261.Nq4tWv.rst diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index d07447d66571e52..76df4f23a49a682 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -3569,6 +3569,7 @@ def test_cli_converters(self): "DWORD", "fildes", "float", + "gid_t", "HANDLE", "int", "long", @@ -3587,6 +3588,7 @@ def test_cli_converters(self): "size_t", "slice_index", "str", + "uid_t", "uint16", "uint32", "uint64", diff --git a/Lib/test/test_pwd.py b/Lib/test/test_pwd.py index 82acce85f1db572..2ad89444348376d 100644 --- a/Lib/test/test_pwd.py +++ b/Lib/test/test_pwd.py @@ -61,8 +61,8 @@ def test_errors(self): self.assertRaises(TypeError, pwd.getpwuid, 0.0) self.assertRaises(TypeError, pwd.getpwuid, 0, 0) # should be out of uid_t range - self.assertRaises(KeyError, pwd.getpwuid, 2**128) - self.assertRaises(KeyError, pwd.getpwuid, -2**128) + self.assertRaises(OverflowError, pwd.getpwuid, 2**128) + self.assertRaises(OverflowError, pwd.getpwuid, -2**128) self.assertRaises(TypeError, pwd.getpwnam) self.assertRaises(TypeError, pwd.getpwnam, 42) self.assertRaises(TypeError, pwd.getpwnam, b'root') diff --git a/Misc/NEWS.d/next/Library/2026-08-22-19-00-00.gh-issue-156261.Nq4tWv.rst b/Misc/NEWS.d/next/Library/2026-08-22-19-00-00.gh-issue-156261.Nq4tWv.rst new file mode 100644 index 000000000000000..a641f8371b368fd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-22-19-00-00.gh-issue-156261.Nq4tWv.rst @@ -0,0 +1,3 @@ +:func:`pwd.getpwuid` now raises :exc:`OverflowError` instead of +:exc:`KeyError` if the user id is out of the range of the C ``uid_t`` type, +as :func:`grp.getgrgid` does for the group id. diff --git a/Misc/NEWS.d/next/Tools-Demos/2026-08-23-18-10-00.gh-issue-156261.Rw8pKd.rst b/Misc/NEWS.d/next/Tools-Demos/2026-08-23-18-10-00.gh-issue-156261.Rw8pKd.rst index da5e42dfa744297..d2f55f626f6d329 100644 --- a/Misc/NEWS.d/next/Tools-Demos/2026-08-23-18-10-00.gh-issue-156261.Rw8pKd.rst +++ b/Misc/NEWS.d/next/Tools-Demos/2026-08-23-18-10-00.gh-issue-156261.Rw8pKd.rst @@ -1,3 +1,3 @@ -Argument Clinic: the ``pid_t``, ``Py_off_t``, ``HANDLE``, ``DWORD`` and -``BOOL`` converters, previously defined in 9 different files, are now -provided by Argument Clinic itself. +Argument Clinic: the ``pid_t``, ``uid_t``, ``gid_t``, ``Py_off_t``, +``HANDLE``, ``DWORD`` and ``BOOL`` converters, previously defined in +individual files, are now provided by Argument Clinic itself. diff --git a/Modules/clinic/grpmodule.c.h b/Modules/clinic/grpmodule.c.h index 665c2f2dfdac148..9f1ea181d9bc4ca 100644 --- a/Modules/clinic/grpmodule.c.h +++ b/Modules/clinic/grpmodule.c.h @@ -20,7 +20,7 @@ PyDoc_STRVAR(grp_getgrgid__doc__, {"getgrgid", _PyCFunction_CAST(grp_getgrgid), METH_FASTCALL|METH_KEYWORDS, grp_getgrgid__doc__}, static PyObject * -grp_getgrgid_impl(PyObject *module, PyObject *id); +grp_getgrgid_impl(PyObject *module, gid_t gid); static PyObject * grp_getgrgid(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -54,15 +54,17 @@ grp_getgrgid(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject }; #undef KWTUPLE PyObject *argsbuf[1]; - PyObject *id; + gid_t gid; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } - id = args[0]; - return_value = grp_getgrgid_impl(module, id); + if (!_Py_Gid_Converter(args[0], &gid)) { + goto exit; + } + return_value = grp_getgrgid_impl(module, gid); exit: return return_value; @@ -152,4 +154,4 @@ grp_getgrall(PyObject *module, PyObject *Py_UNUSED(ignored)) { return grp_getgrall_impl(module); } -/*[clinic end generated code: output=35aa81c00dbd3229 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9052db62d986262c input=a9049054013a1b77]*/ diff --git a/Modules/clinic/pwdmodule.c.h b/Modules/clinic/pwdmodule.c.h index 43d4825031c7e61..64127ff8e494db8 100644 --- a/Modules/clinic/pwdmodule.c.h +++ b/Modules/clinic/pwdmodule.c.h @@ -15,6 +15,24 @@ PyDoc_STRVAR(pwd_getpwuid__doc__, #define PWD_GETPWUID_METHODDEF \ {"getpwuid", (PyCFunction)pwd_getpwuid, METH_O, pwd_getpwuid__doc__}, +static PyObject * +pwd_getpwuid_impl(PyObject *module, uid_t uid); + +static PyObject * +pwd_getpwuid(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + uid_t uid; + + if (!_Py_Uid_Converter(arg, &uid)) { + goto exit; + } + return_value = pwd_getpwuid_impl(module, uid); + +exit: + return return_value; +} + PyDoc_STRVAR(pwd_getpwnam__doc__, "getpwnam($module, name, /)\n" "--\n" @@ -73,4 +91,4 @@ pwd_getpwall(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef PWD_GETPWALL_METHODDEF #define PWD_GETPWALL_METHODDEF #endif /* !defined(PWD_GETPWALL_METHODDEF) */ -/*[clinic end generated code: output=5a8fb12939ff4ea3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5bca0b2c2f4c5e89 input=a9049054013a1b77]*/ diff --git a/Modules/grpmodule.c b/Modules/grpmodule.c index 32ead2598036146..fe1d16d8322005c 100644 --- a/Modules/grpmodule.c +++ b/Modules/grpmodule.c @@ -117,7 +117,7 @@ mkgrent(PyObject *module, struct group *p) /*[clinic input] grp.getgrgid - id: object + id as gid: gid_t Return the group database entry for the given numeric group ID. @@ -125,18 +125,14 @@ If id is not valid, raise KeyError. [clinic start generated code]*/ static PyObject * -grp_getgrgid_impl(PyObject *module, PyObject *id) -/*[clinic end generated code: output=30797c289504a1ba input=15fa0e2ccf5cda25]*/ +grp_getgrgid_impl(PyObject *module, gid_t gid) +/*[clinic end generated code: output=a9e7385cd6df08da input=fca15128dd772588]*/ { PyObject *retval = NULL; int nomem = 0; char *buf = NULL, *buf2 = NULL; - gid_t gid; struct group *p; - if (!_Py_Gid_Converter(id, &gid)) { - return NULL; - } #ifdef HAVE_GETGRGID_R int status; Py_ssize_t bufsize; diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index a9375e48a11d899..9724187dda4bb6c 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -3185,14 +3185,6 @@ class dir_fd_converter(CConverter): def c_default_init(self): self.c_default = 'DEFAULT_DIR_FD' -class uid_t_converter(CConverter): - type = "uid_t" - converter = '_Py_Uid_Converter' - -class gid_t_converter(CConverter): - type = "gid_t" - converter = '_Py_Gid_Converter' - class dev_t_converter(CConverter): type = 'dev_t' converter = '_Py_Dev_Converter' @@ -3249,7 +3241,7 @@ class confname_converter(CConverter): """, argname=argname, converter=self.converter, table=self.table) [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=e459765bdf453ebf]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=7ceccf55bb600f61]*/ /*[clinic input] diff --git a/Modules/pwdmodule.c b/Modules/pwdmodule.c index 4a2b33f8700d101..be5e0d4d0c29ae3 100644 --- a/Modules/pwdmodule.c +++ b/Modules/pwdmodule.c @@ -121,7 +121,7 @@ mkpwent(PyObject *module, struct passwd *p) /*[clinic input] pwd.getpwuid - uidobj: object + uidobj as uid: uid_t / Return the password database entry for the given numeric user ID. @@ -130,21 +130,14 @@ See `help(pwd)` for more on password database entries. [clinic start generated code]*/ static PyObject * -pwd_getpwuid(PyObject *module, PyObject *uidobj) -/*[clinic end generated code: output=c4ee1d4d429b86c4 input=ae64d507a1c6d3e8]*/ +pwd_getpwuid_impl(PyObject *module, uid_t uid) +/*[clinic end generated code: output=631bad376fa670c3 input=506d3a592ef19799]*/ { PyObject *retval = NULL; - uid_t uid; int nomem = 0; struct passwd *p; char *buf = NULL, *buf2 = NULL; - if (!_Py_Uid_Converter(uidobj, &uid)) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) - PyErr_Format(PyExc_KeyError, - "getpwuid(): uid not found"); - return NULL; - } #ifdef HAVE_GETPWUID_R int status; Py_ssize_t bufsize; diff --git a/Tools/clinic/libclinic/converters.py b/Tools/clinic/libclinic/converters.py index c2ac6fd22d5bdc9..5f663a50ab8ce8a 100644 --- a/Tools/clinic/libclinic/converters.py +++ b/Tools/clinic/libclinic/converters.py @@ -601,6 +601,16 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st argname=argname) +class gid_t_converter(CConverter): + type = 'gid_t' + converter = '_Py_Gid_Converter' + + +class uid_t_converter(CConverter): + type = 'uid_t' + converter = '_Py_Uid_Converter' + + class pid_t_converter(CConverter): type = 'pid_t' format_unit = '" _Py_PARSE_PID "'