Skip to content
7 changes: 5 additions & 2 deletions Doc/library/getpass.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ The :mod:`getpass` module provides two functions:
:envvar:`USER`, :envvar:`!LNAME` and :envvar:`USERNAME`, in order, and
returns the value of the first one which is set to a non-empty string. If
none are set, the login name from the password database is returned on
systems which support the :mod:`pwd` module, otherwise, an exception is
raised.
systems which support the :mod:`pwd` module, otherwise, an :exc:`OSError`
is raised.

In general, this function should be preferred over :func:`os.getlogin()`.

.. versionchanged:: 3.13
Previously, various exceptions beyond just :exc:`OSError` were raised.
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,10 @@ Changes in the Python API
recomended in the documentation.
(Contributed by Serhiy Storchaka in :gh:`106672`.)

* An :exc:`OSError` is now raised by :func:`getpass.getuser` for any failure to
retrieve a username, instead of :exc:`ImportError` on non-Unix platforms or
:exc:`KeyError` on Unix platforms where the password database is empty.


Build Changes
=============
Expand Down
13 changes: 10 additions & 3 deletions Lib/getpass.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,17 +156,24 @@ def getuser():

First try various environment variables, then the password
database. This works on Windows as long as USERNAME is set.
Any failure to find a username raises OSError.

.. versionchanged:: 3.13
Previously, various exceptions beyond just :exc:`OSError`
were raised.
"""

for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'):
user = os.environ.get(name)
if user:
return user

# If this fails, the exception will "explain" why
import pwd
return pwd.getpwuid(os.getuid())[0]
try:
import pwd
return pwd.getpwuid(os.getuid())[0]
except (ImportError, KeyError) as e:
raise OSError('No username set in the environment') from e


# Bind the name getpass to the appropriate function
try:
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_getpass.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def test_username_priorities_of_env_values(self, environ):
environ.get.return_value = None
try:
getpass.getuser()
except ImportError: # in case there's no pwd module
except OSError: # in case there's no pwd module
pass
except KeyError:
# current user has no pwd entry
Expand All @@ -47,7 +47,7 @@ def test_username_falls_back_to_pwd(self, environ):
getpass.getuser())
getpw.assert_called_once_with(42)
else:
self.assertRaises(ImportError, getpass.getuser)
self.assertRaises(OSError, getpass.getuser)


class GetpassRawinputTest(unittest.TestCase):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:func:`getpass.getuser` now raises :exc:`OSError` for all failures rather
than :exc:`ImportError` on systems lacking the :mod:`pwd` module or
:exc:`KeyError` if the password database is empty.