I have a program which has to run a 'watcher' function in the background, which checks if the time specified on an 'alarm' object equals to the current time, and if so, it triggers the alarm. This is the function:
def watcher(this):
while this.run:
this.lock.acquire(True)
for i , a in enumerate(this.alarms):
if a.activate:
if a.repeat:
if a.dateTime.minute + (a.dateTime.hour*60) == datetime.datetime.now().minute + (datetime.datetime.now().hour*60):
this.trigger(i)
while True:
if keyboard.is_pressed('a'):
this.stop()
break
elif a.dateTime.date() == datetime.datetime.now().date() and a.dateTime.minute + a.dateTime.hour*60 == datetime.datetime.now().minute + datetime.datetime.now().hour*60:
this.trigger(i)
while True:
if keyboard.is_pressed('a'):
this.stop()
break
this.lock.release()
The lock object is created previously inside the class this function is in.
It works when i run it on a program where i set the alarms beforehand, but when i have to run another program alongside it that manipules the alarms, the other program hangs and only this one runs.
Here part of the main program:
import alarms
import datetime
import manager
import threading
import keyboard
lock = threading.Lock()
mngr = manager.manager()
print("Program Test")
tr = threading.Thread(target = mngr.watcher)
tr.start()
while True:
command = input()
if len(command) == 0:
print('')
elif command.split()[0] == "rem":
lock.acquire(True)
try:
mngr.remove_alarm(int(command.split()[1]) - 1)
except IndexError as excp:
print('Invalid alarm number. Use the command "list" to get alarms')
except Exception as excp:
print('Wrong usage of command: use like this: rem {alarmNumber}')
print('DEBUG: ' + str(excp))
finally:
lock.release()
Am i doing the synchronization wrong? Upon even instancing the Thread on the main program it halts execution.
Related
I need to design a script that uses the top portion of the terminal as output where some lines are printed after each second in an infinite loop, and the bottom portion keeps taking user input and also printing them in the above portion (among the regular periodic outputs).
In other words, I need to design a sort of shell.
I tried multithreading with the naive approach like this:
#!/usr/bin/python3
from math import acos
from threading import Thread
from random import choice
from time import sleep
from queue import Queue, Empty
commandQueue = Queue()
def outputThreadFunc():
outputs = ["So this is another output","Yet another output","Is this even working"] # Just for demo
while True:
print(choice(outputs))
try:
inp = commandQueue.get(timeout=0.1)
if inp == 'exit':
return
else:
print(inp)
except Empty:
pass
sleep(1)
def inputThreadFunc():
while True:
command = input("> ") # The shell
if command == 'exit':
return
commandQueue.put(command)
# MAIN CODE
outputThread = Thread(target=outputThreadFunc)
inputThread = Thread(target=inputThreadFunc)
outputThread.start()
inputThread.start()
outputThread.join()
inputThread.join()
print("Exit")
But as obviously expected, the output lines merge with the input lines as the user keeps typing.
Any ideas?
As discussed in comments, used curses library.
Update
used two subwin for input and output
#!/usr/bin/python3
import curses
from math import acos
from threading import Thread
from random import choice
from time import sleep
from queue import Queue, Empty
commandQueue = Queue()
stdscr = curses.initscr()
stdscr.keypad(True)
upperwin = stdscr.subwin(2, 80, 0, 0)
lowerwin = stdscr.subwin(2,0)
def outputThreadFunc():
outputs = ["So this is another output","Yet another output","Is this even working"] # Just for demo
while True:
upperwin.clear()
upperwin.addstr(f"{choice(outputs)}")
try:
inp = commandQueue.get(timeout=0.1)
if inp == 'exit':
return
else:
upperwin.addch('\n')
upperwin.addstr(inp)
except Empty:
pass
upperwin.refresh()
sleep(1)
def inputThreadFunc():
while True:
global buffer
lowerwin.addstr("->")
command = lowerwin.getstr()
if command:
command = command.decode("utf-8")
commandQueue.put(command)
lowerwin.clear()
lowerwin.refresh()
if command == 'exit':
return
# MAIN CODE
outputThread = Thread(target=outputThreadFunc)
inputThread = Thread(target=inputThreadFunc)
outputThread.start()
inputThread.start()
outputThread.join()
inputThread.join()
stdscr.keypad(False)
curses.endwin()
print("Exit")
Old Solution
I've edited your example to use getch insted of input
#!/usr/bin/python3
import curses
import datetime
from math import acos
from threading import Thread
from random import choice
from time import sleep
from queue import Queue, Empty
INFO_REFRESH_SECONDS = 1
commandQueue = Queue()
buffer = list() # stores your input buffer
stdscr = curses.initscr()
stdscr.keypad(True)
def outputThreadFunc():
outputs = ["So this is another output","Yet another output","Is this even working"] # Just for demo
info = choice(outputs), datetime.datetime.now()
while True:
if datetime.datetime.now() - info[1] > datetime.timedelta(seconds=INFO_REFRESH_SECONDS):
# refresh info after certain period of time
info = choice(outputs), datetime.datetime.now() # timestamp which info was updated
inp = ''
buffer_text = ''.join(buffer)
try:
command = commandQueue.get(timeout=0.1)
if command == 'exit':
return
inp = f"\n{command}"
except Empty:
pass
output_string = f"{info[0]}{inp}\n->{buffer_text}"
stdscr.clear()
stdscr.addstr(output_string)
stdscr.refresh()
if inp:
# to make sure you see the command
sleep(1)
def inputThreadFunc():
while True:
global buffer
# get one character at a time
key = stdscr.getch()
curses.echo()
if chr(key) == '\n':
command = ''.join(buffer)
commandQueue.put(command)
if command == 'exit':
return
buffer = []
elif key == curses.KEY_BACKSPACE:
if buffer:
buffer.pop()
else:
buffer.append(chr(key))
# MAIN CODE
outputThread = Thread(target=outputThreadFunc)
inputThread = Thread(target=inputThreadFunc)
outputThread.start()
inputThread.start()
outputThread.join()
inputThread.join()
stdscr.keypad(False)
curses.endwin()
print("Exit")
The simplest solution is to use two scripts; One, a server that prints the output, and the other, a client that sends the user's input to the server. Then you can use a standard solution like tmux to open the two scripts in two panes.
The two are merging because of the way the terminal writes to the output. It collects outputs in a buffer, and when the time is right it outputs everything at once. An easy fix would be to use a '\n' before each actual statement so that each new output is on a separate line.
#!/usr/bin/python3
.
.
.
if inp == 'exit':
return
else:
print("\n", inp) # CHANGE OVER HERE
.
.
.
command = input("\n> ") # CHANGE OVER HERE
if command == 'exit':
return
.
.
.
print("Exit")
Beware that since two threads are running in parallel, the next output will print before you are done typing and pressing enter to the input (unless you can type really fast and have really fast reflexes). Hope this answers your question!
I'm making a small program that can do some small tasks for me with multiple threads. Currently, I have a small menu loop that asks me what task I want to run, it then asks me for the information it needs to complete the task, and sets up a specified number of threads to complete the task through a queue.
Currently, my menu loop code looks like this and is run when the program starts:
# menu loop
def menu_loop():
while True:
choice = menu()
if choice is not None:
break
# configure program
if choice == 1:
while True:
config_choice = configure() # another menu loop
if config_choice is not None:
if config_choice == 0:
clear_screen()
print(f"[*] {red}exiting config...\n")
menu_loop()
elif config_choice == 1:
num_of_threads = int(input("number of threads: "))
clear_screen()
print(f"[*] {green}number of threads set to {num_of_threads}.\n")
menu_loop()
elif config_choice == 2:
folder_path = input("folder path for results: ")
clear_screen()
print(f"[*] {green}path to results set to {folder_path}.\n")
menu_loop()
break
if choice == 2:
strings = [line.strip() for line in open((input("path to string file: ")), "r")] # load strings
for string in strings:
q.put(string) # insert each string into queue
print(f"{green}loaded {len(strings)} strings into the checker.") # log
with open(string_results_ml, "a+") as sr_multi_line:
sr_multi_line.write("------------------------------\n") # first line of the file
for i in range(int(input("number of threads: "))):
Thread(target=check_string, args=(q,)).start() # start threads
The code for the check_string function is as follows:
def check_string(q):
while True:
try:
work = q.get()
if q.empty(): # needed because except doesn't trigger half the time
sleep(1)
quit()
except queue.Empty:
quit()
headers = {"Content-Type": "application/json"}
url = "https://[redacted]/{}".format(work)
try:
r = requests.get(url, headers=headers)
jd = r.json()
if r.status_code == 200:
try:
result = jd["result"]
# write working strings and their data to the results file
with open(string_results, "a+") as s_r:
s_r.write(f"{string} - {result}\n")
with lock:
print(f"[{blue}{r.status_code}{rs}]{green} got result for {string}")
sleep(0.3) # sleep for a bit, time out
except:
print(f"[{blue}{r.status_code}{rs}]{red} something happened while parsing the data.")
else:
with lock:
print(f"[{blue}{r.status_code}{rs}]{red} no results for {string}")
except:
with lock:
print(f"{red}fatal error while checking {string}! the string has been written to a separate file for you to check later.")
with open(fatal, "a+") as f_e:
f_e.write(string + "\n")
q.task_done()
Right now, what happens after all the strings are done being checked is that the program just hangs and doesn't go back to the menu loop. There is no code for it to go back to the loop currently but it should at least quit after all strings are done being checked. It just hangs currently. When I press CTRL-C, it gives me the following exception:
^CException ignored in: <module 'threading' from '/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py'>
Traceback (most recent call last):
File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 1307, in _shutdown
lock.acquire()
KeyboardInterrupt
How can I make the program go back to the main menu loop after all strings are done being checked instead of just hanging?
Your threads are stopping because the queue is empty and they are waiting for new item to be queued.
By default, queue.get() blocks if the queue is empty. In your code, check the queue before q.get(). In addition, change q.get() to q.get(false) to prevent blocking (an error will be thrown if empty queue).
while True:
try:
if q.empty(): quit() # exit thread if no items left - add this line
work = q.get(false) # don't block to be safe - update this line
if q.empty(): # needed because except doesn't trigger half the time
time.sleep(1)
quit()
except queue.Empty:
quit()
How can I run a timer while asking for user input from the console? I was reading about multiprocessing, and I tried to use this answer: Python: Executing multiple functions simultaneously. When I tried to get it going, it gave me a bunch of framework errors.
Right now it runs start_timer(), but then stops it when it runs cut_wire().
Here's my start_timer function:
def start_timer():
global timer
timer = 10
while timer > 0:
time.sleep(1)
timer -= 1
sys.stdout.write ("There's only %i seconds left. Good luck. \r" % (timer))
sys.stdout.flush()
cut_wire()
if timer == 0:
print("Boom!")
sys.exit()
and this is the cut_wire function:
def cut_wire():
wire_choice = raw_input("\n> ")
if wire_choice == "cut wire" or wire_choice == "Cut Wire":
stop_timer()
else:
print("Boom!")
sys.exit()
Of course it stops running when it plays the cut_wire function because "raw_input" command reads the text and wait for the user to put the text and press enter.
My suggestion is to check for they key press "Enter" and when then key was press, read the line. If the key wasn't press, just continue with your timer.
Regards.
Instead of using raw_input() use this function taken from here.
def readInput( caption, timeout = 1):
start_time = time.time()
sys.stdout.write('\n%s:'%(caption));
input = ''
while True:
if msvcrt.kbhit():
chr = msvcrt.getche()
if ord(chr) == 13: # enter_key
break
elif ord(chr) >= 32: #space_char
input += chr
if len(input) == 0 and (time.time() - start_time) > timeout:
break
print '' # needed to move to next line
if len(input) > 0:
return input
else:
return ""
Thearding option
To make sure that both functions run completely simultaneously you can use this example of threading event:
import threading
event = threading.Event()
th = theading.Thread(target=start_timer, args=(event, ))
th1 = theading.Thread(target=cut_wire, args=(event, ))
th.start()
th1.start()
th.join()
th1.join()
In your function you can set an event using event.set(), check it using event.is_set() and clear it using event.clear().
Only addressing your concerns, here is a quick fix using threading :
import time
import sys
import os
def start_timer():
global timer
timer = 10
while timer > 0:
time.sleep(1)
timer -= 1
sys.stdout.write ("There's only %i seconds left. Good luck. \r" % (timer))
sys.stdout.flush()
#cut_wire() ==> separate call is easier to handle
if timer == 0:
print("Boom!")
os._exit(0) #sys.exit() only exits thread
def cut_wire():
wire_choice = raw_input("\n> ")
if wire_choice == "cut wire" or wire_choice == "Cut Wire":
stop_timer()
else:
print("Boom!")
os._exit(0) #same reason
if __name__ == '__main__':
import threading
looper = threading.Thread(target=start_timer)
looper.start()
cut_wire()
I tried:
from time import sleep
while sleep(3):
input("Press enter to continue.")
But it doesn't seem to work. I want the program to await user input, but if there's no user input after 10 minutes, continue with the program anyway.
This is with python 3.
Why does the code not work?
time.sleep returns nothing; the value of the time.sleep(..) become None; while loop body is not executed.
How to solve it
If you're on Unix, you can use select.select.
import select
import sys
print('Press enter to continue.', end='', flush=True)
r, w, x = select.select([sys.stdin], [], [], 600)
Otherwise, you should use thread.
Windows specific solution that use msvcrt:
import msvcrt
import time
t0 = time.time()
while time.time() - t0 < 600:
if msvcrt.kbhit():
if msvcrt.getch() == '\r': # not '\n'
break
time.sleep(0.1)
Here is a simple way using an alarm. When the time expires, the lambda function is called, which would raise a ZeroDivisionError.
from signal import signal, alarm, SIGALRM
signal(SIGALRM, lambda x, y: 1/0)
try:
alarm(600)
input("Press enter to continue.")
except ZeroDivisionError:
print("timed out")
Another way to do it is this:
from time import sleep
print("I'm executing")
try:
print("Wake up Ctrl-C!")
sleep(600)
except KeyboardInterrupt:
print("I woke up!")
else:
print("I'm executing again.")
Not the greatest of answers and it definitely feels like an abuse of exceptions, but it works.
I am currently generating a python-script with the fabric framwork that is supposed to collect a backup from a remote server and store it locally on the client running fabric.
Now, since the backup file is >400MB, it takes quite some time to transfer it. And here is where my question bumps in:
Is there any kind of progressbars for the fabric get()-function? Or rather, is it possible to add a progressbar somehow?
Here's a piece of my code:
def collect_backup():
env.warn_only=True
run('uptime')
print "Copying scrips to be run..."
filename, remotepath = _getlatest()
print "Copy complete."
print "Collecting backup..."
localpath = _collect(filename, remotepath)
def _collect(filename, remotepath):
a=remotepath + filename
localpath="/home/bcns/backups/"
####Here's the get() I was talking about
get(a, localpath)
return(localpath)
The "filename" and "remotepath" variables are set in another function.
There is a lot of great info at the following site:
http://thelivingpearl.com/2012/12/31/creating-progress-bars-with-python/
Here is their solution for a console prog bar with threading:
import sys
import time
import threading
class progress_bar_loading(threading.Thread):
def run(self):
global stop
global kill
print 'Loading.... ',
sys.stdout.flush()
i = 0
while stop != True:
if (i%4) == 0:
sys.stdout.write('\b/')
elif (i%4) == 1:
sys.stdout.write('\b-')
elif (i%4) == 2:
sys.stdout.write('\b\\')
elif (i%4) == 3:
sys.stdout.write('\b|')
sys.stdout.flush()
time.sleep(0.2)
i+=1
if kill == True:
print '\b\b\b\b ABORT!',
else:
print '\b\b done!',
kill = False
stop = False
p = progress_bar_loading()
p.start()
try:
#anything you want to run.
time.sleep(1)
stop = True
except KeyboardInterrupt or EOFError:
kill = True
stop = True
Hope that helps or at least gets you started.