Python get user input password with timeout - python

I'm writing a Python application and would like to prompt a user for their password. If they do not enter it within 60 seconds, the program should exit. If they do enter it, their password should be hidden in the terminal.
How do I do this?
Context
I've seen posts like this one that suggest methods to input some user input with a timeout. However, these solutions don't hide input like getpass does. I'm wondering if there's some functionality that could use getpass or something like it.

Inspired by this (which only seems to only work on Unix systems), you can timeout a function call by signal.alarm()
import getpass
import signal
def timeout(signum, frame):
print('Timeout!')
exit()
def get_user_password():
upass = getpass.getpass()
return upass
if __name__ == "__main__":
signal.signal(signal.SIGALRM, timeout)
timeout_in_s = 10
signal.alarm(timeout_in_s)
upass = get_user_password()
signal.alarm(0) # Stop alarm

It's not a perfect solution, but you could try using multiprocessing:
import multiprocessing
import queue
def take_input(q):
stdin = open(0)
print("Enter your input: (y/n) >> ", end="", flush=True)
user_input = stdin.readline()
q.put(user_input)
def confirm_user_input():
value = ''
if __name__ == "__main__":
q = multiprocessing.Queue()
process = multiprocessing.Process(target=take_input, args=(q,))
process.start()
try:
value = q.get(timeout=10)
except queue.Empty:
print("no input...")
process.terminate()
process.join()
if value.strip() == 'y':
print("confirmed: do something in this case...")
else:
print("not confirmed: do something else in that case...")
confirm_user_input()
This doesn't use input(), but it waits for response for n seconds and if there's no input, continues with something else and it doesn't require Unix system. Keep in mind it needs to be in main. Otherwise you should call it from the main with something like this:
if __name__ == '__main__':
freeze_support()
confirm_user_input()
In that case remove if __name__ == "__main__": from the function.
May be you could implement it in your code.

Related

If two multiprocessing can request input on the terminal, is there a way to pause one of them until the answer is given?

As can be seen in the code below, two multiprocessing runs together, but both have a moment that can ask for an input() in the Terminal, is there any way to pause the other multiprocessing until the answer is given in the Terminal?
File Code_One archaic and simple example to speed up the explanation:
from time import sleep
def main():
sleep(1)
print('run')
sleep(1)
print('run')
sleep(1)
input('Please, give the number:')
File Code_Two archaic and simple example to speed up the explanation:
from time import sleep
def main():
sleep(2)
input('Please, give the number:')
sleep(1)
print('run 2')
sleep(1)
print('run 2')
sleep(1)
print('run 2')
sleep(1)
print('run 2')
sleep(1)
print('run 2')
File Main_Code:
import Code_One
import Code_Two
import multiprocessing
from time import sleep
def main():
while True:
pression = multiprocessing.Process(target=Code_One.main)
xgoals = multiprocessing.Process(target=Code_Two.main)
pression.start()
xgoals.start()
pression.join()
xgoals.join()
print('Done')
sleep(5)
if __name__ == '__main__':
main()
How should I proceed in this situation?
In this example, as it doesn't pause the other multi, whenever it asks for an input this error happens:
input('Please, give the number:')
EOFError: EOF when reading a line
Sure, this is possible. To do it you will need to use some sort of interprocess communication (IPC) mechanism to allow the two processes to coordinate. time.sleep is not the best option though, and there are much more efficient ways of tackling it that are specifically made just for this problem.
Probably the most efficient way is to use a multiprocessing.Event, like this:
import multiprocessing
import sys
import os
def Code_One(event, fno):
proc_name = multiprocessing.current_process().name
print(f'running {proc_name}')
sys.stdin = os.fdopen(fno)
val = input('give proc 1 input: ')
print(f'proc 1 got input: {val}')
event.set()
def Code_Two(event, fno):
proc_name = multiprocessing.current_process().name
print(f'running {proc_name} and waiting...')
event.wait()
sys.stdin = os.fdopen(fno)
val = input('give proc 2 input: ')
print(f'proc 2 got input {val}')
if __name__ == '__main__':
event = multiprocessing.Event()
pression = multiprocessing.Process(name='code_one', target=Code_One, args=(event, sys.stdin.fileno()))
xgoals = multiprocessing.Process(name='code_two', target=Code_Two, args=(event, sys.stdin.fileno()))
xgoals.start()
pression.start()
xgoals.join()
pression.join()
This creates the event object, and the two subprocesses. Event objects have an internal flag that starts out False, and can then be toggled True by any process calling event.set(). If a process calls event.wait() while the flag is False, that process will block until another process calls event.set().
The event is created in the parent process, and passed to each subprocess as an argument. Code_Two begins and calls event.wait(), which blocks until the internal flag in the event is set to True. Code_One executes immediately and then calls event.set(), which sets event's internal flag to True, and allows Code_Two to proceed. At that point both processes have returned and called join, and the program ends.
This is a little hacky because it is also passing the stdin file number from the parent to the child processes. That is necessary because when subprocesses are forked, those file descriptors are closed, so for a child process to read stdin using input it first needs to open the correct input stream (that is what sys.stdin = os.fdopen(fno) is doing). It won't work to just send sys.stdin to the child as another argument, because of the mechanics that Python uses to set up the environment for forked processes (sys.stdin is a IO wrapper object and is not pickleable).

