Source

CherryPy / cherrypy / process / plugins.py

Diff from to

File cherrypy/process/plugins.py

 
 class SimplePlugin(object):
     """Plugin base class which auto-subscribes methods for known channels."""
-    
+
     bus = None
     """A :class:`Bus <cherrypy.process.wspbus.Bus>`, usually cherrypy.engine."""
-    
+
     def __init__(self, bus):
         self.bus = bus
-    
+
     def subscribe(self):
         """Register this object as a (multi-channel) listener on the bus."""
         for channel in self.bus.listeners:
             method = getattr(self, channel, None)
             if method is not None:
                 self.bus.subscribe(channel, method)
-    
+
     def unsubscribe(self):
         """Unregister this object as a listener on the bus."""
         for channel in self.bus.listeners:
 
 class SignalHandler(object):
     """Register bus channels (and listeners) for system signals.
-    
+
     You can modify what signals your application listens for, and what it does
     when it receives signals, by modifying :attr:`SignalHandler.handlers`,
     a dict of {signal name: callback} pairs. The default set is::
-    
+
         handlers = {'SIGTERM': self.bus.exit,
                     'SIGHUP': self.handle_SIGHUP,
                     'SIGUSR1': self.bus.graceful,
                    }
-    
+
     The :func:`SignalHandler.handle_SIGHUP`` method calls
     :func:`bus.restart()<cherrypy.process.wspbus.Bus.restart>`
     if the process is daemonized, but
     :func:`bus.exit()<cherrypy.process.wspbus.Bus.exit>`
     if the process is attached to a TTY. This is because Unix window
     managers tend to send SIGHUP to terminal windows when the user closes them.
-    
+
     Feel free to add signals which are not available on every platform. The
     :class:`SignalHandler` will ignore errors raised from attempting to register
     handlers for unknown signals.
     """
-    
+
     handlers = {}
     """A map from signal names (e.g. 'SIGTERM') to handlers (e.g. bus.exit)."""
-    
+
     signals = {}
     """A map from signal numbers to names."""
-    
+
     for k, v in vars(_signal).items():
         if k.startswith('SIG') and not k.startswith('SIG_'):
             signals[v] = k
     del k, v
-    
+
     def __init__(self, bus):
         self.bus = bus
         # Set default handlers
             self.handlers['SIGINT'] = self._jython_SIGINT_handler
 
         self._previous_handlers = {}
-    
+
     def _jython_SIGINT_handler(self, signum=None, frame=None):
         # See http://bugs.jython.org/issue1313
         self.bus.log('Keyboard Interrupt: shutting down bus')
         self.bus.exit()
-        
+
     def subscribe(self):
         """Subscribe self.handlers to signals."""
         for sig, func in self.handlers.items():
                 self.set_handler(sig, func)
             except ValueError:
                 pass
-    
+
     def unsubscribe(self):
         """Unsubscribe self.handlers from signals."""
         for signum, handler in self._previous_handlers.items():
             signame = self.signals[signum]
-            
+
             if handler is None:
                 self.bus.log("Restoring %s handler to SIG_DFL." % signame)
                 handler = _signal.SIG_DFL
             else:
                 self.bus.log("Restoring %s handler %r." % (signame, handler))
-            
+
             try:
                 our_handler = _signal.signal(signum, handler)
                 if our_handler is None:
             except ValueError:
                 self.bus.log("Unable to restore %s handler %r." %
                              (signame, handler), level=40, traceback=True)
-    
+
     def set_handler(self, signal, listener=None):
         """Subscribe a handler for the given signal (number or name).
-        
+
         If the optional 'listener' argument is provided, it will be
         subscribed as a listener for the given signal's channel.
-        
+
         If the given signal name or number is not available on the current
         platform, ValueError is raised.
         """
             except KeyError:
                 raise ValueError("No such signal: %r" % signal)
             signum = signal
-        
+
         prev = _signal.signal(signum, self._handle_signal)
         self._previous_handlers[signum] = prev
