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.

+1


source to share


1 answer


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

.

+2


source







All Articles