I have a problem in managing a infinite while loop in Python, in which I would insert a timer as a sort of "watchdog". I try to explain better: the script has to listen on a serial channel and wait for messages coming from sensors connected on the other side of the channel.
I do this with a while True loop because the script must catch all the signals that are passing. However, even if it's probably unnecessary, i would like to insert a timer which is always reset every loop. So, if (for example) the loop get stuck for some reason, the timer will end and will exit the program. I thought i could do this in this way:
def periodicUpdate():
exitTimer = threading.Timer(60.0, sys.exit())
while True:
exitTimer.start()
readData()
exitTimer.cancel()
Now, the problem is that when i start the script it immediatly exit. It seems that it reads sys.exit() before all the rest, and it doesn't respect the construct of the timer, but simply when periodicUpdate is called it exits. Why this happen?! I tried changing the syntax, putting sys.exit in an other function and call that function, i tried other solutions but always it behave in two ways: it exits or it behave as the timer doesn't exists. Can someone help me? Thanks very much
Remove the parentheses. If you use sys.exit(), it will fire straight away. When you use sys.exit, it will pass the function as Timer's second argument.
I think your approach is not the best one, instead of running a timer that exits the application, why not simply timeout the function that reads the data:
Timeout on a function call
It seems that it reads sys.exit() before all the rest
Sure it does. The second argument of threadint.Timer is a function that will be called. What you do is you actually call sys.exit and supply its return value (which is undefined, by the way, since the call terminates the program) as a function to be called. That's an error, since sys.exit's return value is not a function, but that doesn't matter, as the program is terminated anyway.
What you want to do is
exitTimer = threading.Timer(60.0, sys.exit) # note: no brackets
But that actually will not work, as sys.exit in the thread will terminate only that thread. In short, you can't use a watchdog to terminate a stuck while-loop if this loops is in your main thread. In your particular situation you should investigate the function you are using to read data, most likely it lets you set a read timeout.
It seems to me that none of the solutions proposed here actually works. This is an example of code. Would be great to know how to fix it. Also, please try it before answering.
import time, threading, sys
def _exit():
print 'exiting'
sys.exit()
def periodicUpdate():
exitTimer = threading.Timer(2.0, _exit)
while True:
exitTimer.start()
print 'go'
time.sleep(5)
exitTimer.cancel()
periodicUpdate()
The actual output is:
$python test.py
go
exiting
RuntimeError: threads can only be started once
exitTimer = threading.Timer(60.0, sys.exit)
Remove the () from sys.exit in timer call
def _exit()
sys.exit()
def periodicUpdate():
exitTimer = threading.Timer(60.0, _exit)
while True:
exitTimer.start()
readData()
exitTimer.cancel()
In you put the () the interpreter call this function if not the interpreter get a reference for this function.
Have fun :)
Edit: Use of _exit
threading.Timer(60.0, os._exit, [0])
def periodicUpdate():
exitTimer = sys.exit(60.0, sys.exit())
while sys.exit():
sys.exit()
readData()
sys.exit()
if __name__ == "__main__":
sys.exit()
Don't forget to import sys.
Related
I need to break from time.sleep() using ctrl c.
While 1:
time.sleep(60)
In the above code when the control enters time.sleep function an entire 60 seconds needs to elapsed for python to handled the CTRL C
Is there any elegant way to do it. such that I can interrupt even when the control is in time.sleep function
edit
I was testing it on a legacy implementation which uses python 2.2 on windows 2000 which caused all the trouble . If I had used a higher version of python CTRL C would have interrupted the sleep() . I did a quick hack by calling sleep(1) inside a for loop . which temporarily fixed my issue
The correct answer is to use python stdlib's threading.Event
Sure you can tune down your sleep interval so you sleep for very short periods, but what if you actually want to run your loop once every 60s? Then you need to do more work to determine if it's time to run or just keep sleeping. Furthermore, you're still technically blocking but for only a short period of time. Contrast to threading.Event:
from threading import Event
exit = Event()
def main():
while not exit.is_set():
do_my_thing()
exit.wait(60)
print("All done!")
# perform any cleanup here
def quit(signo, _frame):
print("Interrupted by %d, shutting down" % signo)
exit.set()
if __name__ == '__main__':
import signal
for sig in ('TERM', 'HUP', 'INT'):
signal.signal(getattr(signal, 'SIG'+sig), quit);
main()
When the signal handler calls exit.set(), the main thread's wait() call will immediately be interrupted.
Now, you could use an Event to signal that there's more work to do, etc. But in this case it does double duty as a convenient indicator that we want to quit (e.g. the while not exit.is_set() part.)
You also have the option to put any cleanup code after your while loop.
Not sure what the sense of this code is - but if necessary use a shorter sleep() interval and put a for loop around it:
for i in range(60):
sleep(1)
Catching the KeyboardInterrupt exception using try..except is straight-forward
The KeyboardInterrupt exception is raised when a user hits the interrupt key, Ctrl-C. In python this is translated from a SIGINT signal. That means, you can get handle it however you want using the signal module:
import signal
def handler(signum, frame):
print("do whatever, like call thread.interrupt_main()")
signal.signal(signal.SIGINT, handler)
print("Waiting for SIGINT...")
signal.pause()
That way, you can do whatever you want at the receipt of a keyboard interrupt.
The most elegant solution is certainly threading.Event, though if you only need a quick hack, this code works well :
import time
def main():
print("It’s time !")
if __name__ == "__main__":
print("press ctrl-c to stop")
loop_forever = True
while loop_forever:
main()
try:
time.sleep(60)
except KeyboardInterrupt:
loop_forever = False
I tried your code with python versions 2.5, 2.6, 3 under Linux and all throw "KeyboardInterrupt" exception when hitting CTRL-C.
Maybe some exception handling catches the Interrupt or your problem is like this:
Why is KeyboardInterrupt not working in python?
Based on #Andreas Jung answer
for i in range(360):
try:
sleep(1)
except KeyboardInterrupt:
sys.exit(0)
Figured I'd throw this in.
import time
def sleep(seconds):
for i in range(seconds):
try:
time.sleep(1)
except KeyboardInterrupt:
print("Oh! You have sent a Keyboard Interrupt to me.\nBye, Bye")
break
sleep(60)
I'm making a Discord bot with a lot of commands that take a while to finish (like loops) so I'd like to also have a command that stops any actively running code. I've tried sys.exit but I don't want to have to restart the program each time before it will take another input. Anyone know what I can do?
It will depend on the way your code is formatted, but you will probably want to use functions that utilize boolean or return statements:
def foo():
if end_this:
return
# stuff
If you have some tracking boolean end_this that is set to True, the function foo() will not execute everything below. Alternatively, you could use a while-loop with a break in your code:
def foo():
while True: # runs forever unless ended
# stuff
break
Now, foo() will continue indefinitely until the break statement is reached. An if-statement can enclose the break, setting some logic on when the break occurs. Again, this may require a main() function to handle the calls and ends of your previous functions, but it would allow following code/functions to execute.
I need to say first and foremost, I am just learning Python.
I am making a simple python program that has a menu option for exiting the program by using a function I called exit. I have tried making the exit function just call break, but I am getting an error when the exit function is called.
Any help would be greatly appreciated.
Sorry for not posting code earlier....
def exit():
break
evade = evade_fw()
# Main program running dialogue
def main(): # menu goes here
opt_list = [xsl_file,
basic_loud_scan,
fw_main,
exit
]
Just forget about your own exit() function. You can simply do:
from sys import exit
And the exit() function from sys module will do the job.
It's also worth to know what happens under the hood. Function sys.exit() actually throws a special exception. You can do it as well explicitly and without importing anything:
raise SystemExit()
break is for breaking out of for or while loops, but it must be called from within the loop. I'm guessing that you expect the break to break out of your program's main event loop from an event handler, and that is not going to work because, as aforementioned, the break must be within the loop itself.
Instead your exit function can clean up any resources, e.g. open files, database connections, etc. then call [sys.exit()][1] which will cause the Python interpreter to terminate. You can optionally pass a status code to sys.exit() which will be the system exit status available to shell scripts and batch files.
import sys
def exit():
# clean up resources
sys.exit() # defaults to status 0 == success
I have two programs
program1.py is like commandline interface which takes command from user
program2.py has the program which runs the relevant program as per the command.
Program 1 has also has an quit_program() module
In our simple universe.. lets say I have just one command and just one program
So lets say...
program1.py
def main():
while True:
try:
command = raw_input('> ')
if command == "quit" :
return
if command == '':
continue
except KeyboardInterrupt:
exit()
parseCommand(command)
And then I have:
if commmand == "hi":
say_hi()
Now program2 has
def say_hi():
#do something..
Now there can be two cases...
Either say_hi() completes in which case no issue...
But what i want is that if user enters a command (say: end)
then this say_hi() is terminated in between..
But my current implementation is very sequential.. I mean I dont get to type anything on my terminal untill the execution is completed..
Somethng tells me that the say_hi() should be running on another thread?
I am not able to think straight about this.
Any suggestions?
Thanks
The threading module is what you are looking for.
import threading
t = threading.Thread(target=target_function,name=name,args=(args))
t.daemon = True
t.start()
The .daemon option makes it so you don't have to explicitly kill threads when your app exits... Threads can be quite nasty otherwise
Specific to this question and the question in the comments, the say_hi function can be called in another thread as such:
import threading
if commmand == "hi":
t = threading.Thread(target=say_hi, name='Saying hi') #< Note that I did not actually call the function, but instead sent it as a parameter
t.daemon = True
t.start() #< This actually starts the thread execution in the background
As a side note, you must make sure you are using thread safe functions inside of threads. In the example of saying hi, you would want to use the logging module instead of print()
import logging
logging.info('I am saying hi in a thread-safe manner')
You can read more in the Python Docs.
I've read about this technique to timeout blocking IO operations, the problem is that it doesn't seem to work. for example:
import thread, threading
def read_timeout(prompt, timeout=10.0):
timer = threading.Timer(timeout, thread.interrupt_main)
s = ''
timer.start()
try:
s = raw_input(prompt)
except KeyboardInterrupt:
print 'operation timed out.'
timer.cancel()
return s
s = read_timeout('enter input: ')
if s:
print 'you entered: %s' % s
this won't interrupt the main thread until raw_input() returns.
Any help is appreciated.
Update:
Using os.kill(os.getpid(), signal.SIGINT) instead of thread.interrupt_main() seems to work (at least on Linux, which doesn't give me the portability I initially wanted). However, I'm still wondering why the code above doesn't work.
On Unix machines, there is a way to do what you are trying to do. Look at this post:
raw_input and timeout
Just remove the comma at the end of line 5, or the prompt won't be displayed until the program is terminated.
On the same page there's also solution for Windows OS, but I haven't tested it to know if it's working.
According to the latest doc of the API: https://docs.python.org/3/library/_thread.html#thread.interrupt_main
This does not emit the corresponding signal but schedules a call to the associated handler (if it exists).
And the call is performed after the current blocking call in the main thread.
On the contrary, the os.kill solution mentioned in the update actually emits a signal to the main thread.
Sorry that it's been quite a while since the question was raised, but hopefully it still helps.