-        
+
         if listener is not None:
             self.bus.log("Listening for %s." % signame)
             self.bus.subscribe(signame, listener)
-    
+
     def _handle_signal(self, signum=None, frame=None):
         """Python signal handler (self.set_handler subscribes it for you)."""
         signame = self.signals[signum]
         self.bus.log("Caught signal %s." % signame)
         self.bus.publish(signame)
-    
+
     def handle_SIGHUP(self):
         """Restart if daemonized, else exit."""
         if os.isatty(sys.stdin.fileno()):
 
 class DropPrivileges(SimplePlugin):
     """Drop privileges. uid/gid arguments not available on Windows.
-    
+
     Special thanks to Gavin Baker: http://antonym.org/node/100.
     """
-    
+
     def __init__(self, bus, umask=None, uid=None, gid=None):
         SimplePlugin.__init__(self, bus)
         self.finalized = False
         self.uid = uid
         self.gid = gid
         self.umask = umask
-    
+
     def _get_uid(self):
         return self._uid
     def _set_uid(self, val):
         self._uid = val
     uid = property(_get_uid, _set_uid,
         doc="The uid under which to run. Availability: Unix.")
-    
+
     def _get_gid(self):
         return self._gid
     def _set_gid(self, val):
         self._gid = val
     gid = property(_get_gid, _set_gid,
         doc="The gid under which to run. Availability: Unix.")
-    
+
     def _get_umask(self):
         return self._umask
     def _set_umask(self, val):
         self._umask = val
     umask = property(_get_umask, _set_umask,
         doc="""The default permission mode for newly created files and directories.
-        
+
         Usually expressed in octal format, for example, ``0644``.
         Availability: Unix, Windows.
         """)
-    
+
     def start(self):
         # uid/gid
         def current_ids():
             if grp:
                 group = grp.getgrgid(os.getgid())[0]
             return name, group
