SIGINT ignored in google pubsub_v1/python - python

from google.cloud import pubsub_v1
def run():
# created full_* vars here...
future = subscriber.subscribe(full_subscription, print_and_ack_message)
try:
future.result()
except KeyboardInterrupt: # this doesn't work for some reason...
logging.info("Subscription terminated...")
future.cancel()
except BaseException as exc:
logging.info("Other %s", type(exc))
if __name__ == '__main__':
run()
The above code cannot be interrupted on macOS, zsh, iTerm and pyenv-virtualenv with python 2.7.15, for some reason?
CTRL+C fails from the terminal with this code; nothing happens, only ^C is visible in the output and it does neither terminate nor print anything. What is wrong?
I'm following the docs

You don't need to use the result method here:
The subscriber is non-blocking, so we must keep the main thread from exiting to allow it to process messages in the background.
def run():
future = subscriber.subscribe(full_subscription, print_and_ack_message)
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
future.cancel()

Related

How to terminate main program when thread ends? Still getting waiting for process to detach in python?

I am having a main program which is defined like this:
main.py
def main():
try:
registry.start_server()
except:
print("Shutting down the program")
pass
if __name__ == '__main__':
main()
registry.start_server() is the method in another module which looks like this:
def start_server():
t_server = threading.Thread(target=server.start)
t_server.start()
try:
t_server.join()
except KeyboardInterrupt:
print("Error")
raise ValueError
finally:
fp.close()
server.start is the method in another module which does some listening work in a while(True) manner. I am not sure how to stop the whole program when clicking Stop in PyCharm which is Ctrl + C (Signal). I tried with Event but without success. I get to the main.py by raising an exception when the signal gets caught but that does not terminate the whole program. It shows Waiting for program to detach. The only way is to use SIGKILL. I don't understand where does the program keeps hanging? I have also tried calling sys.exit(0) when the signal gets caught and creating the thread as Deamon but that didnt help either.
EDIT
While True method in another module
def start(self, event):
try:
while True:
if event.is_set():
if self.pubsub.channels:
print("It enters here")
message = self.pubsub.get_message(True)
if message:
.
.
.
else:
return
To solve the problem, all you need to do is:
let the child-thread exit, and
let main thread join the child-thread.

handling unexpected shutdown python

is there any way to catch an exception for an unexpected shutdown of program in python ?
let say I am running a python script in a console then I don't press control+c to stop the program but rather just click the close button of the console is there any way to catch the error before the console close?
like this:
try:
print("hello")
except KeyboardInterrupt:
exit()
except UnexpectedClose:
print("unexpected shutoff")
exit()
thanks in advance
Following the link I put in the comment above already and reading here that forced closing is sending a SIGHUP this modified version writes an output file when the terminal window is closed and the python process is "killed".
Note, I just combined information (as cited) available on SE.
import signal
import time
class GracefulKiller:
kill_now = False
def __init__(self):
signal.signal(signal.SIGINT, self.exit_gracefully)
signal.signal(signal.SIGTERM, self.exit_gracefully)
signal.signal(signal.SIGHUP, self.exit_gracefully)
def exit_gracefully(self,signum, frame):
with open('kill.txt', 'w') as fpntr:
fpntr.write('killed')
self.kill_now = True
if __name__ == '__main__':
killer = GracefulKiller()
while True:
time.sleep(1)
print("doing something in a loop ...")
if killer.kill_now:
break
print "End of the program. I was killed gracefully :)"

(Unit) Test python signal handler

I have a simple Python service, where there is a loop that performs some action infinitely. On various signals, sys.exit(0) is called, which causes SystemExit to be raised and then some cleanup should happen if it can.
In a test, i.e. standard unittest.TestCase, I would like to test that this cleanup happens and the loop exits. However, I'm stuck on even getting the signal to be triggered / SystemExit to be raised.
# service.py
import signal
import sys
import time
def main():
def signal_handler(signalnum, _):
# How to get this to block to run in a test?
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
while True:
try:
print("Some action here")
time.sleep(10)
except SystemExit:
# How to get this to block to run in a test?
print("Some cleanup")
break
if __name__ == '__main__':
main()
How can the code enter the SystemExit handler / signal handler in the test environment? An alternative pattern would also be welcome.
You can trigger a SIGINT (or any signal) from another thread after some delay, which is received in the main thread. You can then assert on its effects just as in any other test, as below.
import os
import signal
import time
import threading
import unittest
from unittest.mock import (
Mock,
patch,
)
import service
class TestService(unittest.TestCase):
#patch('service.print')
def test_signal_handling(self, mock_print):
pid = os.getpid()
def trigger_signal():
while len(mock_print.mock_calls) < 1:
time.sleep(0.2)
os.kill(pid, signal.SIGINT)
thread = threading.Thread(target=trigger_signal)
thread.daemon = True
thread.start()
service.main()
self.assertEqual(mock_print.mock_calls[1][1][0], 'Some cleanup')
if __name__ == '__main__':
unittest.main()
Let's refactor that to make it easier to test:
def loop():
try:
print("Some action here")
except:
# clean up and re-raise
print("Some cleanup")
raise
def main():
def signal_handler(signalnum, _):
# How to get this to block to run in a test?
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
while True:
try:
loop_body()
time.sleep(10)
except SystemExit:
break
if __name__ == '__main__':
main()
This doesn't allow easy testing of the signal handling code though. However, that amount is so small, rarely changed and strongly depends on the environment, that it is possible and perhaps even better to test manually.
For clarity, it could be useful to use a context handler, which is usually a good idea when you have setup/shutdown code. You don't mention the setup code, but my Crystall Ball (tm) tells me it exists. It could then be called like this:
try:
with my_service() as service:
while True:
service.run()
sleep(10)
except SystemExit:
# perform graceful shutdown on signal
pass
I'll leave the implementation of that context manager to you, but check out contextlib, which makes it easy and fun.

