Merge pull request #575 from PR0M3TH3AN/codex/add-masked-input-function-with-tests

Add masked input utility
This commit is contained in:
thePR0M3TH3AN
2025-07-16 03:34:03 -04:00
committed by GitHub
3 changed files with 105 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
import types
from utils import seed_prompt
def test_masked_input_posix_backspace(monkeypatch, capsys):
seq = iter(["a", "b", "\x7f", "c", "\n"])
monkeypatch.setattr(seed_prompt.sys.stdin, "read", lambda n=1: next(seq))
monkeypatch.setattr(seed_prompt.sys.stdin, "fileno", lambda: 0)
monkeypatch.setattr(seed_prompt.termios, "tcgetattr", lambda fd: None)
monkeypatch.setattr(seed_prompt.termios, "tcsetattr", lambda fd, *_: None)
monkeypatch.setattr(seed_prompt.tty, "setraw", lambda fd: None)
result = seed_prompt.masked_input("Enter: ")
assert result == "ac"
out = capsys.readouterr().out
assert out.startswith("Enter: ")
assert out.count("*") == 3
def test_masked_input_windows_space(monkeypatch, capsys):
seq = iter(["x", "y", " ", "z", "\r"])
fake_msvcrt = types.SimpleNamespace(getwch=lambda: next(seq))
monkeypatch.setattr(seed_prompt, "msvcrt", fake_msvcrt)
monkeypatch.setattr(seed_prompt.sys, "platform", "win32", raising=False)
result = seed_prompt.masked_input("Password: ")
assert result == "xy z"
out = capsys.readouterr().out
assert out.startswith("Password: ")
assert out.count("*") == 4

View File

@@ -25,6 +25,7 @@ try:
update_checksum_file,
)
from .password_prompt import prompt_for_password
from .seed_prompt import masked_input
from .input_utils import timed_input
from .memory_protection import InMemorySecret
from .clipboard import copy_to_clipboard
@@ -58,6 +59,7 @@ __all__ = [
"exclusive_lock",
"shared_lock",
"prompt_for_password",
"masked_input",
"timed_input",
"InMemorySecret",
"copy_to_clipboard",

73
src/utils/seed_prompt.py Normal file
View File

@@ -0,0 +1,73 @@
import os
import sys
try:
import msvcrt # type: ignore
except ImportError: # pragma: no cover - Windows only
msvcrt = None # type: ignore
try:
import termios
import tty
except ImportError: # pragma: no cover - POSIX only
termios = None # type: ignore
tty = None # type: ignore
def _masked_input_windows(prompt: str) -> str:
"""Windows implementation using ``msvcrt``."""
if msvcrt is None: # pragma: no cover - should not happen
return input(prompt)
sys.stdout.write(prompt)
sys.stdout.flush()
buffer: list[str] = []
while True:
ch = msvcrt.getwch()
if ch in ("\r", "\n"):
sys.stdout.write("\n")
return "".join(buffer)
if ch in ("\b", "\x7f"):
if buffer:
buffer.pop()
sys.stdout.write("\b \b")
else:
buffer.append(ch)
sys.stdout.write("*")
sys.stdout.flush()
def _masked_input_posix(prompt: str) -> str:
"""POSIX implementation using ``termios`` and ``tty``."""
if termios is None or tty is None: # pragma: no cover - should not happen
return input(prompt)
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
sys.stdout.write(prompt)
sys.stdout.flush()
buffer: list[str] = []
try:
tty.setraw(fd)
while True:
ch = sys.stdin.read(1)
if ch in ("\r", "\n"):
sys.stdout.write("\n")
return "".join(buffer)
if ch in ("\x7f", "\b"):
if buffer:
buffer.pop()
sys.stdout.write("\b \b")
else:
buffer.append(ch)
sys.stdout.write("*")
sys.stdout.flush()
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
def masked_input(prompt: str) -> str:
"""Return input from the user while masking typed characters."""
if sys.platform == "win32":
return _masked_input_windows(prompt)
return _masked_input_posix(prompt)