-        
+
         if self.finalized:
             if not (self.uid is None and self.gid is None):
                 self.bus.log('Already running as uid: %r gid: %r' %
                 if self.uid is not None:
                     os.setuid(self.uid)
                 self.bus.log('Running as uid: %r gid: %r' % current_ids())
-        
+
         # umask
         if self.finalized:
             if self.umask is not None:
                 old_umask = os.umask(self.umask)
                 self.bus.log('umask old: %03o, new: %03o' %
                              (old_umask, self.umask))
-        
+
         self.finalized = True
     # This is slightly higher than the priority for server.start
     # in order to facilitate the most common use: starting on a low
 
 class Daemonizer(SimplePlugin):
     """Daemonize the running script.
-    
+
     Use this with a Web Site Process Bus via::
-    
+
         Daemonizer(bus).subscribe()
-    
+
     When this component finishes, the process is completely decoupled from
     the parent environment. Please note that when this component is used,
     the return code from the parent process will still be 0 if a startup
     of whether the process fully started. In fact, that return code only
     indicates if the process succesfully finished the first fork.
     """
-    
+
     def __init__(self, bus, stdin='/dev/null', stdout='/dev/null',
                  stderr='/dev/null'):
         SimplePlugin.__init__(self, bus)
         self.stdout = stdout
         self.stderr = stderr
         self.finalized = False
-    
+
     def start(self):
         if self.finalized:
             self.bus.log('Already deamonized.')
-        
+
         # forking has issues with threads:
         # http://www.opengroup.org/onlinepubs/000095399/functions/fork.html
         # "The general problem with making fork() work in a multi-threaded
             self.bus.log('There are %r active threads. '
                          'Daemonizing now may cause strange failures.' %
                          threading.enumerate(), level=30)
-        
+
         # See http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
         # (or http://www.faqs.org/faqs/unix-faq/programmer/faq/ section 1.7)
         # and http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66012
-        
+
         # Finish up with the current stdout/stderr
         sys.stdout.flush()
         sys.stderr.flush()
-        
+
         # Do first fork.
         try:
             pid = os.fork()
             exc = sys.exc_info()[1]
             sys.exit("%s: fork #1 failed: (%d) %s\n"
                      % (sys.argv[0], exc.errno, exc.strerror))
-        
+
         os.setsid()
-        
+
         # Do second fork
         try:
             pid = os.fork()
             exc = sys.exc_info()[1]
             sys.exit("%s: fork #2 failed: (%d) %s\n"
                      % (sys.argv[0], exc.errno, exc.strerror))
-        
+
         os.chdir("/")
         os.umask(0)
-        
+
         si = open(self.stdin, "r")
         so = open(self.stdout, "a+")
         se = open(self.stderr, "a+")
         os.dup2(si.fileno(), sys.stdin.fileno())
         os.dup2(so.fileno(), sys.stdout.fileno())
         os.dup2(se.fileno(), sys.stderr.fileno())
-        
+
         self.bus.log('Daemonized to PID: %s' % os.getpid())
         self.finalized = True
     start.priority = 65
 
 class PIDFile(SimplePlugin):
     """Maintain a PID file via a WSPBus."""
-    
+
     def __init__(self, bus, pidfile):
         SimplePlugin.__init__(self, bus)
         self.pidfile = pidfile
         self.finalized = False
-    
+
     def start(self):
         pid = os.getpid()
         if self.finalized:
             self.bus.log('PID %r written to %r.' % (pid, self.pidfile))
             self.finalized = True
     start.priority = 70
-    
+
     def exit(self):
         try:
             os.remove(self.pidfile)
 
 class PerpetualTimer(threading._Timer):
     """A responsive subclass of threading._Timer whose run() method repeats.
-    
+
     Use this timer only when you really need a very interruptible timer;
     this checks its 'finished' condition up to 20 times a second, which can
-    results in pretty high CPU usage 
+    results in pretty high CPU usage
     """
-    
+
     def run(self):
         while True:
             self.finished.wait(self.interval)
 
 class BackgroundTask(threading.Thread):
     """A subclass of threading.Thread whose run() method repeats.
-    
+
     Use this class for most repeating tasks. It uses time.sleep() to wait
     for each interval, which isn't very responsive; that is, even if you call
     self.cancel(), you'll have to wait until the sleep() call finishes before
     the thread stops. To compensate, it defaults to being daemonic, which means
     it won't delay stopping the whole process.
     """
-    
+
     def __init__(self, interval, function, args=[], kwargs={}, bus=None):
         threading.Thread.__init__(self)
         self.interval = interval
         self.kwargs = kwargs
         self.running = False
         self.bus = bus
-    
+
     def cancel(self):
         self.running = False
-    
+
     def run(self):
         self.running = True
         while self.running:
                                  % self.function, level=40, traceback=True)
                 # Quit on first error to avoid massive logs.
                 raise
-    
+
     def _set_daemon(self):
         return True
 
 
 class Monitor(SimplePlugin):
     """WSPBus listener to periodically run a callback in its own thread."""
-    
+
     callback = None
     """The function to call at intervals."""
-    
+
     frequency = 60
     """The time in seconds between callback runs."""
-    
+
     thread = None
     """A :class:`BackgroundTask<cherrypy.process.plugins.BackgroundTask>` thread."""
-    
+
     def __init__(self, bus, callback, frequency=60, name=None):
         SimplePlugin.__init__(self, bus)
         self.callback = callback
         self.frequency = frequency
         self.thread = None
         self.name = name
-    
+
     def start(self):
         """Start our callback in its own background thread."""
         if self.frequency > 0:
             else:
                 self.bus.log("Monitor thread %r already started." % threadname)
     start.priority = 70
-    
+
     def stop(self):
         """Stop our callback's background task thread."""
         if self.thread is None:
                     self.thread.join()
                 self.bus.log("Stopped thread %r." % name)
             self.thread = None
