Graceful exiting of a program in Python? - python

I have a script that runs as a
while True:
doStuff()
What is the best way to communicate with this script if I need to stop it but I don't want to kill it if it is in the middle of an operation?

And I'm assuming you mean killing from outside the python script.
The way I've found easiest is
#atexit.register
def cleanup()
sys.unlink("myfile.%d" % os.getpid() )
f = open("myfile.%d" % os.getpid(), "w" )
f.write("Nothing")
f.close()
while os.path.exists("myfile.%d" % os.getpid() ):
doSomething()
Then to terminate the script just remove the myfile.xxx and the application should quit for you. You can use this even with multiple instances of the same script running at once if you only need to shut one down. And it tries to clean up after itself....

The best way is to rewrite the script so it doesn't use while True:.
Sadly, it's impossible to conjecture a good way to terminate this.
You could use the Linux signals.
You could use a timer and stop after a while.
You could have dostuff return a value and stop if the value is False.
You could check for a local file and stop if the file exists.
You could check an FTP site for a remote file and stop of the file exists.
You could check an HTTP web page for information that indicates if your loop should stop or not stop.
You could use OS-specific things like semaphores or shared memory.

I think the most elegant would be:
keep_running = true
while keep_running:
dostufF()
and then dostuff() can set keep_running = false whenever in no longer wants to keep running, then the while loop ends, and everything cleans up nicely.

If that's a console aplication and exiting by pressing Ctrl+C is ok, could that solve your problem?
try:
while True:
doStuff()
except KeyboardInterrupt:
doOtherStuff()
I guess the problem with that approach is that you wouldn't have any control exactly when and where in doStuff the execution is terminated.

Long time ago I've implemented such a thing. It catches Ctrl+C (or keyboard interrupt). It uses my package snuff-utils.
To install:
pip install snuff-utils
from snuff_utils.graceful_exit import graceful_exit
while True:
do_task_until_complete()
if graceful_exit:
do_stuff_before_exit()
break
On Ctrl+C it will log:
An interrupt signal has been received. The signal will be processed according to the logic of the application.
The goal I was after is to exit program but only after finishing already running task.
Be careful with multiprocessing/multithreading. It is not tested.

The signal module can trap signals and react accordingly?

Related

using quit(), exit(), ETC. says "The program is still running, do you want to kill it?"

While making an HTML help tool in Python, I wanted to exit the program smoothly, like clicking the X button on Google Chrome. But, I encountered an issue. It asks me if I want to kill the program, instead of doing it automatically.
I tried using quit(), exit() and sys.exit(). All do the same thing. How can I get the program to exit smoothly?
As it was suggested in the comments, your problem should only be noticed inside Python's IDLE, but should run just fine when executed inside a terminal. However, this code should also kill your program in IDLE:
import os, signal
from time import sleep
print("I will sleep for 3 secs and shut down!")
sleep(3)
os.kill(os.getpid(), signal.SIGTERM)
This sends a signal to your application to terminate.
Or alternatively you could call os' _exit function.
From the docs:
Exit the process with status n, without calling cleanup handlers, flushing stdio buffers, etc.
I know it's late but I took a different way.
It might be a dirty way of doing it. I don't like the accepted
answer because you have to hardcode those lines in every script.
I just comment out the lines, so that the dialog never shows on screen.
You can find the file /usr/lib/python3.9/idlelib/pyshell.py
aprx: line 1007
def close(self):
"Extend EditorWindow.close()"
#if self.executing:
#response = messagebox.askokcancel(
#"Kill?",
#"Your program is still running!\n Do you want to kill it?",
#default="ok",
#parent=self.text)
#if response is False:
#return "cancel"
self.stop_readline()
self.canceled = True
self.closing = True
return EditorWindow.close(self)
That way it will never ask you this silly question "Your program is still running!\n Do you want to kill it?"

How can I terminate a Python script manually without interrupting file output?

I am somewhat new to Python, so I imagine this question has a simple answer. But I cannot seem to find a solution anywhere.
I have a Python script that continually accepts input from a streaming API and saves the data out to a file.
My problem when I need to stop the script to modify the code. If I use ctrl-f2, I sometime catch the script while it is in the process of writing to the output file, and the file ends up corrupted.
Is there a simple way to stop Python manually that allows it to finish executing the current line of code?
You can catch the SIGTERM or SIGINT signal and set a global variable that your script routinely checks to see if it should exit. It may mean you need to break your operations up into smaller chunks so that you can check the exit variable more frequently
import signal
EXIT = False
def handler(signum, frame):
global EXIT
EXIT = True
signal.signal(signal.SIGINT, handler)
def long_running_operation():
for i in range(1000000):
if EXIT:
# do cleanup or raise exception so that cleanup
# can be done higher up.
return
# Normal operation.

