In Windows I am trying to create a python process that waits for SIGINT signal.And when it receives SIGINT I want it to just print a message and wait for another occurrence of SIGINT.So I used signal handler.
Here is my signal_receiver.py code.
import signal, os, time
def handler(signum, frame):
print 'Yes , Received', signum
signal.signal(signal.SIGINT, handler)
print 'My process Id' , os.getpid()
while True:
print 'Waiting for signal'
time.sleep(10)
When this process running ,I just send SIGINT to this procees from some other python process using,
os.kill(pid,SIGINT).
But when the signal_receiver.py receives SIGINT it just quits the execution .But expected behavior is to print the message inside the handler function and continue execution.
Can some one please help me to solve this issue.Is it a limitation in windows ,because the same works fine in linux.
Thanks in advance.
When you press CTRL+C, the process receives a SIGINT and you are catching it correctly, because otherwise it would throw a KeyboardInterrupt error.
On Windows, when time.sleep(10) is interrupted, although you catch SIGINT, it still throws an InterruptedError. Just add a try/except statement inside time.sleep to catch this exception, for example:
import signal
import os
import time
def handler(signum, frame):
if signum == signal.SIGINT:
print('Signal received')
if __name__ == '__main__':
print('My PID: ', os.getpid())
signal.signal(signal.SIGINT, handler)
while True:
print('Waiting for signal')
try:
time.sleep(5)
except InterruptedError:
pass
Note: tested on Python3.x, it should also work on 2.x.
Related
So i finally found how to capture the signal from this site "https://pymotw.com/2/signal/" , I now can capture most of the kill signals like SIGINT, SIGUSR1, SIGUSR2.... all except the SIGTERM which heroku uses for some reason when I get a SIGTERM my script just exit instead of capturing the signal and do some work, Can Anyone tell me if I'm doing something wrong? I'm testing with the command "kill -INT $pid", i can capture every kill signals Except the SIGTERM, THANKS....
Code :
run = false
def receive_signal(signum, stack):
print("Received:", signum)
global run
run = False
signal.signal(signal.SIGUSR1,receive_signal)
signal.signal(signal.SIGUSR2, receive_signal)
signal.signal(signal.SIGINT, receive_signal)
signal.signal(signal.SIGTERM, receive_signal)
print("My PID is:", os.getpid())
while run:
main()
I have a thread waiting on input, but in the event that no input is provided, I need to exit the program. How can i exit the program? in this example the exit should be triggered by keyboard ctrl+c however I would also like to do this without interaction ie via a timeout or other event.
import threading
import signal
import sys
import time
shutdown = False
def shutdownHook(sigNum, currentStackFrame):
global shutdown
print('shutdown')
shutdown = True
def readInput():
print('readInput')
print(sys.stdin.readline())
print('done reading input')
if __name__ == '__main__':
signal.signal(signal.SIGINT, shutdownHook)
signal.signal(signal.SIGTERM, shutdownHook)
inputThread = threading.Thread(name='input', target=readInput)
inputThread.start()
print('started input')
while not shutdown:
time.sleep(1)
print('waiting ' + str(shutdown))
print('current thread' + str(threading.current_thread()))
print('end of program ' + str(shutdown))
sys.exit(0)
You may use signal.alarm() to send a SIGALRM to your program after a certain amount of time (define here in second):
if __name__ == '__main__':
# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, shutdownHook)
signal.alarm(5)
Here is the complete working example from the documentation:
Here is a minimal example program. It uses the alarm() function to
limit the time spent waiting to open a file; this is useful if the
file is for a serial device that may not be turned on, which would
normally cause the os.open() to hang indefinitely. The solution is to
set a 5-second alarm before opening the file; if the operation takes
too long, the alarm signal will be sent, and the handler raises an
exception.
import signal, os
def handler(signum, frame):
print('Signal handler called with signal', signum)
raise OSError("Couldn't open device!")
# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)
# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)
signal.alarm(0) # Disable the alarm
As for why your program is not quitting is because quoted the doc
Python signal handlers are always executed in the main Python thread,
even if the signal was received in another thread. This means that
signals can’t be used as a means of inter-thread communication. You
can use the synchronization primitives from the threading module
instead. Besides, only the main thread is allowed to set a new signal handler.
That means your thread cannot receive no signals the way you design the program. In fact if you try to set a signal in your thread you will receive a ValueError:
ValueError: signal only works in main thread
That's why your program keeps turning after receiving a SIGTERM. Because the thread did not received the signal.
See here: Kill python thread using os for alternative solution.
Make the thread as Deamon thread, this way it will also shutdown when main thread is exited.
inputThread = threading.Thread(name='input', target=readInput)
inputThread.setDaemon(True) # add this line
inputThread.start()
Also you can add a time lapse for no activity within specified period.
time_limit_for_shutdown_in_secs = 10
secs = 0
while not shutdown:
if secs > time_limit_for_shutdown_in_secs: break
time.sleep(1)
print('waiting ' + str(shutdown))
secs += 1
print('current thread' + str(threading.current_thread()))
print('end of program ' + str(shutdown))
sys.exit(0)
I'm starting a simple TCP server using SocketServer:
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
...
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
try:
...
finally:
server.shutdown()
However, after the program ends it doesn't terminate and seems to be stuck. It doesn't respond to keyboard events (CTRL-C) and the only way to exit is to call os._exit(0) or to just close the shell window.
I've searched a bit about it but I still don't see what I'm missing: The thread is marked as daemon, and the server is shut-down at the end.
I'm running Python 2.7.9 under Windows 8.1
Ctrl-c raises KeyboardInterrupt exception and the issue is SocketServer internally catches the error but never bothers to come out. You can add an extra signal handler for signal.SIGINT & then call server.start().
Like this,
import signal
import sys
def signal_handler(signal, frame):
logger.log("DEBUG","You pressed Ctrl+C!")
server.stop()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
server_thread.start()
signal.pause()
My code is simple:
def start():
signal(SIGINT, lambda signal, frame: raise SystemExit())
startTCPServer()
So I register my application with signal handling of SIGINT, then I start a start a TCP listener.
here are my questions:
How can I using python code to send a SIGINT signal?
How can I test whether if the application receives a signal of SIGINT, it will raise a SystemExit exception?
If I run start() in my test, it will block and how can I send a signal to it?
First of, testing the signal itself is a functional or integration test, not a unit test. See What's the difference between unit, functional, acceptance, and integration tests?
You can run your Python script as a subprocess with subprocess.Popen(), then use the Popen.send_signal() method to send signals to that process, then test that the process has exited with Popen.poll().
How can I using python code to send a SIGINT signal?
You can use os.kill, which slightly misleadingly, can used to send any signal to any process by its ID. The process ID of the application/test can be found by os.getpid(), so you would have...
pid = os.getpid()
# ... other code discussed later in the answer ...
os.kill(pid, SIGINT)
How can I test whether if the application receives a signal of SIGINT, it will raise a SystemExit exception?
The usual way in a test you can check that some code raises SystemExit, is with unittest.TestCase::assertRaises...
import start
class TestStart(unittest.TestCase):
def test_signal_handling(self):
# ... other code discussed later in the answer ...
with self.assertRaises(SystemExit):
start.start()
If I run start() in my test, it will block and how can I send a signal to it?
This is the trick: you can start another thread which then sends a signal back to the main thread which is blocking.
Putting it all together, assuming your production start function is in start.py:
from signal import (
SIGINT,
signal,
)
import socketserver
def startTCPServer():
# Taken from https://docs.python.org/3.4/library/socketserver.html#socketserver-tcpserver-example
class MyTCPHandler(socketserver.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
self.request.sendall(self.data.upper())
HOST, PORT = "localhost", 9999
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
server.serve_forever()
def start():
def raiseSystemExit(_, __):
raise SystemExit
signal(SIGINT, raiseSystemExit)
startTCPServer()
Then your test code could be like the following, say in test.py
import os
from signal import (
SIGINT,
)
import threading
import time
import unittest
import start
class TestStart(unittest.TestCase):
def test_signal_handling(self):
pid = os.getpid()
def trigger_signal():
# You could do something more robust, e.g. wait until port is listening
time.sleep(1)
os.kill(pid, SIGINT)
thread = threading.Thread(target=trigger_signal)
thread.daemon = True
thread.start()
with self.assertRaises(SystemExit):
start.start()
if __name__ == '__main__':
unittest.main()
and run using
python test.py
The above is the same technique as in the answer at https://stackoverflow.com/a/49500820/1319998
So I'm writing a small script to use with Deluge. Deluge uses Twisted, and I really don't have a firm grasp on how it works. Normally I'd just look up more info on it, but getting started with Twisted would take a long time and is beyond the scope of this little project. So I figured I would just ask here.
Now, I have this code. I'll try to explain the specifig parts I need help with
import base64
import processargs
from deluge.ui.client import client
from twisted.internet import reactor
from deluge.log import setupLogger
setupLogger()
options = processargs.readConfig(os.path.expanduser("~/.deluge-automator"))
d = client.connect(
host=options['host'],
port=int(options['port']),
username=options['username'],
password=options['password']
)
def start():
#other code
t = client.core.add_torrent_file(tfile,
base64.encodestring(data), None)
t.addCallback(on_torrent_added_success, tfile)
t.addErrback(on_torrent_added_fail)
def handle_stop_signal(SIGNAL, stack):
client.disconnect()
reactor.stop()
def on_torrent_added_success(result, tfile):
#other code
start()
def on_torrent_added_fail(result):
print "Add torrent failed!"
print "result: ", result
def on_connect_success(result):
#other code
start()
d.addCallback(on_connect_success)
def on_connect_fail(result):
print "Connection failed!"
print "result: ", result
d.addErrback(on_connect_fail)
signal.signal(signal.SIGTERM, handle_stop_signal)
signal.signal(signal.SIGINT, handle_stop_signal)
reactor.run()
When a torrent is successfully added, it should go back to start(), and it does, but I think it loses the reactor or something. Because now whenever it recieves a SIGTERM or SIGINT, the reactor closes, but doesn't quit the program:
± % python2 main.py
Connection was successful!
result: 10
^C^CConnection failed!
result: [Failure instance: Traceback: <class 'twisted.internet.error.ReactorNotRunning'>: Can't stop reactor that isn't running.
/usr/lib/python2.7/site-packages/twisted/internet/defer.py:551:_runCallbacks
/usr/lib/python2.7/site-packages/deluge/ui/client.py:412:__on_login
/usr/lib/python2.7/site-packages/twisted/internet/defer.py:368:callback
/usr/lib/python2.7/site-packages/twisted/internet/defer.py:464:_startRunCallbacks
--- <exception caught here> ---
/usr/lib/python2.7/site-packages/twisted/internet/defer.py:551:_runCallbacks
main.py:70:on_connect_success
main.py:32:start
main.py:49:handle_stop_signal
/usr/lib/python2.7/site-packages/twisted/internet/base.py:577:stop
]
So the reactor gets stopped, but it doesn't quit the program. I have to keyboard interrupt twice. Once to stop the reactor, and a second time to throw the error. Is there a certain way to set up a loop like this?
reactor handles sigint, sigterm itself (there might be a parameter of reactor.run() that disables that). Install reactor.addSystemEventTrigger('before', 'shutdown', client.disconnect) instead.
See twisted: catch keyboardinterrupt and shutdown properly.