Why is CTRL-C not captured and signal_handler called?

后端 未结 1 1540
灰色年华
灰色年华 2020-12-21 19:26

I have the following standard implementation of capturing Ctrl+C:

def signal_handler(signal, frame):
    status = server.stop()
    print(\"[{sour         


        
1条回答
  •  醉梦人生
    2020-12-21 20:00

    It's not quite clear what you want to achieve in the end, but it looks like you miss important point of CherryPy design.

    CherryPy state and component orchestration is built around the message bus. To you as a developer it's also an abstraction from OS-specific signalling. So if you want to have a thread, it is a good idea to wrap in into CherryPy plugin which will adhere to state of the server.

    #!/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's 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

    More explicitly about handling 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 or EXITING as both relate to handling SIGINT. The difference is that STOPPING may occur several times e.g. when the server is daemonised SIGHUP makes it restart. So you can just put your termination routine in ExamplePlugin.exit.

    0 讨论(0)
提交回复
热议问题