How do I get an input function to work whilst another code is running (using multiprocessing)?

I can't get this code to run an input whilst another block of code is running. I want to know if there are any workarounds, my code is as follows.
import multiprocessing
def test1():
input('hello')
def test2():
a=True
while a == True:
b = 5
if __name__ == "__main__":
p1 = multiprocessing.Process(target=test1)
p2 = multiprocessing.Process(target=test2)
p1.start()
p2.start()
p1.join()
p2.join()
When the code is run I get an EOF error which apparently happens when the input function is interrupted.
I would have the main process create a daemon thread responsible for doing the input in conjunction with creating the greatly under-utilized full duplex Pipe which provides two two-way communication Connection instances. For simplicity the following demo just creates one Process instance that loops doing input requests echoing the response until the user enters 'quit':
import multiprocessing
import threading
def test1(conn):
while True:
conn.send('Please enter a value: ')
s = conn.recv()
if s == 'quit':
break
print(f'You entered: "{s}"')
def inputter(conn):
while True:
# The contents of the request is the prompt to be used:
prompt = conn.recv()
conn.send(input(prompt))
if __name__ == "__main__":
conn1, conn2 = multiprocessing.Pipe(duplex=True)
t = threading.Thread(target=inputter, args=(conn1,), daemon=True)
p = multiprocessing.Process(target=test1, args=(conn2,))
t.start()
p.start()
p.join()
That's not all of your code, because it doesn't show the multiprocessing. However, the issue is that only the main process can interact with the console. The other processes do not have a stdin. You can use a Queue to communicate with the main process if you need to, but in general you want the secondary processes to be pretty much standalone.

Python: How to resume the python script as soon as vpn network is up?

I have a python script (xyz.py) that I run through the command prompt. My question is that don't we have any method which helps to resume the python code automatically from where it was lost the VPN connection, without any manual intervention. This will help to avoid monitoring the code frequently. Below is my code but it reads from the start if there is any disconnection. Please suggest.
filename = 'xyz.py'
while True:
p = subprocess.Popen('python '+filename, shell=True).wait()
""" #if your there is an error from running 'xyz.py',
the while loop will be repeated,
otherwise the program will break from the loop"""
if p != 0:
continue
else:
break
If me, time.sleep will be used:
import os
import time
from datetime import datetime
import requests
script = 'xyz.py'
def main():
network_check_url = 'http://8.8.8.8'
while True:
try:
requests.get(network_check_url)
except Exception as e:
print(datetime.now(), e)
time.sleep(1)
else:
print('Network is ok. {datetime.now():%Y-%m-%d_%H:%M:%S}')
os.system(f'python {script}')
return
if __name__ == '__main__':
main()

How can I make the print() function not take the text in the input field with it

