Why wasn't CTRL-C captured and signal_handler called?
I have the following standard capture implementation Ctrl+C:
def signal_handler(signal, frame):
status = server.stop()
print("[{source}] Server Status: {status}".format(source=__name__.upper(),
status=status))
print("Exiting ...")
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
On server.start()
I am running a threaded instance of CherryPy. I created a thread thinking that maybe since CherryPy started the main thread hasn't seen it Ctrl+C. It didn't seem to have any effect, but posted the code as I am now:
__ main __ :
server.start()
server
def start(self):
# self.engine references cherrypy.engine
self.__cherry_thread = threading.Thread(target=self.engine.start)
self.status['running'] = True
self.status['start_time'] = get_timestamp()
self.__cherry_thread.start()
def stop(self):
self.status['running'] = False
self.status['stop_time'] = get_timestamp()
self.engine.exit()
self.__thread_event.set()
return self.status
When I click Ctrl+C, the app doesn't stop. I put a breakpoint at signal_handler
above and it never hit.
source to share
It's not entirely clear what you want to achieve at the end, but it looks like you missed an important point in CherryPy's design.
CherryPy's state and component layout is built around a message bus . For you as a developer, this is also an abstraction from OS-specific signaling. Therefore, if you want to have a stream, it is recommended to include in a CherryPy plugin that will stick to server state.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import threading
import time
import logging
import cherrypy
from cherrypy.process.plugins import SimplePlugin
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8
}
}
class ExamplePlugin(SimplePlugin):
_thread = None
_running = None
_sleep = None
def __init__(self, bus, sleep = 2):
SimplePlugin.__init__(self, bus)
self._sleep = sleep
def start(self):
'''Called when the engine starts'''
self.bus.log('Setting up example plugin')
# You can listen for a message published in request handler or
# elsewhere. Usually it putting some into the queue and waiting
# for queue entry in the thread.
self.bus.subscribe('do-something', self._do)
self._running = True
if not self._thread:
self._thread = threading.Thread(target = self._target)
self._thread.start()
# Make sure plugin priority matches your design e.g. when starting a
# thread and using Daemonizer which forks and has priority of 65, you
# need to start after the fork as default priority is 50
# see https://groups.google.com/forum/#!topic/cherrypy-users/1fmDXaeCrsA
start.priority = 70
def stop(self):
'''Called when the engine stops'''
self.bus.log('Freeing up example plugin')
self.bus.unsubscribe('do-something', self._do)
self._running = False
if self._thread:
self._thread.join()
self._thread = None
def exit(self):
'''Called when the engine exits'''
self.unsubscribe()
def _target(self):
while self._running:
try:
self.bus.log('some periodic routine')
time.sleep(self._sleep)
except:
self.bus.log('Error in example plugin', level = logging.ERROR, traceback = True)
def _do(self, arg):
self.bus.log('handling the message: {0}'.format(arg))
class App:
@cherrypy.expose
def index(self):
cherrypy.engine.publish('do-something', 'foo')
return 'Look in the terminal or log'
if __name__ == '__main__':
ExamplePlugin(cherrypy.engine).subscribe()
cherrypy.quickstart(App(), '/', config)
UPDATE
Learn more about handling the SIGINT signal. Here's the FSM diagram from the first link.
O
|
V
STOPPING --> STOPPED --> EXITING -> X
A A |
| \___ |
| \ |
| V V
STARTED <-- STARTING
Your interest is either STOPPING
, EXITING
as both are related to SIGINT processing. The difference is that it STOPPING
can happen multiple times, for example. when the server is unmounted SIGHUP forces it to reboot. So you can just put your completion routine in ExamplePlugin.exit
.
source to share