This article explains how to read a password so each keystroke can show as * instead of plain text. It covers getpass (including echo_char on Python 3.14+), pwinput on older versions, and legacy stdiomask. It also explains why Python user input with input() is a weak default for secrets, and how real terminals differ from some IDE run windows. For reading credentials from the pwd module on Unix, see that guide when masking is not the goal.
You do not “convert” a string after typing—the goal is to read the password already masked from the keyboard.
Tested on: Python 3.13.3; kernel 6.14.0-37-generic.
Best way to show asterisks for password input in Python
- Python 3.14 or newer: use
getpass.getpass(..., echo_char="*"). It is in the standard library, so you do not add a masking dependency for stars. - Python 3.13 and older: the same
getpassAPI has noecho_charyet. Installpwinputand usepwinput.pwinput(..., mask="*")for asterisks while typing. - Any supported Python: if silent typing (no characters on screen) is enough, default
getpass.getpass("...")withoutecho_charis usually the simplest stdlib choice.
Hide password input using getpass
getpass lives in the standard library. Import it and call getpass.getpass: by default the user types without characters echoed—sometimes called “blind” entry. That is not the same as printing *, but it still reduces shoulder-surfing in a normal terminal.
import getpass
password = getpass.getpass("Enter password: ")
print("Password received")Show asterisks using getpass echo_char in Python 3.14+
Starting in Python 3.14, getpass.getpass accepts a keyword-only argument echo_char. Set it to a single printable ASCII character such as '*' so the user sees that character for each keypress. Leave echo_char=None (the default) to keep the older silent behavior.
On Unix, a non-empty echo_char can put the terminal in noncanonical mode and change how some control keys behave—see the note in the official getpass documentation if you ship this to advanced terminal users.
import getpass
password = getpass.getpass("Enter password: ", echo_char="*")
print("Password received")On Python before 3.14, passing echo_char raises TypeError. The following shell session uses /usr/bin/python3.13 (3.13.3) to show the older signature and the error (non-interactive checks only).
Signature on 3.13 (no echo_char)
$ /usr/bin/python3.13 -c "import getpass, inspect; print('Python:', __import__('sys').version.split()[0]); print('getpass.getpass signature:', inspect.signature(getpass.getpass))"
Python: 3.13.3
getpass.getpass signature: (prompt='Password: ', stream=None)echo_char on 3.13
$ /usr/bin/python3.13 -c "import getpass; getpass.getpass('x', echo_char='*')"
TypeError: unix_getpass() got an unexpected keyword argument 'echo_char'Show asterisks using pwinput in Python 3.13 and older
pwinput prints a mask character (for example *) while the user types. Prefer it over stdiomask for new projects. Install it per environment (often inside a venv):
pip install pwinputimport pwinput
password = pwinput.pwinput(prompt="Enter password: ", mask="*")
print("Password received")In a real terminal you should see one * per character as you type. Masking still needs a TTY in practice. On Python 3.13.3, after creating a venv and installing pwinput, the function signature looks like this:
$ /usr/bin/python3.13 -m venv /tmp/pwi-test
$ /tmp/pwi-test/bin/pip install -q pwinput
$ /tmp/pwi-test/bin/python -c "import pwinput, inspect; print('pwinput.pwinput:', inspect.signature(pwinput.pwinput))"
pwinput.pwinput: (prompt='Password: ', mask='*')Legacy option: stdiomask
The stdiomask package remains on PyPI for older codebases. The install name is:
pip install stdiomask(not pip install stdio, a common typo in older posts).
import stdiomask
password = stdiomask.getpass(prompt="Enter password: ")
print("Password received")For new code, plan a move to pwinput when you can.
getpass vs pwinput vs stdiomask
| Approach | Asterisks while typing? | Needs pip install? |
When to use it |
|---|---|---|---|
getpass.getpass() default |
No—input is hidden | No | Silent typing, stdlib only, wide Python range |
getpass.getpass(..., echo_char="*") |
Yes, on Python 3.14+ | No | Stdlib masking on a new enough interpreter |
pwinput.pwinput(..., mask="*") |
Yes (where the TTY allows) | Yes (pwinput) |
Python 3.13 and older, or masking without 3.14+ |
stdiomask.getpass(...) |
Yes (where supported) | Yes (stdiomask) |
Legacy projects; migrate to pwinput when practical |
Do you need to pip install getpass?
No. getpass is part of the Python standard library. You only run import getpass.
Third-party helpers such as pwinput and stdiomask are installed with pip into your environment (or your virtual environment), not getpass itself.
Why input() should not be used for passwords
input() always echoes what you type, so anyone reading the screen can see the secret. For real passwords, use getpass or a masking helper instead.
If you are learning if / else flow around a login, keep the read path masked from the start—do not capture the password with input() and try to “fix” it afterward with string tricks once it has already been displayed.
Password masking in terminal vs IDE
Masking and hidden input work best in a real terminal (your OS console, SSH session, or similar). Some IDE-embedded run windows, notebooks, and online sandboxes do not provide a full TTY. When echo-free input is unavailable, getpass may fall back, print a warning, and read from standard input—which can mean characters become visible or behavior feels odd.
If masking fails where you develop, run the same script in a normal system terminal before you assume the library is broken.
Example: masked password check
Minimal sketch for local tries only—never ship a hard-coded password in production.
import pwinput
saved_password = "python123"
password = pwinput.pwinput("Enter password: ", mask="*")
if password == saved_password:
print("Login successful")
else:
print("Invalid password")Install pwinput first (pip install pwinput). In a real system you compare a password hash, you do not embed plaintext secrets in source, and you avoid printing or logging real passwords.
Common mistakes with Python password input
- Using
input()for real secrets so everything echoes. print(password)or logging the value after entry—acceptable only in toy demos.- Running
pip install getpass—getpassis standard library; that is not how you obtain it. - Expecting
echo_charon Python before 3.14—you getTypeError; usepwinputor upgrade. - Starting new projects on
stdiomaskinstead ofpwinputwhenpwinputis the maintained choice for new work. - Testing only inside an IDE panel that lacks TTY masking, then concluding “Python cannot mask passwords.”
Python password input quick reference table
| What you need | What to do |
|---|---|
| Silent typing, stdlib only | import getpass then getpass.getpass("...") |
| Asterisks, Python 3.14+ | getpass.getpass("...", echo_char="*") |
| Asterisks, Python 3.13 and older | pip install pwinput then pwinput.pwinput(..., mask="*") |
| Legacy code already using stdiomask | pip install stdiomask; plan a move to pwinput |
| Quick line for learners (not secure) | input()—avoid for real passwords |
Summary
For asterisks while typing a password: on Python 3.14+, prefer getpass.getpass(..., echo_char="*"). On older Python, use pwinput with mask="*". For silent entry with no extra packages, default getpass.getpass is enough. Import getpass from the stdlib—never pip install it. Treat stdiomask as legacy. Avoid input() for secrets, and use a real terminal when an IDE run window misbehaves.

