Skip to content

Ree-verse/masked-input

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

masked-input - Cross-Platform Password Input with Various Masking Options

masked-input logo

OverviewWhy use masked-input?FeaturesPrerequisitesInstallationUsageAPI ReferenceParametersReturn ValueExceptionsImplementation DetailsNotesKnown Issues and LimitationsExamplesCredits & InspirationLicenseStar History

GitHub commit activity GitHub Issues License Discord

PyPI - Python Version PyPI Status PyPI - Downloads


Overview

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.

Why Use masked-input?

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.

Features

  • 🌐 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 to getpass 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

Prerequisites

  • 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

Installation

Option 1: PyPI

pip install masked-input

Option 2: Manual Installation

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 .

Usage

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="")

API Reference

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

Parameters

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

Return Value

The function returns a string containing the user's input without the masking.

Exceptions

  • TypeError:

    • Raised:
      • If prompt or mask are not strings
      • If mask_repeat is not an integer
      • If char_timeout, timeout, or last_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
  • ValueError:

    • Raised:
      • If mask contains more than one character
      • If mask is an empty string but mode is not invisible
      • If mask is not one of '' or '•' when mode is invisible
      • If mask_repeat is not between 1 and 100
      • If timeout_prompt is provided without setting timeout
      • 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 is invisible or mask is empty and mask_repeat, char_timeout, or last_char_visible_duration are set to non-default values
      • If last_char_visible_duration is set while mode is not last-char-temporary
      • If last_char_visible_duration is not greater than zero
  • KeyboardInterrupt:

    • Raised:
      • If the user interrupts input (e.g., by pressing Ctrl+C)

Implementation Details

  • On Windows systems, the function uses the msvcrt module to read characters
  • On POSIX-based systems, it uses termios and tty to set the terminal to raw mode
  • Falls back to getpass when the script is not run in an interactive terminal

Notes

  • 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 an OverflowError or MemoryError

Known Issues and Limitations

  • 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 and Ctrl+J send the same sequences as Enter (\r) and Line Feed (\n), so they behave like pressing Enter. Similarly, Ctrl+H sends the same sequence as Backspace (\b), which deletes a character. Since masked-input focuses on handling individual key inputs (not complex shortcuts), these overlaps are expected and not treated specially just like in getpass and Python’s standard input.
  • 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’t last-char-temporary, as long as its value remains at 0.1 (the default).

Examples

# 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}')

Credits & Inspiration

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.

License

Released under the MIT License © 2025 Ree-verse.

Star History

Star History Chart

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.

Languages