Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7d412d9
Accept only single character with getpass.getpass(echo_char) (see: gh…
benjaminjohnson01 Sep 6, 2025
a99e900
accept suggestion to reword blurb
benjaminjohnson01 Sep 6, 2025
bbd741a
reword echo_char description per suggestion
benjaminjohnson01 Sep 10, 2025
4338c9f
reword echo_char error message per suggestion
benjaminjohnson01 Sep 10, 2025
37a0a72
reword getpass echo_char news per suggestion
benjaminjohnson01 Sep 10, 2025
b063e33
fix getpass echo_char spacing per suggestion
benjaminjohnson01 Sep 10, 2025
5151819
Merge branch 'main' into gh-138514-fix-getpass-echo-char
benjaminjohnson01 Sep 10, 2025
fcda047
remove
benjaminjohnson01 Sep 10, 2025
4eb5701
modify
benjaminjohnson01 Sep 10, 2025
a183a71
Merge branch 'main' into gh-138514-fix-getpass-echo-char
benjaminjohnson01 Sep 10, 2025
e8d08f8
modify TypeError message
benjaminjohnson01 Sep 10, 2025
749cc7d
modify TypeError message again
benjaminjohnson01 Sep 10, 2025
a6aed93
add comma to ValueError message
benjaminjohnson01 Sep 10, 2025
8385bc5
Merge branch 'main' into gh-138514-fix-getpass-echo-char
benjaminjohnson01 Sep 10, 2025
d1281cb
Merge branch 'main' into gh-138514-fix-getpass-echo-char
benjaminjohnson01 Sep 10, 2025
7253600
accept suggestion from picnixz
benjaminjohnson01 Sep 10, 2025
cf0cc73
accept language suggestion from picnixz
benjaminjohnson01 Sep 10, 2025
58f86cd
accept spacing suggestion from picnixz
benjaminjohnson01 Sep 10, 2025
6bb99d3
accept leftover artifact removal suggestion from picnixz
benjaminjohnson01 Sep 10, 2025
fde711f
add new line per suggestion
benjaminjohnson01 Sep 10, 2025
acc7bca
use subTest for getpass echo_char tests
benjaminjohnson01 Sep 10, 2025
461fd5b
switch to one-liner style
benjaminjohnson01 Sep 10, 2025
301ef46
use @support.subTests syntax
benjaminjohnson01 Sep 10, 2025
6e148e5
match style
benjaminjohnson01 Sep 10, 2025
caded77
match style further
benjaminjohnson01 Sep 10, 2025
981d49d
remove too-verbose comments
benjaminjohnson01 Sep 11, 2025
07534ce
remove too-verbose comments
benjaminjohnson01 Sep 11, 2025
2203e03
simplify non-ascii tests using ascii
benjaminjohnson01 Sep 11, 2025
6485375
test every non-printable ascii byte
benjaminjohnson01 Sep 11, 2025
2e7bd33
Merge branch 'main' into gh-138514-fix-getpass-echo-char
benjaminjohnson01 Sep 11, 2025
3629a53
remove s characters
benjaminjohnson01 Sep 11, 2025
e2ba1b7
Merge branch 'main' into gh-138514-fix-getpass-echo-char
benjaminjohnson01 Sep 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Doc/library/getpass.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ The :mod:`getpass` module provides two functions:

The *echo_char* argument controls how user input is displayed while typing.
If *echo_char* is ``None`` (default), input remains hidden. Otherwise,
*echo_char* must be a printable ASCII string and each typed character
is replaced by it. For example, ``echo_char='*'`` will display
asterisks instead of the actual input.
*echo_char* must be a single printable ASCII character and each
typed character is replaced by it. For example, ``echo_char='*'`` will
display asterisks instead of the actual input.

If echo free input is unavailable getpass() falls back to printing
a warning message to *stream* and reading from ``sys.stdin`` and
Expand Down
21 changes: 15 additions & 6 deletions Lib/getpass.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ def unix_getpass(prompt='Password: ', stream=None, *, echo_char=None):
prompt: Written on stream to ask for the input. Default: 'Password: '
stream: A writable file object to display the prompt. Defaults to
the tty. If no tty is available defaults to sys.stderr.
echo_char: A string used to mask input (e.g., '*'). If None, input is
hidden.
echo_char: A single ASCII character to mask input (e.g., '*').
If None, input is hidden.
Returns:
The seKr3t input.
Raises:
Expand Down Expand Up @@ -144,10 +144,19 @@ def fallback_getpass(prompt='Password: ', stream=None, *, echo_char=None):


def _check_echo_char(echo_char):
# ASCII excluding control characters
if echo_char and not (echo_char.isprintable() and echo_char.isascii()):
raise ValueError("'echo_char' must be a printable ASCII string, "
f"got: {echo_char!r}")
# Single-character ASCII excluding control characters
if echo_char is None:
return
if not isinstance(echo_char, str):
raise TypeError("'echo_char' must be a str or None, not "
f"{type(echo_char).__name__}")
if not (
len(echo_char) == 1
and echo_char.isprintable()
and echo_char.isascii()
):
raise ValueError("'echo_char' must be a single printable ASCII "
f"character, got: {echo_char!r}")


def _raw_input(prompt="", stream=None, input=None, echo_char=None):
Expand Down
33 changes: 33 additions & 0 deletions Lib/test/test_getpass.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,5 +201,38 @@ def test_control_chars_with_echo_char(self):
self.assertEqual('Password: *******\x08 \x08', mock_output.getvalue())


class GetpassEchoCharTest(unittest.TestCase):
# Tests for the `echo_char` parameter of `getpass.getpass()`
# Successful Validation Case(s)
def test_accepts_none(self):
getpass._check_echo_char(None)

@support.subTests('echo_char', ["*", "A", " "])
def test_accepts_single_printable_ascii(self, echo_char):
getpass._check_echo_char(echo_char)

# Rejected `echo_char` Case(s)
# ValueError Rejection(s)
def test_rejects_empty_string(self):
self.assertRaises(ValueError, getpass.getpass, echo_char="")

@support.subTests('echo_char', ["***", "AA", "aA*!"])
def test_rejects_multi_character_strings(self, echo_char):
self.assertRaises(ValueError, getpass.getpass, echo_char=echo_char)

@support.subTests('echo_char', ["Æ", "❤️", "🐍"])
def test_rejects_non_ascii(self, echo_char):
self.assertRaises(ValueError, getpass.getpass, echo_char=echo_char)

@support.subTests('echo_char', ["\n", "\t", "\r", "\x00", "\x7f", "\x07"])
def test_rejects_control_characters(self, echo_char):
self.assertRaises(ValueError, getpass.getpass, echo_char=echo_char)

# TypeError Rejection(s)
@support.subTests('echo_char', [b"*", 0, 0.0, [], {}])
def test_rejects_non_string(self, echo_char):
self.assertRaises(TypeError, getpass.getpass, echo_char=echo_char)


if __name__ == "__main__":
unittest.main()
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,7 @@ Jim Jewett
Pedro Diaz Jimenez
Orjan Johansen
Fredrik Johansson
Benjamin K. Johnson
Gregory K. Johnson
Kent Johnson
Michael Johnson
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Raise :exc:`ValueError` when a multi-character string is passed to the
*echo_char* parameter of :func:`getpass.getpass`. Patch by Benjamin Johnson.
Loading