Handles line editing for password input with echo character.
| 250 | |
| 251 | |
| 252 | class _PasswordLineEditor: |
| 253 | """Handles line editing for password input with echo character.""" |
| 254 | |
| 255 | def __init__(self, stream, echo_char, ctrl_chars, prompt=""): |
| 256 | self.stream = stream |
| 257 | self.echo_char = echo_char |
| 258 | self.prompt = prompt |
| 259 | self.password = [] |
| 260 | self.cursor_pos = 0 |
| 261 | self.eof_pressed = False |
| 262 | self.literal_next = False |
| 263 | self.ctrl = ctrl_chars |
| 264 | self.dispatch = { |
| 265 | ctrl_chars['SOH']: self.handle_move_start, # Ctrl+A |
| 266 | ctrl_chars['ENQ']: self.handle_move_end, # Ctrl+E |
| 267 | ctrl_chars['VT']: self.handle_kill_forward, # Ctrl+K |
| 268 | ctrl_chars['KILL']: self.handle_kill_line, # Ctrl+U |
| 269 | ctrl_chars['WERASE']: self.handle_erase_word, # Ctrl+W |
| 270 | ctrl_chars['ERASE']: self.handle_erase, # DEL |
| 271 | ctrl_chars['BS']: self.handle_erase, # Backspace |
| 272 | # special characters |
| 273 | ctrl_chars['LNEXT']: self.handle_literal_next, # Ctrl+V |
| 274 | ctrl_chars['EOF']: self.handle_eof, # Ctrl+D |
| 275 | ctrl_chars['INTR']: self.handle_interrupt, # Ctrl+C |
| 276 | '\x00': self.handle_nop, # ignore NUL |
| 277 | } |
| 278 | |
| 279 | def refresh_display(self, prev_len=None): |
| 280 | """Redraw the entire password line with *echo_char*. |
| 281 | |
| 282 | If *prev_len* is not specified, the current password length is used. |
| 283 | """ |
| 284 | prompt_len = len(self.prompt) |
| 285 | clear_len = prev_len if prev_len is not None else len(self.password) |
| 286 | # Clear the entire line (prompt + password) and rewrite. |
| 287 | self.stream.write('\r' + ' ' * (prompt_len + clear_len) + '\r') |
| 288 | self.stream.write(self.prompt + self.echo_char * len(self.password)) |
| 289 | if self.cursor_pos < len(self.password): |
| 290 | self.stream.write('\b' * (len(self.password) - self.cursor_pos)) |
| 291 | self.stream.flush() |
| 292 | |
| 293 | def insert_char(self, char): |
| 294 | """Insert *char* at cursor position.""" |
| 295 | self.password.insert(self.cursor_pos, char) |
| 296 | self.cursor_pos += 1 |
| 297 | # Only refresh if inserting in middle. |
| 298 | if self.cursor_pos < len(self.password): |
| 299 | self.refresh_display() |
| 300 | else: |
| 301 | self.stream.write(self.echo_char) |
| 302 | self.stream.flush() |
| 303 | |
| 304 | def is_eol(self, char): |
| 305 | """Check if *char* is a line terminator.""" |
| 306 | return char in ('\r', '\n') |
| 307 | |
| 308 | def is_eof(self, char): |
| 309 | """Check if *char* is a file terminator.""" |
no outgoing calls
no test coverage detected
searching dependent graphs…