1

paaword.py is a script where getpass() asked the user about the password and validates it. but i want to automate the whole process and used subprocess for it (main.py). And i am using python3.10

Problem:
problem is when i run the main.py in pycharm IDE it works normally (it automates the process). but when I run the script python3 main.py in ubuntu terminal it asked for the input.

I dont know why it behaves deifferent in in IDE and terminal?

password.py

import warnings
import getpass
import time

# Suppress warnings
warnings.filterwarnings("ignore", category=getpass.GetPassWarning)
for x in range(10):
    print(f"curnt index {x}")
time.sleep(5)
password = getpass.getpass("Enter your password: ")
if password != "test":
    print("wrong password")
else:
    print("correct password")

main.py

import subprocess

#  subprocess
proc = subprocess.Popen(["python", "password.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)

password = "test"
input_data = f"{password}\n"

# read output from the subprocess in real-time
while True:
    if proc.poll() is not None:
        break

    proc.stdin.write(input_data.encode())
    proc.stdin.flush()

    output = proc.stdout.readline().decode().strip()
    if output:
        print(output)

output in pycharm:

enter image description here

output in ubuntu terminal (20.04)

enter image description here

1 Answer 1

1

Judging by the screenshots, your OS is Linux.

In Linux, getpass() first tries to read directly from the process' controlling terminal (/dev/tty), or, if that fails, stdin using direct terminal I/O; and only if that fails, it falls back to regular I/O, displaying a warning.
Judging by the warnings in the IDE, the latter is exactly what happens in your first case.

Lib/getpass.py:

def unix_getpass(prompt='Password: ', stream=None):
    <...>
    try:
        # Always try reading and writing directly on the tty first.
        fd = os.open('/dev/tty', os.O_RDWR|os.O_NOCTTY)
        tty = io.FileIO(fd, 'w+')
        <...>
        input = io.TextIOWrapper(tty)
        <...>
    except OSError:
        # If that fails, see if stdin can be controlled.
        <...>
        try:
            fd = sys.stdin.fileno()
        except (AttributeError, ValueError):
            fd = None
            passwd = fallback_getpass(prompt, stream)    # fallback_getpass is what displays the warnings
        input = sys.stdin
        <...>
    if fd is not None:
        try:
            old = termios.tcgetattr(fd)
            <...>
        except termios.error:
            <...>
            passwd = fallback_getpass(prompt, stream)

    <...>
    return passwd

As you can see, getpass() is specifically designed to be interactive and resist intercepting its input. So if you need to provide a password automatically, use another way:

  • store it in a file readable only by you (e.g. SSH does that; you can provide that file as an argument and store other arguments there as well), or
  • use the system's keyring
  • and only fall back to getpass if the password was not provided that way and/or if you detect that the program is being run interactively (sys.stdin.isatty())
  • while it's also possible to provide the password on the command line -- in that case, you have to overwrite it in your process' stored command line to hide it from snooping. I couldn't find a way to do that in Python.

You can check Secure Password Handling in Python | Martin Heinz | Personal Website & Blog for a more detailed rundown of the above. (note: it suggests using envvars and load them from .env which would probably not apply to you. That's designed for .NET projects which due to the rigid structure of MS Visual Studio's build system, have had to rely on envvars for any variable values.)

3
  • thanks for insights, but the problem is password.py is just a dummy script. In real task issue is whenever we run the systemd service. There is a pin we needed to unlock. And that pin is being prompted on the console. So the systemd service is virtually blocking every time you try to restart that service. And we want to get around it by automating this service, we can avoid user to adding a pin every time if restarts the service. so I have to use getpass, because in real-time i will use getpass. Commented Dec 30, 2022 at 11:33
  • what if I use pexpect in python. Commented Dec 30, 2022 at 11:40
  • @RabiaHussain If it runs within a service, it shouldn't have an associated terminal so getpass should be fallbacking and you should not have this problem. If it does have an associated terminal, maybe you do not properly daemonize your service process? Commented Dec 31, 2022 at 8:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.