-    
+
     def graceful(self):
         """Stop the callback's background task thread and restart it."""
         self.stop()
 
 class Autoreloader(Monitor):
     """Monitor which re-executes the process when files change.
-    
+
     This :ref:`plugin<plugins>` restarts the process (via :func:`os.execv`)
     if any of the files it monitors change (or is deleted). By default, the
     autoreloader monitors all imported modules; you can add to the
     set by adding to ``autoreload.files``::
-    
+
         cherrypy.engine.autoreload.files.add(myFile)
-    
+
     If there are imported files you do *not* wish to monitor, you can adjust the
     ``match`` attribute, a regular expression. For example, to stop monitoring
     cherrypy itself::
-    
+
         cherrypy.engine.autoreload.match = r'^(?!cherrypy).+'
-    
+
     Like all :class:`Monitor<cherrypy.process.plugins.Monitor>` plugins,
     the autoreload plugin takes a ``frequency`` argument. The default is
     1 second; that is, the autoreloader will examine files once each second.
     """
-    
+
     files = None
     """The set of files to poll for modifications."""
-    
+
     frequency = 1
     """The interval in seconds at which to poll for modified files."""
-    
+
     match = '.*'
     """A regular expression by which to match filenames."""
-    
+
     def __init__(self, bus, frequency=1, match='.*'):
         self.mtimes = {}
         self.files = set()
         self.match = match
         Monitor.__init__(self, bus, self.run, frequency)
-    
+
     def start(self):
         """Start our own background task thread for self.run."""
         if self.thread is None:
             self.mtimes = {}
         Monitor.start(self)
-    start.priority = 70 
-    
+    start.priority = 70
+
     def sysfiles(self):
         """Return a Set of sys.modules filenames to monitor."""
         files = set()
                         f = os.path.normpath(os.path.join(_module__file__base, f))
                 files.add(f)
         return files
-    
+
     def run(self):
         """Reload the process if registered files have been modified."""
         for filename in self.sysfiles() | self.files:
             if filename:
                 if filename.endswith('.pyc'):
                     filename = filename[:-1]
-                
+
                 oldtime = self.mtimes.get(filename, 0)
                 if oldtime is None:
                     # Module with no .py file. Skip it.
                     continue
-                
+
                 try:
                     mtime = os.stat(filename).st_mtime
                 except OSError:
                     # Either a module with no .py file, or it's been deleted.
                     mtime = None
-                
+
                 if filename not in self.mtimes:
                     # If a module has no .py file, this will be None.
                     self.mtimes[filename] = mtime
 
 class ThreadManager(SimplePlugin):
     """Manager for HTTP request threads.
-    
+
     If you have control over thread creation and destruction, publish to
     the 'acquire_thread' and 'release_thread' channels (for each thread).
     This will register/unregister the current thread and publish to
     'start_thread' and 'stop_thread' listeners in the bus as needed.
-    
+
     If threads are created and destroyed by code you do not control
     (e.g., Apache), then, at the beginning of every HTTP request,
     publish to 'acquire_thread' only. You should not publish to
     the thread will be re-used or not. The bus will call
     'stop_thread' listeners for you when it stops.
     """
-    
+
     threads = None
     """A map of {thread ident: index number} pairs."""
-    
+
     def __init__(self, bus):
         self.threads = {}
         SimplePlugin.__init__(self, bus)
 
     def acquire_thread(self):
         """Run 'start_thread' listeners for the current thread.
-        
+
         If the current thread has already been seen, any 'start_thread'
         listeners will not be run again.
         """
             i = len(self.threads) + 1
             self.threads[thread_ident] = i
             self.bus.publish('start_thread', i)
-    
+
     def release_thread(self):
         """Release the current thread and run 'stop_thread' listeners."""
         thread_ident = get_thread_ident()
         i = self.threads.pop(thread_ident, None)
         if i is not None:
             self.bus.publish('stop_thread', i)
-    
+
     def stop(self):
         """Release all threads and run all 'stop_thread' listeners."""
         for thread_ident, i in self.threads.items():