Repository navigation
gh-138577: Fix keyboard shortcuts in getpass with echo_char #141597
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
bb60653
31e35e4
b8609bd
2691ad9
6a59f3b
d280ce9
3f1a861
537392c
e1e4aa3
3e05fa3
90da605
ef1efcb
a7c1de3
78b2c6a
e1461a7
741a817
cc5dc99
0f5f5c8
9d90dfa
3b2ae38
919af5c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -217,7 +217,7 @@ def _raw_input(prompt="", stream=None, input=None, echo_char=None, | |
| # NOTE: The Python C API calls flockfile() (and unlock) during readline. | ||
| if echo_char: | ||
| return _readline_with_echo_char(stream, input, echo_char, | ||
| term_ctrl_chars) | ||
| term_ctrl_chars, prompt) | ||
| line = input.readline() | ||
| if not line: | ||
| raise EOFError | ||
|
|
@@ -229,9 +229,10 @@ def _raw_input(prompt="", stream=None, input=None, echo_char=None, | |
| class _PasswordLineEditor: | ||
| """Handles line editing for password input with echo character.""" | ||
|
|
||
| def __init__(self, stream, echo_char, ctrl_chars): | ||
| def __init__(self, stream, echo_char, ctrl_chars, prompt=""): | ||
| self.stream = stream | ||
| self.echo_char = echo_char | ||
| self.prompt = prompt | ||
| self.passwd = [] | ||
| self.cursor_pos = 0 | ||
| self.eof_pressed = False | ||
|
|
@@ -247,18 +248,18 @@ def __init__(self, stream, echo_char, ctrl_chars): | |
| '\b': self._handle_erase, # Backspace | ||
| } | ||
|
|
||
| def _refresh_display(self): | ||
| def _refresh_display(self, prev_len=None): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The whole class is private, I don't think that it's useful to mark methods as private as well. Can you remove th underscore ("_") prefix from all methods?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, I’ve made the change. Just for my understanding and to follow the preferred style going forward: if a class is already internal/non-public, is it generally considered unnecessary to prefix all its methods with _ as well?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know if it's a general rule, but yes, if a class is private, I consider that it's not needed to mark its methods as private as well. |
||
| """Redraw the entire password line with *echo_char*.""" | ||
| self.stream.write('\r' + ' ' * len(self.passwd) + '\r') | ||
| self.stream.write(self.echo_char * len(self.passwd)) | ||
| prompt_len = len(self.prompt) | ||
| # Use prev_len if given, otherwise current password length | ||
| clear_len = prev_len if prev_len is not None else len(self.passwd) | ||
| # Clear the entire line (prompt + password) and rewrite | ||
| self.stream.write('\r' + ' ' * (prompt_len + clear_len) + '\r') | ||
| self.stream.write(self.prompt + self.echo_char * len(self.passwd)) | ||
| if self.cursor_pos < len(self.passwd): | ||
| self.stream.write('\b' * (len(self.passwd) - self.cursor_pos)) | ||
| self.stream.flush() | ||
|
|
||
| def _erase_chars(self, count): | ||
| """Erase *count* echo characters from display.""" | ||
| self.stream.write("\b \b" * count) | ||
|
|
||
| def _insert_char(self, char): | ||
| """Insert *char* at cursor position.""" | ||
| self.passwd.insert(self.cursor_pos, char) | ||
|
|
@@ -282,28 +283,23 @@ def _handle_erase(self): | |
| """Delete character before cursor (Backspace/DEL).""" | ||
| if self.cursor_pos <= 0: | ||
| return | ||
| prev_len = len(self.passwd) | ||
| del self.passwd[self.cursor_pos - 1] | ||
| self.cursor_pos -= 1 | ||
| # Only refresh if deleting from middle | ||
| if self.cursor_pos < len(self.passwd): | ||
| self._refresh_display() | ||
| else: | ||
| self.stream.write("\b \b") | ||
| self.stream.flush() | ||
| self._refresh_display(prev_len) | ||
|
|
||
| def _handle_kill_line(self): | ||
| """Erase entire line (Ctrl+U).""" | ||
| self._erase_chars(len(self.passwd)) | ||
| prev_len = len(self.passwd) | ||
| self.passwd.clear() | ||
| self.cursor_pos = 0 | ||
| self.stream.flush() | ||
| self._refresh_display(prev_len) | ||
|
|
||
| def _handle_kill_forward(self): | ||
| """Kill from cursor to end (Ctrl+K).""" | ||
| chars_to_delete = len(self.passwd) - self.cursor_pos | ||
| prev_len = len(self.passwd) | ||
| del self.passwd[self.cursor_pos:] | ||
| self._erase_chars(chars_to_delete) | ||
| self.stream.flush() | ||
| self._refresh_display(prev_len) | ||
|
|
||
| def _handle_erase_word(self): | ||
| """Erase previous word (Ctrl+W).""" | ||
|
|
@@ -315,8 +311,9 @@ def _handle_erase_word(self): | |
| while self.cursor_pos > 0 and self.passwd[self.cursor_pos - 1] != ' ': | ||
| self.cursor_pos -= 1 | ||
| # Remove the deleted portion | ||
| prev_len = len(self.passwd) | ||
| del self.passwd[self.cursor_pos:old_cursor] | ||
| self._refresh_display() | ||
| self._refresh_display(prev_len) | ||
|
|
||
| def handle(self, char): | ||
| """Handle a single character input. Returns True if handled.""" | ||
|
|
@@ -328,12 +325,13 @@ def handle(self, char): | |
| return False | ||
|
|
||
|
|
||
| def _readline_with_echo_char(stream, input, echo_char, term_ctrl_chars=None): | ||
| def _readline_with_echo_char(stream, input, echo_char, term_ctrl_chars=None, | ||
| prompt=""): | ||
| """Read password with echo character and line editing support.""" | ||
| if term_ctrl_chars is None: | ||
| term_ctrl_chars = _POSIX_CTRL_CHARS.copy() | ||
|
vstinner marked this conversation as resolved.
Outdated
|
||
|
|
||
| editor = _PasswordLineEditor(stream, echo_char, term_ctrl_chars) | ||
| editor = _PasswordLineEditor(stream, echo_char, term_ctrl_chars, prompt) | ||
|
|
||
| while True: | ||
|
vstinner marked this conversation as resolved.
Outdated
|
||
| char = input.read(1) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.