I am trying to make a console for my python applications, but i ran into a problem:
when printing something using the print() function, the text in the input field is also included. This is purely visual, because the program still works.
I tried searching online, but I do not even now what to search for and had no luck.
This is my code. It prints "foo" until the user types "exit":
import multiprocessing as mp
import os
import time
def f(q):
while True:
print(q)
time.sleep(1)
if __name__=="__main__":
p=mp.Process(target=f, args=("foo",))
p.start()
while True:
comm=str(input())
if comm=="exit":
p.terminate()
break
When the program is running, the user can still type, but when the program prints something, it also takes whatever is in the input field at the time:
foo
foo
foo
foo
efoo
xfoo
itfoo
When pressing "enter", the program still registers the input correctly and exits the program.
Here is a modification of your code that only prints foo after you have finished your input typing (i.e., until you hit Enter):
import multiprocessing as mp
from multiprocessing import Queue
def f(q, queue):
while True:
queue.get()
print(q)
if __name__=="__main__":
queue = Queue()
p=mp.Process(target=f, args=("foo", queue))
p.start()
while True:
comm=str(input())
queue.put(None)
if comm=="exit":
p.terminate()
break
If terminating the process is all you want your user to be able to do, then you can instruct them to enter Ctrl+C if they wish to stop the operation and then catch the KeyboardInterrupt exception that comes along with it.
import multiprocessing as mp
import os
import time
def f(q):
while True:
print(q)
time.sleep(1)
if __name__=="__main__":
p=mp.Process(target=f, args=("foo",))
print("Process starting. Use Ctrl+c anytime to stop it!")
p.start()
try:
while True:
input() # Trash command
except KeyboardInterrupt:
print("Terminating process...")
p.terminate()
print("Process terminated...")
If you want to do more complicated commands then a GUI would be your best approach (as mentioned by John)

Stop an infinite while loop repeatedly invoking os.system

Thank you guys for seeing my post.
First, the following is my code:
import os
print("You can create your own message for alarm.")
user_message = input(">> ")
print("\n<< Sample alarm sound >>")
for time in range(0, 3):
os.system('say ' + user_message) # this code makes sound.
print("\nOkay, The alarm has been set.")
"""
##### My problem is here #####
##### THIS IS NOT STOPPED #####
while True:
try:
os.system('say ' + user_message)
except KeyboardInterrupt:
print("Alarm stopped")
exit(0)
"""
My problem is that Ctrl + C does not work!
I tried changing position of try block, and making signal(SIGINT) catching function.
But those also does not work.
I have seen https://stackoverflow.com/a/8335212/5247212, https://stackoverflow.com/a/32923070/5247212, and other several answers about this problem.
I am using MAC OS(10.12.3) and python 3.5.2.
This is expected behaviour, as os.system() is a thin wrapper around the C function system(). As noted in the man page, the parent process ignores SIGINT during the execution of the command. In order to exit the loop, you have to manually check the exit code of the child process (this is also mentioned in the man page):
import os
import signal
while True:
code = os.system('sleep 1000')
if code == signal.SIGINT:
print('Awakened')
break
However, the preferred (and more pythonic) way to achieve the same result is to use the subprocess module:
import subprocess
while True:
try:
subprocess.run(('sleep', '1000'))
except KeyboardInterrupt:
print('Awakened')
break
Your code would then look like something like this:
import subprocess
print("You can create your own message for alarm.")
user_message = input(">> ")
print("\n<< Sample alarm sound >>")
for time in range(0, 3):
subprocess.run(['say', user_message]) # this code makes sound.
print("\nOkay, The alarm has been set.")
while True:
try:
subprocess.run(['say', user_message])
except KeyBoardInterrupt:
print("Alarm terminated")
exit(0)
As an added note, subprocess.run() is only available in Python 3.5+. You can use subprocess.call() to achieve the same effect in older versions of Python.
Also catch "SystemExit"
except (KeyboardInterrupt, SystemExit):
print("Alarm stopped")
The problem seems to be that Ctrl+C is captured by the subprocess you call via os.system. This subprocess reacts correspondingly, probably by terminating whatever it is doing. If so, the return value of os.system() will be not zero. You can use that to break the while loop.
Here's an example that works with me (substituting say by sleep):
import os
import sys
while True:
try:
if os.system('sleep 1 '):
raise KeyboardInterrupt
except KeyboardInterrupt:
print("Alarm stopped")
sys.exit(0)
If Ctrl-C is captured by the subprocess, which is the case here, the simplest solution is to check the return value of os.system(). For example in my case it returns value of 2 if Ctrl-C stops it, which is a SIGINT code.
import os
while True:
r = os.system(my_job)
if r == 2:
print('Stopped')
break
elif r != 0:
print('Some other error', r)

Categories