Python ctrl+c, excute function if early termination

I have a python script that creates a lot of temporary files. If the script terminates early because of a ctrl+c interrupt, I would like to quickly delete those files before the program is allowed to end.
What's the pythonic way handling this?
Open the files in a with statement, if possible, or use a try statement with a finally block that closes the files. If you're using tempfile, the files will automatically be destroyed when closed; otherwise, you may need to delete them yourself in the finally block.
http://docs.python.org/2/library/exceptions.html#exceptions.KeyboardInterrupt
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
cleanUp()
Either catch and handle KeyboardInterrupt, or set an exit handler with atexit.
Also, tempfile.

Python threads with os.system() calls. Main thread doesn't exit on ctrl+c

Please don't consider it a duplicate before reading, There are a lot of questions about multithreading and keyboard interrupt, but i didn't find any considering os.system and it looks like it's important.
I have a python script which makes some external calls in worker threads.
I want it to exit if I press ctrl+c But it look like the main thread ignores it.
Something like this:
from threading import Thread
import sys
import os
def run(i):
while True:
os.system("sleep 10")
print i
def main():
threads=[]
try:
for i in range(0, 3):
threads.append(Thread(target=run, args=(i,)))
threads[i].daemon=True
threads[i].start()
for i in range(0, 3):
while True:
threads[i].join(10)
if not threads[i].isAlive():
break
except(KeyboardInterrupt, SystemExit):
sys.exit("Interrupted by ctrl+c\n")
if __name__ == '__main__':
main()
Surprisingly, it works fine if I change os.system("sleep 10") to time.sleep(10).
I'm not sure what operating system and shell you are using. I describe Mac OS X and Linux with zsh (bash/sh should act similar).
When you hit Ctrl+C, all programs running in the foreground in your current terminal receive the signal SIGINT. In your case it's your main python process and all processes spawned by os.system.
Processes spawned by os.system then terminate their execution. Usually when python script receives SIGINT, it raises KeyboardInterrupt exception, but your main process ignores SIGINT, because of os.system(). Python os.system() calls the Standard C function system(), that makes calling process ignore SIGINT (man Linux / man Mac OS X).
So neither of your python threads receives SIGINT, it's only children processes who get it.
When you remove os.system() call, your python process stops ignoring SIGINT, and you get KeyboardInterrupt.
You can replace os.system("sleep 10") with subprocess.call(["sleep", "10"]). subprocess.call() doesn't make your process ignore SIGINT.
I've had this same problem more times than I could count back when i was first learning python multithreading.
Adding the sleep call within the loop makes your main thread block, which will allow it to still hear and honor exceptions. What you want to do is utilize the Event class to set an event in your child threads that will serve as an exit flag to break execution upon. You can set this flag in your KeyboardInterrupt exception, just put the except clause for that in your main thread.
I'm not entirely certain what is going on with the different behaviors between the python specific sleep and the os called one, but the remedy I am offering should work for what your desired end result is. Just offering a guess, the os called one probably blocks the interpreter itself in a different way?
Keep in mind that generally in most situations where threads are required the main thread is going to keep executing something, in which case the "sleeping" in your simple example would be implied.
http://docs.python.org/2/library/threading.html#event-objects

Pausing a process?

Is there a way to pause a process (running from an executable) so that it stops the cpu load while it's paused, and waits till it's unpaused to go on with its work? Possibly in python, or in some way accessible by python.
By using psutil ( https://github.com/giampaolo/psutil ):
>>> import psutil
>>> somepid = 1023
>>> p = psutil.Process(somepid)
>>> p.suspend()
>>> p.resume()
you are thinking of SIGTSTP -- the same signal that happens when you push CTRL-Z. This suspends the process until it gets SIGCONT.
of course, some programs can just catch and ignore this signal, so it depends on the executable. however, if you can suspend and resume it manually, you can do it from a python program, too. use os.kill()
I just implemented this with signals in python something like this:
def mysignalhandler(sig, frame):
print "Got " + str(sig)
if sig == signal.SIGUSR1:
do_something()
signal.signal(signal.SIGUSR1, mysignalhandler)
signal.pause()
This will pause at the last line and call do_something() when it receives the signal USR1, for example through a
kill -USR1 <pid>
command.
This will only work in UNIX though.
There is a (almost) native way of doing this in Python, and it's quite simple :
import time
time.sleep(5)
In this snippet, 5 is the number of seconds you want to pause your program.

Categories