From 8b31c4d5619635903a44d043ca6230b1dcebb7e3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 7 Aug 2026 22:32:41 +0200 Subject: [PATCH 1/2] gh-155358: Use named attributes with pwd and grp modules * Replace pwd[0] with pwd.pw_name * Replace pwd[2] with pwd.pw_uid * Replace grp[0] with grp.gr_name * Replace grp[2] with grp.gr_gid --- Lib/getpass.py | 2 +- Lib/shutil.py | 4 ++-- Lib/tarfile.py | 8 ++++---- Lib/test/test_getpass.py | 5 ++++- Lib/test/test_pwd.py | 2 +- Lib/test/test_shutil.py | 12 ++++++------ Lib/test/test_tarfile.py | 4 ++-- 7 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Lib/getpass.py b/Lib/getpass.py index cfbd63dded6cc1..b9eec4c57abc97 100644 --- a/Lib/getpass.py +++ b/Lib/getpass.py @@ -428,7 +428,7 @@ def getuser(): try: import pwd - return pwd.getpwuid(os.getuid())[0] + return pwd.getpwuid(os.getuid()).pw_name except (ImportError, KeyError) as e: raise OSError('No username set in the environment') from e diff --git a/Lib/shutil.py b/Lib/shutil.py index 94617ec296f508..ce6969d6a4bf5a 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -983,7 +983,7 @@ def _get_gid(name): except KeyError: result = None if result is not None: - return result[2] + return result.gr_gid return None def _get_uid(name): @@ -1001,7 +1001,7 @@ def _get_uid(name): except KeyError: result = None if result is not None: - return result[2] + return result.pw_uid return None def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, diff --git a/Lib/tarfile.py b/Lib/tarfile.py index d12bd15aa2d231..dc5c3a59744cbc 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -2282,14 +2282,14 @@ def gettarinfo(self, name=None, arcname=None, fileobj=None): if pwd: if tarinfo.uid not in self._unames: try: - self._unames[tarinfo.uid] = pwd.getpwuid(tarinfo.uid)[0] + self._unames[tarinfo.uid] = pwd.getpwuid(tarinfo.uid).pw_name except KeyError: self._unames[tarinfo.uid] = '' tarinfo.uname = self._unames[tarinfo.uid] if grp: if tarinfo.gid not in self._gnames: try: - self._gnames[tarinfo.gid] = grp.getgrgid(tarinfo.gid)[0] + self._gnames[tarinfo.gid] = grp.getgrgid(tarinfo.gid).gr_name except KeyError: self._gnames[tarinfo.gid] = '' tarinfo.gname = self._gnames[tarinfo.gid] @@ -2837,12 +2837,12 @@ def chown(self, tarinfo, targetpath, numeric_owner): if not numeric_owner: try: if grp and tarinfo.gname: - g = grp.getgrnam(tarinfo.gname)[2] + g = grp.getgrnam(tarinfo.gname).gr_gid except KeyError: pass try: if pwd and tarinfo.uname: - u = pwd.getpwnam(tarinfo.uname)[2] + u = pwd.getpwnam(tarinfo.uname).pw_uid except KeyError: pass if g is None: diff --git a/Lib/test/test_getpass.py b/Lib/test/test_getpass.py index 272414a6204856..23f8a328506c6e 100644 --- a/Lib/test/test_getpass.py +++ b/Lib/test/test_getpass.py @@ -39,10 +39,13 @@ def test_username_falls_back_to_pwd(self, environ): expected_name = 'some_name' environ.get.return_value = None if pwd: + class User: + pass with mock.patch('os.getuid') as uid, \ mock.patch('pwd.getpwuid') as getpw: uid.return_value = 42 - getpw.return_value = [expected_name] + getpw.return_value = User() + getpw.return_value.pw_name = expected_name self.assertEqual(expected_name, getpass.getuser()) getpw.assert_called_once_with(42) diff --git a/Lib/test/test_pwd.py b/Lib/test/test_pwd.py index bdf57776c82be1..82acce85f1db57 100644 --- a/Lib/test/test_pwd.py +++ b/Lib/test/test_pwd.py @@ -50,7 +50,7 @@ def test_values_extended(self): # check whether the entry returned by getpwuid() # for each uid is among those from getpwall() for this uid for e in entries: - if not e[0] or e[0] == '+': + if not e.pw_name or e.pw_name == '+': continue # skip NIS entries etc. self.assertIn(pwd.getpwnam(e.pw_name), entriesbyname[e.pw_name]) self.assertIn(pwd.getpwuid(e.pw_uid), entriesbyuid[e.pw_uid]) diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index ed5d15ecc7ddad..d6b3b6a642bee1 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1999,8 +1999,8 @@ def test_make_archive_owner_group(self): # testing make_archive with owner and group, with various combinations # this works even if there's not gid/uid support if UID_GID_SUPPORT: - group = grp.getgrgid(0)[0] - owner = pwd.getpwuid(0)[0] + group = grp.getgrgid(0).gr_name + owner = pwd.getpwuid(0).pw_name else: group = owner = 'root' @@ -2027,8 +2027,8 @@ def test_make_archive_owner_group(self): def test_tarfile_root_owner(self): root_dir, base_dir = self._create_files() base_name = os.path.join(self.mkdtemp(), 'archive') - group = grp.getgrgid(0)[0] - owner = pwd.getpwuid(0)[0] + group = grp.getgrgid(0).gr_name + owner = pwd.getpwuid(0).pw_name with os_helper.change_cwd(root_dir), no_chdir: archive_name = make_archive(base_name, 'gztar', root_dir, 'dist', owner=owner, group=group) @@ -2433,8 +2433,8 @@ def check_chown(path, uid=None, gid=None): check_chown(dirname, gid=gid) try: - user = pwd.getpwuid(uid)[0] - group = grp.getgrgid(gid)[0] + user = pwd.getpwuid(uid).pw_name + group = grp.getgrgid(gid).gr_name except KeyError: # On some systems uid/gid cannot be resolved. pass diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index c86bcb79eb85d8..5fa97e2ac226c4 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -3351,12 +3351,12 @@ def root_is_uid_gid_0(): except ImportError: return False try: - if pwd.getpwuid(0)[0] != 'root': + if pwd.getpwuid(0).pw_name != 'root': return False except KeyError: # On Cygwin, there is no root user (uid 0) return False - if grp.getgrgid(0)[0] != 'root': + if grp.getgrgid(0).gr_name != 'root': return False return True From d85a43dc897780e4308d8584421a2175e1ccf9d9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 7 Aug 2026 23:02:08 +0200 Subject: [PATCH 2/2] Replace more code * Replace pwd[3] with pwd.pw_gid --- Doc/library/os.rst | 2 +- Lib/http/server.py | 2 +- Lib/netrc.py | 2 +- Lib/test/support/smtpd.py | 2 +- Lib/test/test_os/test_posix.py | 4 ++-- Tools/c-analyzer/c_common/fsutil.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 0a4a02c45b533b..fceadde7df6bf4 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -452,7 +452,7 @@ process and user. process. For most purposes, it is more useful to use :func:`getpass.getuser` since the latter checks the environment variables :envvar:`LOGNAME` or :envvar:`USERNAME` to find out who the user is, and - falls back to ``pwd.getpwuid(os.getuid())[0]`` to get the login name of the + falls back to ``pwd.getpwuid(os.getuid()).pw_name`` to get the login name of the current real user id. .. availability:: Unix, Windows, not WASI. diff --git a/Lib/http/server.py b/Lib/http/server.py index 095b5744bd12fc..a74773bf8a12d4 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -1013,7 +1013,7 @@ def nobody_uid(): except ImportError: return -1 try: - nobody = pwd.getpwnam('nobody')[2] + nobody = pwd.getpwnam('nobody').pw_uid except KeyError: nobody = 1 + max(x[2] for x in pwd.getpwall()) return nobody diff --git a/Lib/netrc.py b/Lib/netrc.py index a28ea297df894b..e9b5538d2c4399 100644 --- a/Lib/netrc.py +++ b/Lib/netrc.py @@ -15,7 +15,7 @@ def _can_security_check(): def _getpwuid(uid): try: import pwd - return pwd.getpwuid(uid)[0] + return pwd.getpwuid(uid).pw_name except (ImportError, LookupError): return f'uid {uid}' diff --git a/Lib/test/support/smtpd.py b/Lib/test/support/smtpd.py index 6537679db9ad24..9800332a27f86c 100755 --- a/Lib/test/support/smtpd.py +++ b/Lib/test/support/smtpd.py @@ -862,7 +862,7 @@ def parseargs(): except ImportError: print('Cannot import module "pwd"; try running with -n option.', file=sys.stderr) sys.exit(1) - nobody = pwd.getpwnam('nobody')[2] + nobody = pwd.getpwnam('nobody').pw_uid try: os.setuid(nobody) except PermissionError: diff --git a/Lib/test/test_os/test_posix.py b/Lib/test/test_os/test_posix.py index 8743b0bf0bc493..d4a8d837fa6b45 100644 --- a/Lib/test/test_os/test_posix.py +++ b/Lib/test/test_os/test_posix.py @@ -1315,8 +1315,8 @@ def _create_and_do_getcwd(dirname, current_path_length = 0): @unittest.skipUnless(hasattr(pwd, 'getpwuid'), "test needs pwd.getpwuid()") @unittest.skipUnless(hasattr(os, 'getuid'), "test needs os.getuid()") def test_getgrouplist(self): - user = pwd.getpwuid(os.getuid())[0] - group = pwd.getpwuid(os.getuid())[3] + user = pwd.getpwuid(os.getuid()).pw_name + group = pwd.getpwuid(os.getuid()).pw_gid self.assertIn(group, posix.getgrouplist(user, group)) diff --git a/Tools/c-analyzer/c_common/fsutil.py b/Tools/c-analyzer/c_common/fsutil.py index a8cf8d0537e40d..eb9b74d552ece0 100644 --- a/Tools/c-analyzer/c_common/fsutil.py +++ b/Tools/c-analyzer/c_common/fsutil.py @@ -411,7 +411,7 @@ def _get_user_info(user): if user is None: uid = os.geteuid() #username = os.getlogin() - username = pwd.getpwuid(uid)[0] + username = pwd.getpwuid(uid).pw_name gid = os.getgid() groups = os.getgroups() else: