Index: Doc/library/spwd.rst =================================================================== --- Doc/library/spwd.rst (revision 68360) +++ Doc/library/spwd.rst (working copy) @@ -45,7 +45,8 @@ | 8 | ``sp_flag`` | Reserved | +-------+---------------+---------------------------------+ -The sp_nam and sp_pwd items are strings, all others are integers. +The sp_nam and sp_pwd items are either strings or bytes objects, depending on +which function is called, while all the others are integers. :exc:`KeyError` is raised if the entry asked for cannot be found. It defines the following items: @@ -53,15 +54,31 @@ .. function:: getspnam(name) - Return the shadow password database entry for the given user name. + Return the shadow password database entry for the given user name, using + strings for the text fields. + :exc:`UnicodeDecodeError` is raised if any character field + could not be decoded as UTF-8 text. +.. function:: getspnamb(name) + + Return the shadow password database entry for the given user name, using + bytes objects for the text fields; *name* may be a string or a bytes object. + + .. function:: getspall() Return a list of all available shadow password database entries, in arbitrary - order. + order, using strings for the text fields. + :exc:`UnicodeDecodeError` is raised if any character field + could not be decoded as UTF-8 text. +.. function:: getspallb() + + Return a list of all available shadow password database entries, in + arbitrary order, using bytes objects for the text fields. + .. seealso:: Module :mod:`grp` Index: Doc/library/pwd.rst =================================================================== --- Doc/library/pwd.rst (revision 68360) +++ Doc/library/pwd.rst (working copy) @@ -32,7 +32,8 @@ | 6 | ``pw_shell`` | User command interpreter | +-------+---------------+-----------------------------+ -The uid and gid items are integers, all others are strings. :exc:`KeyError` is +The uid and gid items are integers, while all the others are either strings or +bytes objects, depending on which function is called. :exc:`KeyError` is raised if the entry asked for cannot be found. .. note:: @@ -53,19 +54,46 @@ .. function:: getpwuid(uid) - Return the password database entry for the given numeric user ID. + Return the password database entry for the given numeric user ID, using + strings for the text fields. + :exc:`UnicodeDecodeError` is raised if any character field + could not be decoded as UTF-8 text. +.. function:: getpwuidb(uid) + + Return the password database entry for the given numeric user ID, using + bytes objects for the text fields. + + .. function:: getpwnam(name) - Return the password database entry for the given user name. + Return the password database entry for the given user name, using strings + for the text fields. + :exc:`UnicodeDecodeError` is raised if any character field + could not be decoded as UTF-8 text. +.. function:: getpwnamb(name) + + Return the password database entry for the given user name, using bytes + objects for the text fields; *name* may be a string or a bytes object. + + .. function:: getpwall() - Return a list of all available password database entries, in arbitrary order. + Return a list of all available password database entries, in arbitrary + order, using strings for the text fields. + :exc:`UnicodeDecodeError` is raised if any character field + could not be decoded as UTF-8 text. +.. function:: getpwallb() + + Return a list of all available password database entries, in arbitrary + order, using bytes objects for the text fields. + + .. seealso:: Module :mod:`grp` Index: Doc/library/grp.rst =================================================================== --- Doc/library/grp.rst (revision 68360) +++ Doc/library/grp.rst (working copy) @@ -24,12 +24,13 @@ +-------+-----------+---------------------------------+ | 2 | gr_gid | the numerical group ID | +-------+-----------+---------------------------------+ -| 3 | gr_mem | all the group member's user | +| 3 | gr_mem | list of group members' user | | | | names | +-------+-----------+---------------------------------+ -The gid is an integer, name and password are strings, and the member list is a -list of strings. (Note that most users are not explicitly listed as members of +The gr_gid item is an integer, while gr_name, gr_passwd and the items of +gr_mem are either strings or bytes objects, depending on which function is +called. (Note that most users are not explicitly listed as members of the group they are in according to the password database. Check both databases to get complete membership information.) @@ -38,21 +39,47 @@ .. function:: getgrgid(gid) - Return the group database entry for the given numeric group ID. :exc:`KeyError` - is raised if the entry asked for cannot be found. + Return the group database entry for the given numeric group ID, using + strings for the text fields. :exc:`KeyError` is raised if the entry asked + for cannot be found, and :exc:`UnicodeDecodeError` is raised if any + character field could not be decoded as UTF-8 text. +.. function:: getgrgidb(gid) + + Return the group database entry for the given numeric group ID, using bytes + objects for the text fields. :exc:`KeyError` is raised if the entry asked + for cannot be found. + + .. function:: getgrnam(name) - Return the group database entry for the given group name. :exc:`KeyError` is - raised if the entry asked for cannot be found. + Return the group database entry for the given group name, using strings + for the text fields. :exc:`KeyError` is raised if the entry asked for + cannot be found, and :exc:`UnicodeDecodeError` is raised if any + character field could not be decoded as UTF-8 text. +.. function:: getgrnamb(name) + + Return the group database entry for the given group name, using bytes + objects for the text fields. :exc:`KeyError` is raised if the entry asked + for cannot be found; *name* may be a string or a bytes object. + + .. function:: getgrall() - Return a list of all available group entries, in arbitrary order. + Return a list of all available group entries, in arbitrary order, using + strings for the text fields. :exc:`UnicodeDecodeError` is raised if any + character field could not be decoded as UTF-8 text. +.. function:: getgrallb() + + Return a list of all available group entries, in arbitrary order, using + bytes objects for the text fields. + + .. seealso:: Module :mod:`pwd` Index: Lib/test/test_pwd.py =================================================================== --- Lib/test/test_pwd.py (revision 68360) +++ Lib/test/test_pwd.py (working copy) @@ -46,6 +46,56 @@ self.assert_(pwd.getpwnam(e.pw_name) in entriesbyname[e.pw_name]) self.assert_(pwd.getpwuid(e.pw_uid) in entriesbyuid[e.pw_uid]) + def test_valuesb(self): + entries = pwd.getpwallb() + entriesbyname = {} + entriesbyuid = {} + + for e in entries: + self.assertEqual(len(e), 7) + self.assertEqual(e[0], e.pw_name) + self.assert_(isinstance(e.pw_name, bytes)) + self.assertEqual(e[1], e.pw_passwd) + self.assert_(isinstance(e.pw_passwd, bytes)) + self.assertEqual(e[2], e.pw_uid) + self.assert_(isinstance(e.pw_uid, int)) + self.assertEqual(e[3], e.pw_gid) + self.assert_(isinstance(e.pw_gid, int)) + self.assertEqual(e[4], e.pw_gecos) + self.assert_(isinstance(e.pw_gecos, bytes)) + self.assertEqual(e[5], e.pw_dir) + self.assert_(isinstance(e.pw_dir, bytes)) + self.assertEqual(e[6], e.pw_shell) + self.assert_(isinstance(e.pw_shell, bytes)) + + try: + s = e.pw_name.decode() + e2 = pwd.getpwnam(s) + except UnicodeDecodeError: + pass + else: + self.assertEqual(e.pw_uid, e2.pw_uid) + self.assertEqual(e, pwd.getpwnamb(s)) + + # The following won't work, because of duplicate entries + # for one uid + # self.assertEqual(pwd.getpwuidb(e.pw_uid), e) + # instead of this collect all entries for one uid + # and check afterwards + entriesbyname.setdefault(e.pw_name, []).append(e) + entriesbyuid.setdefault(e.pw_uid, []).append(e) + + if len(entries) > 1000: # Huge passwd file (NIS?) -- skip the rest + return + + # check whether the entry returned by getpwuidb() + # for each uid is among those from getpwallb() for this uid + for e in entries: + if not e[0] or e[0] == b'+': + continue # skip NIS entries etc. + self.assert_(pwd.getpwnamb(e.pw_name) in entriesbyname[e.pw_name]) + self.assert_(pwd.getpwuidb(e.pw_uid) in entriesbyuid[e.pw_uid]) + def test_errors(self): self.assertRaises(TypeError, pwd.getpwuid) self.assertRaises(TypeError, pwd.getpwnam) @@ -90,6 +140,50 @@ self.assertRaises(KeyError, pwd.getpwuid, fakeuid) + def test_errorsb(self): + self.assertRaises(TypeError, pwd.getpwuidb) + self.assertRaises(TypeError, pwd.getpwnamb) + self.assertRaises(TypeError, pwd.getpwallb, 42) + + # try to get some errors + bynames = {} + byuids = {} + for (n, p, u, g, gecos, d, s) in pwd.getpwallb(): + bynames[n] = u + byuids[u] = n + + allnames = list(bynames.keys()) + namei = 0 + fakename = allnames[namei] + while fakename in bynames: + chars = [chr(n) for n in fakename] + for i in range(len(chars)): + if chars[i] == 'z': + chars[i] = 'A' + break + elif chars[i] == 'Z': + continue + else: + chars[i] = chr(ord(chars[i]) + 1) + break + else: + namei = namei + 1 + try: + fakename = allnames[namei] + except IndexError: + # should never happen... if so, just forget it + break + fakename = ''.join(chars) + + self.assertRaises(KeyError, pwd.getpwnamb, fakename) + + # Choose a non-existent uid. + fakeuid = 4127 + while fakeuid in byuids: + fakeuid = (fakeuid * 3) % 0x10000 + + self.assertRaises(KeyError, pwd.getpwuidb, fakeuid) + def test_main(): support.run_unittest(PwdTest) Index: Lib/test/test_grp.py =================================================================== --- Lib/test/test_grp.py (revision 68360) +++ Lib/test/test_grp.py (working copy) @@ -6,14 +6,14 @@ class GroupDatabaseTestCase(unittest.TestCase): - def check_value(self, value): + def check_value(self, value, stype): # check that a grp tuple has the entries and # attributes promised by the docs self.assertEqual(len(value), 4) self.assertEqual(value[0], value.gr_name) - self.assert_(isinstance(value.gr_name, str)) + self.assert_(isinstance(value.gr_name, stype)) self.assertEqual(value[1], value.gr_passwd) - self.assert_(isinstance(value.gr_passwd, str)) + self.assert_(isinstance(value.gr_passwd, stype)) self.assertEqual(value[2], value.gr_gid) self.assert_(isinstance(value.gr_gid, int)) self.assertEqual(value[3], value.gr_mem) @@ -23,22 +23,50 @@ entries = grp.getgrall() for e in entries: - self.check_value(e) + self.check_value(e, str) if len(entries) > 1000: # Huge group file (NIS?) -- skip the rest return for e in entries: e2 = grp.getgrgid(e.gr_gid) - self.check_value(e2) + self.check_value(e2, str) self.assertEqual(e2.gr_gid, e.gr_gid) e2 = grp.getgrnam(e.gr_name) - self.check_value(e2) + self.check_value(e2, str) # There are instances where getgrall() returns group names in # lowercase while getgrgid() returns proper casing. # Discovered on Ubuntu 5.04 (custom). self.assertEqual(e2.gr_name.lower(), e.gr_name.lower()) + def test_valuesb(self): + entries = grp.getgrallb() + + for e in entries: + self.check_value(e, bytes) + + if len(entries) > 1000: # Huge group file (NIS?) -- skip the rest + return + + for e in entries: + e2 = grp.getgrgidb(e.gr_gid) + self.check_value(e2, bytes) + self.assertEqual(e2.gr_gid, e.gr_gid) + e2 = grp.getgrnamb(e.gr_name) + self.check_value(e2, bytes) + # There are instances where getgrall() returns group names in + # lowercase while getgrgid() returns proper casing. + # Discovered on Ubuntu 5.04 (custom). + self.assertEqual(e2.gr_name.lower(), e.gr_name.lower()) + try: + s = e.gr_name.decode() + e2 = grp.getgrnam(s) + except UnicodeDecodeError: + pass + else: + self.assertEqual(e.gr_gid, e2.gr_gid) + self.assertEqual(e.gr_gid, grp.getgrnamb(s).gr_gid) + def test_errors(self): self.assertRaises(TypeError, grp.getgrgid) self.assertRaises(TypeError, grp.getgrnam) @@ -85,6 +113,52 @@ self.assertRaises(KeyError, grp.getgrgid, fakegid) + def test_errorsb(self): + self.assertRaises(TypeError, grp.getgrgidb) + self.assertRaises(TypeError, grp.getgrnamb) + self.assertRaises(TypeError, grp.getgrallb, 42) + + # try to get some errors + bynames = {} + bygids = {} + for (n, p, g, mem) in grp.getgrallb(): + if not n or n == b'+': + continue # skip NIS entries etc. + bynames[n] = g + bygids[g] = n + + allnames = list(bynames.keys()) + namei = 0 + fakename = allnames[namei] + while fakename in bynames: + chars = [chr(n) for n in fakename] + for i in range(len(chars)): + if chars[i] == 'z': + chars[i] = 'A' + break + elif chars[i] == 'Z': + continue + else: + chars[i] = chr(ord(chars[i]) + 1) + break + else: + namei = namei + 1 + try: + fakename = allnames[namei] + except IndexError: + # should never happen... if so, just forget it + break + fakename = ''.join(chars) + + self.assertRaises(KeyError, grp.getgrnamb, fakename) + + # Choose a non-existent gid. + fakegid = 4127 + while fakegid in bygids: + fakegid = (fakegid * 3) % 0x10000 + + self.assertRaises(KeyError, grp.getgrgidb, fakegid) + def test_main(): support.run_unittest(GroupDatabaseTestCase) Index: Modules/grpmodule.c =================================================================== --- Modules/grpmodule.c (revision 68360) +++ Modules/grpmodule.c (working copy) @@ -33,7 +33,7 @@ static PyTypeObject StructGrpType; static PyObject * -mkgrent(struct group *p) +mkgrent(struct group *p, PyObject *constructor(const char *)) { int setIndex = 0; PyObject *v = PyStructSequence_New(&StructGrpType), *w; @@ -47,7 +47,7 @@ return NULL; } for (member = p->gr_mem; *member != NULL; member++) { - PyObject *x = PyUnicode_FromString(*member); + PyObject *x = constructor(*member); if (x == NULL || PyList_Append(w, x) != 0) { Py_XDECREF(x); Py_DECREF(w); @@ -58,13 +58,13 @@ } #define SET(i,val) PyStructSequence_SET_ITEM(v, i, val) - SET(setIndex++, PyUnicode_FromString(p->gr_name)); + SET(setIndex++, constructor(p->gr_name)); #ifdef __VMS SET(setIndex++, Py_None); Py_INCREF(Py_None); #else if (p->gr_passwd) - SET(setIndex++, PyUnicode_FromString(p->gr_passwd)); + SET(setIndex++, constructor(p->gr_passwd)); else { SET(setIndex++, Py_None); Py_INCREF(Py_None); @@ -76,7 +76,6 @@ if (PyErr_Occurred()) { Py_DECREF(v); - Py_DECREF(w); return NULL; } @@ -100,10 +99,30 @@ PyErr_Format(PyExc_KeyError, "getgrgid(): gid not found: %d", gid); return NULL; } - return mkgrent(p); + return mkgrent(p, PyUnicode_FromString); } static PyObject * +grp_getgrgidb(PyObject *self, PyObject *pyo_id) +{ + PyObject *py_int_id; + unsigned int gid; + struct group *p; + + py_int_id = PyNumber_Int(pyo_id); + if (!py_int_id) + return NULL; + gid = PyLong_AS_LONG(py_int_id); + Py_DECREF(py_int_id); + + if ((p = getgrgid(gid)) == NULL) { + PyErr_Format(PyExc_KeyError, "getgrgidb(): gid not found: %d", gid); + return NULL; + } + return mkgrent(p, PyBytes_FromString); +} + +static PyObject * grp_getgrnam(PyObject *self, PyObject *pyo_name) { PyObject *py_str_name; @@ -122,12 +141,30 @@ } Py_DECREF(py_str_name); - return mkgrent(p); + return mkgrent(p, PyUnicode_FromString); } static PyObject * -grp_getgrall(PyObject *self, PyObject *ignore) +grp_getgrnamb(PyObject *self, PyObject *args) { + char *name; + struct group *p; + + if (!PyArg_ParseTuple(args, "et:getgrnamb", NULL, &name)) + return NULL; + + if ((p = getgrnam(name)) == NULL) { + PyErr_Format(PyExc_KeyError, "getgrnamb(): name not found: %s", name); + PyMem_Free(name); + return NULL; + } + PyMem_Free(name); + return mkgrent(p, PyBytes_FromString); +} + +static PyObject * +do_getgrall(PyObject *constructor(const char *)) +{ PyObject *d; struct group *p; @@ -135,10 +172,11 @@ return NULL; setgrent(); while ((p = getgrent()) != NULL) { - PyObject *v = mkgrent(p); + PyObject *v = mkgrent(p, constructor); if (v == NULL || PyList_Append(d, v) != 0) { Py_XDECREF(v); Py_DECREF(d); + endgrent(); return NULL; } Py_DECREF(v); @@ -147,18 +185,44 @@ return d; } +static PyObject * +grp_getgrall(PyObject *self, PyObject *ignore) +{ + return do_getgrall(PyUnicode_FromString); +} + +static PyObject * +grp_getgrallb(PyObject *self, PyObject *ignore) +{ + return do_getgrall(PyBytes_FromString); +} + static PyMethodDef grp_methods[] = { {"getgrgid", grp_getgrgid, METH_O, "getgrgid(id) -> tuple\n\ Return the group database entry for the given numeric group ID. If\n\ id is not valid, raise KeyError."}, + {"getgrgidb", grp_getgrgidb, METH_O, + "getgrgidb(id) -> tuple\n\ +Return the group database entry for the given numeric group ID,\n\ +using bytes objects for the text fields. If id is not valid,\n\ +raise KeyError."}, {"getgrnam", grp_getgrnam, METH_O, "getgrnam(name) -> tuple\n\ Return the group database entry for the given group name. If\n\ name is not valid, raise KeyError."}, + {"getgrnamb", grp_getgrnamb, METH_VARARGS, + "getgrnamb(name) -> tuple\n\ +Return the group database entry for the given group name,\n\ +using bytes objects for the text fields. If name is not valid,\n\ +raise KeyError."}, {"getgrall", grp_getgrall, METH_NOARGS, "getgrall() -> list of tuples\n\ Return a list of all available group entries, in arbitrary order."}, + {"getgrallb", grp_getgrallb, METH_NOARGS, + "getgrallb() -> list of tuples\n\ +Return a list of all available group entries, in arbitrary order,\n\ +using bytes objects for the text fields."}, {NULL, NULL} /* sentinel */ }; @@ -173,7 +237,8 @@ gid - numeric ID of the group\n\ mem - list of members\n\ \n\ -The gid is an integer, name and password are strings. (Note that most\n\ +The gid is an integer, name and password are either strings or bytes\n\ +objects, depending on which function is called. (Note that most\n\ users are not explicitly listed as members of the groups they are in\n\ according to the password database. Check both databases to get\n\ complete membership information.)"); Index: Modules/pwdmodule.c =================================================================== --- Modules/pwdmodule.c (revision 68360) +++ Modules/pwdmodule.c (working copy) @@ -38,21 +38,19 @@ Password database entries are reported as 7-tuples containing the following\n\ items from the password database (see `'), in order:\n\ pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell.\n\ -The uid and gid items are integers, all others are strings. An\n\ -exception is raised if the entry asked for cannot be found."); +The uid and gid items are integers, all others are either strings or bytes\n\ +objects, depending on which function is called. An exception is raised if\n\ +the entry asked for cannot be found."); static int initialized; static PyTypeObject StructPwdType; static void -sets(PyObject *v, int i, const char* val) +sets(PyObject *v, int i, const char* val, PyObject *constructor(const char *)) { - if (val) { - PyObject *o = - PyUnicode_DecodeUnicodeEscape(val, strlen(val), "strict"); - PyStructSequence_SET_ITEM(v, i, o); - } + if (val) + PyStructSequence_SET_ITEM(v, i, constructor(val)); else { PyStructSequence_SET_ITEM(v, i, Py_None); Py_INCREF(Py_None); @@ -60,7 +58,7 @@ } static PyObject * -mkpwent(struct passwd *p) +mkpwent(struct passwd *p, PyObject *constructor(const char *)) { int setIndex = 0; PyObject *v = PyStructSequence_New(&StructPwdType); @@ -68,7 +66,7 @@ return NULL; #define SETI(i,val) PyStructSequence_SET_ITEM(v, i, PyLong_FromLong((long) val)) -#define SETS(i,val) sets(v, i, val) +#define SETS(i,val) sets(v, i, val, constructor) SETS(setIndex++, p->pw_name); #ifdef __VMS @@ -115,9 +113,32 @@ "getpwuid(): uid not found: %d", uid); return NULL; } - return mkpwent(p); + return mkpwent(p, PyUnicode_FromString); } +PyDoc_STRVAR(pwd_getpwuidb__doc__, +"getpwuidb(uid) -> (pw_name,pw_passwd,pw_uid,\n\ + pw_gid,pw_gecos,pw_dir,pw_shell)\n\ +Return the password database entry for the given numeric user ID,\n\ +using bytes objects for the text fields.\n\ +See pwd.__doc__ for more on password database entries."); + +static PyObject * +pwd_getpwuidb(PyObject *self, PyObject *args) +{ + unsigned int uid; + struct passwd *p; + + if (!PyArg_ParseTuple(args, "I:getpwuidb", &uid)) + return NULL; + if ((p = getpwuid(uid)) == NULL) { + PyErr_Format(PyExc_KeyError, + "getpwuidb(): uid not found: %d", uid); + return NULL; + } + return mkpwent(p, PyBytes_FromString); +} + PyDoc_STRVAR(pwd_getpwnam__doc__, "getpwnam(name) -> (pw_name,pw_passwd,pw_uid,\n\ pw_gid,pw_gecos,pw_dir,pw_shell)\n\ @@ -136,19 +157,38 @@ "getpwnam(): name not found: %s", name); return NULL; } - return mkpwent(p); + return mkpwent(p, PyUnicode_FromString); } -#ifdef HAVE_GETPWENT -PyDoc_STRVAR(pwd_getpwall__doc__, -"getpwall() -> list_of_entries\n\ -Return a list of all available password database entries, \ -in arbitrary order.\n\ +PyDoc_STRVAR(pwd_getpwnamb__doc__, +"getpwnamb(name) -> (pw_name,pw_passwd,pw_uid,\n\ + pw_gid,pw_gecos,pw_dir,pw_shell)\n\ +Return the password database entry for the given user name,\n\ +using bytes objects for the text fields.\n\ See pwd.__doc__ for more on password database entries."); static PyObject * -pwd_getpwall(PyObject *self) +pwd_getpwnamb(PyObject *self, PyObject *args) { + char *name; + struct passwd *p; + + if (!PyArg_ParseTuple(args, "et:getpwnamb", NULL, &name)) + return NULL; + if ((p = getpwnam(name)) == NULL) { + PyErr_Format(PyExc_KeyError, + "getpwnamb(): name not found: %s", name); + PyMem_Free(name); + return NULL; + } + PyMem_Free(name); + return mkpwent(p, PyBytes_FromString); +} + +#ifdef HAVE_GETPWENT +static PyObject * +do_getpwall(PyObject *constructor(const char *)) +{ PyObject *d; struct passwd *p; if ((d = PyList_New(0)) == NULL) @@ -159,10 +199,11 @@ setpwent(); while ((p = getpwent()) != NULL) { #endif - PyObject *v = mkpwent(p); + PyObject *v = mkpwent(p, constructor); if (v == NULL || PyList_Append(d, v) != 0) { Py_XDECREF(v); Py_DECREF(d); + endpwent(); return NULL; } Py_DECREF(v); @@ -170,14 +211,43 @@ endpwent(); return d; } + +PyDoc_STRVAR(pwd_getpwall__doc__, +"getpwall() -> list_of_entries\n\ +Return a list of all available password database entries, \ +in arbitrary order.\n\ +See pwd.__doc__ for more on password database entries."); + +static PyObject * +pwd_getpwall(PyObject *self) +{ + return do_getpwall(PyUnicode_FromString); +} + +PyDoc_STRVAR(pwd_getpwallb__doc__, +"getpwallb() -> list_of_entries\n\ +Return a list of all available password database entries, in arbitrary\n\ +order, using bytes objects for the text fields.\n\ +See pwd.__doc__ for more on password database entries."); + +static PyObject * +pwd_getpwallb(PyObject *self) +{ + return do_getpwall(PyBytes_FromString); +} + #endif static PyMethodDef pwd_methods[] = { {"getpwuid", pwd_getpwuid, METH_VARARGS, pwd_getpwuid__doc__}, + {"getpwuidb", pwd_getpwuidb, METH_VARARGS, pwd_getpwuidb__doc__}, {"getpwnam", pwd_getpwnam, METH_VARARGS, pwd_getpwnam__doc__}, + {"getpwnamb", pwd_getpwnamb, METH_VARARGS, pwd_getpwnamb__doc__}, #ifdef HAVE_GETPWENT {"getpwall", (PyCFunction)pwd_getpwall, METH_NOARGS, pwd_getpwall__doc__}, + {"getpwallb", (PyCFunction)pwd_getpwallb, + METH_NOARGS, pwd_getpwallb__doc__}, #endif {NULL, NULL} /* sentinel */ }; Index: Modules/spwdmodule.c =================================================================== --- Modules/spwdmodule.c (revision 68360) +++ Modules/spwdmodule.c (working copy) @@ -19,7 +19,8 @@ Shadow password database entries are reported as 9-tuples of type struct_spwd,\n\ containing the following items from the password database (see `'):\n\ sp_namp, sp_pwdp, sp_lstchg, sp_min, sp_max, sp_warn, sp_inact, sp_expire, sp_flag.\n\ -The sp_namp and sp_pwdp are strings, the rest are integers.\n\ +The sp_namp and sp_pwdp are either strings or bytes objects, depending on\n\ +which function is called, while the rest are integers.\n\ An exception is raised if the entry asked for cannot be found.\n\ You have to be root to be able to use this module."); @@ -57,17 +58,17 @@ static void -sets(PyObject *v, int i, const char* val) +sets(PyObject *v, int i, const char* val, PyObject *constructor(const char *)) { if (val) - PyStructSequence_SET_ITEM(v, i, PyUnicode_FromString(val)); + PyStructSequence_SET_ITEM(v, i, constructor(val)); else { PyStructSequence_SET_ITEM(v, i, Py_None); Py_INCREF(Py_None); } } -static PyObject *mkspent(struct spwd *p) +static PyObject *mkspent(struct spwd *p, PyObject *constructor(const char *)) { int setIndex = 0; PyObject *v = PyStructSequence_New(&StructSpwdType); @@ -75,7 +76,7 @@ return NULL; #define SETI(i,val) PyStructSequence_SET_ITEM(v, i, PyLong_FromLong((long) val)) -#define SETS(i,val) sets(v, i, val) +#define SETS(i,val) sets(v, i, val, constructor) SETS(setIndex++, p->sp_namp); SETS(setIndex++, p->sp_pwdp); @@ -119,21 +120,38 @@ PyErr_SetString(PyExc_KeyError, "getspnam(): name not found"); return NULL; } - return mkspent(p); + return mkspent(p, PyUnicode_FromString); } +PyDoc_STRVAR(spwd_getspnamb__doc__, +"getspnamb(name) -> (sp_namp, sp_pwdp, sp_lstchg, sp_min, sp_max,\n\ + sp_warn, sp_inact, sp_expire, sp_flag)\n\ +Return the shadow password database entry for the given user name,\n\ +using bytes objects for the text fields.\n\ +See spwd.__doc__ for more on shadow password database entries."); + +static PyObject* spwd_getspnamb(PyObject *self, PyObject *args) +{ + char *name; + struct spwd *p; + + if (!PyArg_ParseTuple(args, "et:getspnamb", NULL, &name)) + return NULL; + if ((p = getspnam(name)) == NULL) { + PyErr_SetString(PyExc_KeyError, "getspnamb(): name not found"); + PyMem_Free(name); + return NULL; + } + PyMem_Free(name); + return mkspent(p, PyBytes_FromString); +} + #endif /* HAVE_GETSPNAM */ #ifdef HAVE_GETSPENT -PyDoc_STRVAR(spwd_getspall__doc__, -"getspall() -> list_of_entries\n\ -Return a list of all available shadow password database entries, \ -in arbitrary order.\n\ -See spwd.__doc__ for more on shadow password database entries."); - static PyObject * -spwd_getspall(PyObject *self, PyObject *args) +do_getspall(PyObject *constructor(const char *)) { PyObject *d; struct spwd *p; @@ -141,7 +159,7 @@ return NULL; setspent(); while ((p = getspent()) != NULL) { - PyObject *v = mkspent(p); + PyObject *v = mkspent(p, constructor); if (v == NULL || PyList_Append(d, v) != 0) { Py_XDECREF(v); Py_DECREF(d); @@ -154,14 +172,40 @@ return d; } +PyDoc_STRVAR(spwd_getspall__doc__, +"getspall() -> list_of_entries\n\ +Return a list of all available shadow password database entries, \ +in arbitrary order.\n\ +See spwd.__doc__ for more on shadow password database entries."); + +static PyObject * +spwd_getspall(PyObject *self, PyObject *args) +{ + return do_getspall(PyUnicode_FromString); +} + +PyDoc_STRVAR(spwd_getspallb__doc__, +"getspallb() -> list_of_entries\n\ +Return a list of all available shadow password database entries,\n\ +in arbitrary order, using bytes objects for the text fields.\n\ +See spwd.__doc__ for more on shadow password database entries."); + +static PyObject * +spwd_getspallb(PyObject *self, PyObject *args) +{ + return do_getspall(PyBytes_FromString); +} + #endif /* HAVE_GETSPENT */ static PyMethodDef spwd_methods[] = { #ifdef HAVE_GETSPNAM {"getspnam", spwd_getspnam, METH_VARARGS, spwd_getspnam__doc__}, + {"getspnamb", spwd_getspnamb, METH_VARARGS, spwd_getspnamb__doc__}, #endif #ifdef HAVE_GETSPENT {"getspall", spwd_getspall, METH_NOARGS, spwd_getspall__doc__}, + {"getspallb", spwd_getspallb, METH_NOARGS, spwd_getspallb__doc__}, #endif {NULL, NULL} /* sentinel */ };