Skip to content

Commit 9d90dfa

Browse files
committed
refactor! use more handlers
1 parent 0f5f5c8 commit 9d90dfa

File tree

1 file changed

+45
-22
lines changed

1 file changed

+45
-22
lines changed

Lib/getpass.py

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class GetPassWarning(UserWarning): pass
2828

2929
# Default POSIX control character mappings
3030
_POSIX_CTRL_CHARS = frozendict({
31-
'ERASE': '\x7f', # DEL/Backspace
31+
'BS': '\x08', # Backspace
32+
'ERASE': '\x7f', # DEL
3233
'KILL': '\x15', # Ctrl+U - kill line
3334
'WERASE': '\x17', # Ctrl+W - erase word
3435
'LNEXT': '\x16', # Ctrl+V - literal next
@@ -251,13 +252,18 @@ def __init__(self, stream, echo_char, ctrl_chars, prompt=""):
251252
self.literal_next = False
252253
self.ctrl = ctrl_chars
253254
self.dispatch = {
254-
ctrl_chars['SOH']: self.handle_move_start, # Ctrl+A
255-
ctrl_chars['ENQ']: self.handle_move_end, # Ctrl+E
256-
ctrl_chars['VT']: self.handle_kill_forward, # Ctrl+K
257-
ctrl_chars['KILL']: self.handle_kill_line, # Ctrl+U
258-
ctrl_chars['WERASE']: self.handle_erase_word, # Ctrl+W
259-
ctrl_chars['ERASE']: self.handle_erase, # DEL
260-
'\b': self.handle_erase, # Backspace
255+
ctrl_chars['SOH']: self.handle_move_start, # Ctrl+A
256+
ctrl_chars['ENQ']: self.handle_move_end, # Ctrl+E
257+
ctrl_chars['VT']: self.handle_kill_forward, # Ctrl+K
258+
ctrl_chars['KILL']: self.handle_kill_line, # Ctrl+U
259+
ctrl_chars['WERASE']: self.handle_erase_word, # Ctrl+W
260+
ctrl_chars['ERASE']: self.handle_erase, # DEL
261+
ctrl_chars['BS']: self.handle_erase, # Backspace
262+
# special characters
263+
ctrl_chars['LNEXT']: self.handle_literal_next, # Ctrl+V
264+
ctrl_chars['EOF']: self.handle_eof, # Ctrl+D
265+
ctrl_chars['INTR']: self.handle_interrupt, # Ctrl+C
266+
'\x00': self.handle_nop, # ignore NUL
261267
}
262268

263269
def refresh_display(self, prev_len=None):
@@ -285,6 +291,14 @@ def insert_char(self, char):
285291
self.stream.write(self.echo_char)
286292
self.stream.flush()
287293

294+
def is_eol(self, char):
295+
"""Check if *char* is a line terminator."""
296+
return char in ('\r', '\n')
297+
298+
def is_eof(self, char):
299+
"""Check if *char* is a file terminator."""
300+
return char == self.ctrl['EOF']
301+
288302
def handle_move_start(self):
289303
"""Move cursor to beginning (Ctrl+A)."""
290304
self.cursor_pos = 0
@@ -332,6 +346,23 @@ def handle_erase_word(self):
332346
del self.password[self.cursor_pos:old_cursor]
333347
self.refresh_display(prev_len)
334348

349+
def handle_literal_next(self):
350+
"""State transition to indicate that the next character is literal."""
351+
assert self.literal_next is False
352+
self.literal_next = True
353+
354+
def handle_eof(self):
355+
"""State transition to indicate that the pressed character was EOF."""
356+
assert self.eof_pressed is False
357+
self.eof_pressed = True
358+
359+
def handle_interrupt(self):
360+
"""Raise a KeyboardInterrupt after Ctrl+C has been received."""
361+
raise KeyboardInterrupt
362+
363+
def handle_nop(self):
364+
"""Handler for an ignored character."""
365+
335366
def handle(self, char):
336367
"""Handle a single character input. Returns True if handled."""
337368
handler = self.dispatch.get(char)
@@ -345,33 +376,25 @@ def readline(self, input):
345376
while True:
346377
assert self.cursor_pos >= 0
347378
char = input.read(1)
348-
349-
# Check for line terminators
350-
if char in ('\n', '\r'):
379+
if self.is_eol(char):
351380
break
381+
# Handle literal next mode first as Ctrl+V quotes characters.
352382
elif self.literal_next:
353-
# Handle literal next mode first as Ctrl+V quotes characters.
354383
self.insert_char(char)
355384
self.literal_next = False
356-
# Check if it's the LNEXT character
357-
elif char == self.ctrl['LNEXT']:
358-
self.literal_next = True
359-
# Check for special control characters
360-
elif char == self.ctrl['INTR']:
361-
raise KeyboardInterrupt
362-
elif char == self.ctrl['EOF']:
385+
# Handle EOF now as Ctrl+D must be pressed twice
386+
# consecutively to stop reading from the input.
387+
elif self.is_eof(char):
363388
if self.eof_pressed:
364389
break
365-
elif char == '\x00':
366-
pass
367390
elif self.handle(char):
368391
# Dispatched to handler.
369392
pass
370393
else:
371394
# Insert as normal character.
372395
self.insert_char(char)
373396

374-
self.eof_pressed = (char == self.ctrl['EOF'])
397+
self.eof_pressed = self.is_eof(char)
375398

376399
return ''.join(self.password)
377400

0 commit comments

Comments
 (0)