Overview •
Why use masked-input
? •
Features •
Prerequisites •
Installation •
Usage •
API Reference •
Parameters •
Return Value •
Exceptions •
Implementation Details •
Notes •
Known Issues and Limitations •
Examples •
Credits & Inspiration •
License •
Star History
The masked-input
function provides a secure way to read passwords or other sensitive input from the command line with customizable masking. It works across different operating systems and handles various edge cases.
Unlike getpass
, masked-input
provides visual feedback through masking, improving UX during password entry, especially in CLI tools. It also offers full customization and graceful fallback behavior.
- 🌐 Cross-platform compatibility - Works on Windows and POSIX-based systems
- 🎭 Customizable masking character - Define your own character to mask input (like *, •, or nothing at all)
- 🔢 Adjustable mask repeat count - Control how many masking characters appear per typed character, from single to multiple symbols, or even none
- ⌫ Proper handling of backspace and special keys - Properly deletes characters and gracefully skips over arrow keys, escape sequences, and other exotic key combos without breaking the mask flow
- ⏱️ Per-character delay - Optionally pauses after each typed character
- ⌛ Global input timeout - Abort password input after a defined total duration and optionally show a custom message when time runs out
- 🔐 Character limit - Define a maximum password length
- 🧭 Selectable input mode - Choose between:
- standard: masks each char normally
- last-char-temporary: briefly shows the last char before masking
- invisible: shows nothing at all
- pixel-lock: masks then deletes instantly
- 🧪 Fallback to standard
getpass
when necessary - Automatically switches togetpass
when masking isn’t feasible - ✍️ Clear and concise docstrings - Parameters documented with no fluff, easy to read and maintain
- ✅ Built-in unit tests - Comes with a robust suite of unit tests covering key usage scenarios to ensure reliability
- Python 3.9 or higher
- No external dependencies (uses only standard library modules)
- Terminal or command-line environment that supports interactive input
- On Windows: A terminal that supports ANSI escape sequences for best results
- On POSIX: Terminal with standard TTY capabilities
pip install masked-input
Clone the repository or download the source code, then navigate to the project directory:
git clone https://github.com/ree-verse/masked-input.git
cd path/to/your/folder
pip install .
from masked_input import masked_input
# Basic usage with default settings
password = masked_input() # Displays "Password: " with "•" masking
# Custom prompt, mask character, and mask repeat count
password = masked_input(
prompt="Enter secret key: ",
mask="*",
mask_repeat=2
)
# Hide input completely (no masking character)
password = masked_input(mask="")
masked_input(
prompt: str = 'Password: ',
mask: str = '•',
mask_repeat: int = 1,
char_timeout: Optional[Union[int, float]] = None,
timeout: Optional[Union[int, float]] = None,
timeout_prompt: Optional[str] = None,
char_limit: Optional[int] = None,
mode: Literal['standard', 'last-char-temporary', 'invisible', 'pixel-lock'] = 'standard',
last_char_visible_duration: Union[int, float] = 0.1
) -> str
Parameter | Type | Default | Description |
---|---|---|---|
prompt |
str |
'Password: ' |
The text displayed to prompt the user for input |
mask |
str |
'•' |
Character used to mask each input character (use empty string to hide completely) |
mask_repeat |
int |
1 |
Number of mask symbols displayed per each input character (1-100) |
char_timeout |
int , float , None |
None |
Delay in seconds after each character input |
timeout |
int , float , None |
None |
Total timeout in seconds for the whole input |
timeout_prompt |
str , None |
None |
Message displayed on timeout; requires timeout to be set |
char_limit |
int , None |
None |
Maximum number of input characters allowed |
mode |
str |
'standard' |
Input display mode with various options [^*] |
last_char_visible_duration |
int , float |
0.1 |
Duration in seconds the last typed character is visible (only for last-char-temporary ) |
[^*]
'standard'
: Shows mask character for each typed character'last-char-temporary'
: Briefly shows each character before masking it'invisible'
: No visual feedback (no mask, no cursor movement)'pixel-lock'
: No cursor movement to prevent revealing input length
The function returns a string containing the user's input without the masking.
-
TypeError
:- Raised:
- If
prompt
ormask
are not strings - If
mask_repeat
is not an integer - If
char_timeout
,timeout
, orlast_char_visible_duration
are not int or float - If
timeout_prompt
is not a string - If
char_limit
is not an integer - If
mode
is not a string
- If
- Raised:
-
ValueError
:- Raised:
- If
mask
contains more than one character - If
mask
is an empty string butmode
is notinvisible
- If
mask
is not one of''
or'•'
when mode isinvisible
- If
mask_repeat
is not between 1 and 100 - If
timeout_prompt
is provided without settingtimeout
- If
timeout_prompt
is an empty string - If
char_limit
is set but not positive - If
mode
is not one of:standard
,last-char-temporary
,invisible
,pixel-lock
- If
mode
isinvisible
ormask
is empty andmask_repeat
,char_timeout
, orlast_char_visible_duration
are set to non-default values - If
last_char_visible_duration
is set whilemode
is notlast-char-temporary
- If
last_char_visible_duration
is not greater than zero
- If
- Raised:
-
KeyboardInterrupt
:- Raised:
- If the user interrupts input (e.g., by pressing Ctrl+C)
- Raised:
- On Windows systems, the function uses the
msvcrt
module to read characters - On POSIX-based systems, it uses
termios
andtty
to set the terminal to raw mode - Falls back to
getpass
when the script is not run in an interactive terminal
- Special keys (arrows, function keys, etc.) are properly handled and ignored
- The function correctly processes backspace to remove the last character
- Only printable characters are added to the password
- The terminal is restored to its original state even if an exception occurs
- The
mask_repeat
parameter must be an integer between 1 and 100 to avoid overflow errors or memory issues during mask repetition. Values outside this range may raise anOverflowError
orMemoryError
- Windows Character Input: On Windows systems, certain characters like "à" and other accented characters require pressing the key twice to be registered correctly. This is due to how the Windows console handles special characters.
- Very long passwords may cause display issues in terminals with limited width. This does not affect password entry itself, only its visual representation.
- On Windows and POSIX,
Ctrl+M
andCtrl+J
send the same sequences asEnter
(\r
) andLine Feed
(\n
), so they behave like pressing Enter. Similarly,Ctrl+H
sends the same sequence as Backspace (\b
), which deletes a character. Sincemasked-input
focuses on handling individual key inputs (not complex shortcuts), these overlaps are expected and not treated specially just like ingetpass
and Python’s standardinput
. - Input with non-standard keyboard layouts may have unexpected behavior on some platforms.
- Some terminal emulators may not properly handle the masking behavior, especially when using uncommon terminal settings.
- No error is raised if
last_char_visible_duration
is set while mode isn’tlast-char-temporary
, as long as its value remains at 0.1 (the default).
# Standard masking:
password = masked_input(prompt="Enter password: ", mask="*")
# Temporarily showing each character before masking:
password = masked_input(
prompt="Enter password: ",
mask="*",
mode="last-char-temporary",
last_char_visible_duration=0.2
)
# Invisible input (no visual feedback):
password = masked_input(mode="invisible")
# Input with timeout:
password = masked_input(
timeout=30,
timeout_prompt="Input timed out!"
)
# Character limit:
password = masked_input(char_limit=12)
# Delay between each character input (slows down rendering intentionally):
password = masked_input(
prompt="Password: ",
char_timeout=0.3
)
# Mask repeated for each character (e.g., "**" instead of "*"):
password = masked_input(
mask ="*",
mask_repeat=2
)
# Example usage:
password = masked_input(prompt='Enter your password: ', mask='*', mask_repeat=2)
print(f'Your password is: {password}')
This project was inspired by pwinput, which provided the initial idea and implementation of cross-platform masked input in Python.
Big respect to Al Sweigart for his contributions to the open-source ecosystem.
Released under the MIT License © 2025 Ree-verse.
Disclaimer: This program may contain bugs. It has only been tested on AZERTY keyboards and may not function correctly on QWERTY layouts. If you encounter any issues, please open an issue to report them.