Catch Keyboard Interrupt in program that is waiting on an Event

The following program hangs the terminal such that it ignores Ctrl+C. This is rather annoying since I have to restart the terminal every time one of the threads hang.
Is there any way to catch the KeyboardInterrupt while waiting on an event?
import threading
def main():
finished_event = threading.Event()
startThread(finished_event)
finished_event.wait()#I want to stop the program here
print('done!')
def startThread(evt):
"""Start a thread that will trigger evt when it is done"""
#evt.set()
if __name__ == '__main__':
main()
If you want to avoid polling, you can use the pause() function of the signal module instead of finished_event.wait(). signal.pause() is a blocking function and gets unblocked when a signal is received by the process. In this case, when ^C is pressed, SIGINT signal unblocks the function. Note that the function does not work on Windows according to the documentation. I've tried it on Linux and it worked for me.
I came across this solution in this SO thread.
Update: On the current Python 3 finished_event.wait() works on my Ubuntu machine (starting with Python 3.2). You don't need to specify the timeout parameter, to interrupt it using Ctrl+C. You need to pass the timeout parameter on CPython 2.
Here's a complete code example:
#!/usr/bin/env python3
import threading
def f(event):
while True:
pass
# never reached, otherwise event.set() would be here
event = threading.Event()
threading.Thread(target=f, args=[event], daemon=True).start()
try:
print('Press Ctrl+C to exit')
event.wait()
except KeyboardInterrupt:
print('got Ctrl+C')
There could be bugs related to Ctrl+C. Test whether it works in your environment.
Old polling answer:
You could try to allow the interpreter to run the main thread:
while not finished_event.wait(.1): # timeout in seconds
pass
If you just want to wait until the child thread is done:
while thread.is_alive():
thread.join(.1)
You could also patch the Event.wait() function in the following manner:
def InterruptableEvent():
e = threading.Event()
def patched_wait():
while not e.is_set():
e._wait(3)
e._wait = e.wait
e.wait = patched_wait
return e
>>> event = InterruptableEvent()
>>> try:
... event.wait()
... except KeyboardInterrupt:
... print "Received KeyboardInterrupt"
...
^CReceived KeyboardInterrupt
This works because wait() with a timeout argument will raise a KeyboardInterrupt.
Based on #Pete's answer, but with subclassing and using the actual Event.wait method, just with smaller timeouts to allow handling of KeyboardInterrupts and such in between:
class InterruptableEvent(threading.Event):
def wait(self, timeout=None):
wait = super().wait # get once, use often
if timeout is None:
while not wait(0.01): pass
else:
wait(timeout)

Simple threading in Python 2.6 using thread.start_new_thread()

I'm following a tutorial on simple threading. They give this example and when I try to use it I'm getting unintelligible errors from the interpreter. Can you please tell me why this isn't working? I'm on WinXP SP3 w/ Python 2.6 current
import thread
def myfunction(mystring,*args):
print mystring
if __name__ == '__main__':
try:
thread.start_new_thread(myfunction,('MyStringHere',1))
except Exception as errtxt:
print errtxt
Executing this results in::
Unhandled exception in thread started by
Error in sys.excepthook:
Original exception was:
The information missing in the error is actually missing in the output.
The problem is that your main thread has quit before your new thread has time to finish. The solution is to wait at your main thread.
import thread, time
def myfunction(mystring,*args):
print mystring
if __name__ == '__main__':
try:
thread.start_new_thread(myfunction,('MyStringHere',1))
except Exception, errtxt:
print errtxt
time.sleep(5)
As a side note, you probably want to use the threading module. Your main thread will wait for all of those types of threads to be closed before exiting:
from threading import Thread
def myfunction(mystring,*args):
print mystring
if __name__ == '__main__':
try:
Thread(target=myfunction, args=('MyStringHere',1)).start()
except Exception, errtxt:
print errtxt
You need to wait until your Thread finishes its work, so you have to use Thread.join() :
from threading import Thread
def myfunction(mystring,*args):
print mystring
if __name__ == '__main__':
try:
t = Thread(None,myfunction,None,('MyStringHere',1))
t.start()
t.join()
except Exception as errtxt:
print errtxt
import thread
def myfunction(mystring,*args):
print mystring
if __name__ == '__main__':
try:
thread.start_new_thread(myfunction,('MyStringHere',1))
except Exception as errtxt:
print errtxt
while 1:
pass
Put while loop at last then it will work for you.
I tried it in Python 2.5 on a mac, after changing
except Exception as errtxt:
to
except Exception, errtxt:
The program did not throw an exception but also did not print anything. Not sure if that is helpful, but I do find it curious...
When I ran this code in Python 2.6 it worked, is it possible you have open threads already that are locked on the function? I recommend closing Python completely, checking your running processes to make sure nothing of yours is running and try again.

Categories