1
1
"""Utilities to get a password and/or the current user name.
2
2
3
- getpass(prompt[, stream]) - Prompt for a password, with echo turned off.
3
+ getpass(prompt[, stream[, echo_char]]) - Prompt for a password, with echo
4
+ turned off and optional keyboard feedback.
4
5
getuser() - Get the user name from the environment or password database.
5
6
6
7
GetPassWarning - This UserWarning is issued when getpass() cannot prevent
25
26
class GetPassWarning (UserWarning ): pass
26
27
27
28
28
- def unix_getpass (prompt = 'Password: ' , stream = None ):
29
+ def unix_getpass (prompt = 'Password: ' , stream = None , * , echo_char = None ):
29
30
"""Prompt for a password, with echo turned off.
30
31
31
32
Args:
32
33
prompt: Written on stream to ask for the input. Default: 'Password: '
33
34
stream: A writable file object to display the prompt. Defaults to
34
35
the tty. If no tty is available defaults to sys.stderr.
36
+ echo_char: A string used to mask input (e.g., '*'). If None, input is
37
+ hidden.
35
38
Returns:
36
39
The seKr3t input.
37
40
Raises:
@@ -40,6 +43,8 @@ def unix_getpass(prompt='Password: ', stream=None):
40
43
41
44
Always restores terminal settings before returning.
42
45
"""
46
+ _check_echo_char (echo_char )
47
+
43
48
passwd = None
44
49
with contextlib .ExitStack () as stack :
45
50
try :
@@ -68,12 +73,16 @@ def unix_getpass(prompt='Password: ', stream=None):
68
73
old = termios .tcgetattr (fd ) # a copy to save
69
74
new = old [:]
70
75
new [3 ] &= ~ termios .ECHO # 3 == 'lflags'
76
+ if echo_char :
77
+ new [3 ] &= ~ termios .ICANON
71
78
tcsetattr_flags = termios .TCSAFLUSH
72
79
if hasattr (termios , 'TCSASOFT' ):
73
80
tcsetattr_flags |= termios .TCSASOFT
74
81
try :
75
82
termios .tcsetattr (fd , tcsetattr_flags , new )
76
- passwd = _raw_input (prompt , stream , input = input )
83
+ passwd = _raw_input (prompt , stream , input = input ,
84
+ echo_char = echo_char )
85
+
77
86
finally :
78
87
termios .tcsetattr (fd , tcsetattr_flags , old )
79
88
stream .flush () # issue7208
@@ -93,10 +102,11 @@ def unix_getpass(prompt='Password: ', stream=None):
93
102
return passwd
94
103
95
104
96
- def win_getpass (prompt = 'Password: ' , stream = None ):
105
+ def win_getpass (prompt = 'Password: ' , stream = None , * , echo_char = None ):
97
106
"""Prompt for password with echo off, using Windows getwch()."""
98
107
if sys .stdin is not sys .__stdin__ :
99
108
return fallback_getpass (prompt , stream )
109
+ _check_echo_char (echo_char )
100
110
101
111
for c in prompt :
102
112
msvcrt .putwch (c )
@@ -108,9 +118,15 @@ def win_getpass(prompt='Password: ', stream=None):
108
118
if c == '\003 ' :
109
119
raise KeyboardInterrupt
110
120
if c == '\b ' :
121
+ if echo_char and pw :
122
+ msvcrt .putch ('\b ' )
123
+ msvcrt .putch (' ' )
124
+ msvcrt .putch ('\b ' )
111
125
pw = pw [:- 1 ]
112
126
else :
113
127
pw = pw + c
128
+ if echo_char :
129
+ msvcrt .putwch (echo_char )
114
130
msvcrt .putwch ('\r ' )
115
131
msvcrt .putwch ('\n ' )
116
132
return pw
@@ -126,7 +142,14 @@ def fallback_getpass(prompt='Password: ', stream=None):
126
142
return _raw_input (prompt , stream )
127
143
128
144
129
- def _raw_input (prompt = "" , stream = None , input = None ):
145
+ def _check_echo_char (echo_char ):
146
+ # ASCII excluding control characters
147
+ if echo_char and not (echo_char .isprintable () and echo_char .isascii ()):
148
+ raise ValueError ("'echo_char' must be a printable ASCII string, "
149
+ f"got: { echo_char !r} " )
150
+
151
+
152
+ def _raw_input (prompt = "" , stream = None , input = None , echo_char = None ):
130
153
# This doesn't save the string in the GNU readline history.
131
154
if not stream :
132
155
stream = sys .stderr
@@ -143,6 +166,8 @@ def _raw_input(prompt="", stream=None, input=None):
143
166
stream .write (prompt )
144
167
stream .flush ()
145
168
# NOTE: The Python C API calls flockfile() (and unlock) during readline.
169
+ if echo_char :
170
+ return _readline_with_echo_char (stream , input , echo_char )
146
171
line = input .readline ()
147
172
if not line :
148
173
raise EOFError
@@ -151,6 +176,35 @@ def _raw_input(prompt="", stream=None, input=None):
151
176
return line
152
177
153
178
179
+ def _readline_with_echo_char (stream , input , echo_char ):
180
+ passwd = ""
181
+ eof_pressed = False
182
+ while True :
183
+ char = input .read (1 )
184
+ if char == '\n ' or char == '\r ' :
185
+ break
186
+ elif char == '\x03 ' :
187
+ raise KeyboardInterrupt
188
+ elif char == '\x7f ' or char == '\b ' :
189
+ if passwd :
190
+ stream .write ("\b \b " )
191
+ stream .flush ()
192
+ passwd = passwd [:- 1 ]
193
+ elif char == '\x04 ' :
194
+ if eof_pressed :
195
+ break
196
+ else :
197
+ eof_pressed = True
198
+ elif char == '\x00 ' :
199
+ continue
200
+ else :
201
+ passwd += char
202
+ stream .write (echo_char )
203
+ stream .flush ()
204
+ eof_pressed = False
205
+ return passwd
206
+
207
+
154
208
def getuser ():
155
209
"""Get the username from the environment or password database.
156
210
0 commit comments