#!/usr/local/bin/python

import types

class SignalHandler:
  """Create a mechanism to set a signal handler and reset when the instance
is destroyed.
  hndlr = lambda signo, frame: reset_server()
  try:
    signal = SignalHandler('HUP', hndlr)
    ...
  finally:
    del signal
"""

  # the module is encapsulated in the class
  import signal
  func = signal.signal
  ignore = signal.SIG_IGN

  def __init__(self, signal, handler=None):
    """Can take an integer signal number (checked in the signal module)
or the string name of a valid signal.
"""
    if type(signal) is types.IntType:
      for name, val in vars(self.signal):
	if val == signal:
	  break
      else:
	raise ValueError, "must be a valid signal value"
      self.name = name
      self.sigval = signal
    elif type(signal) is types.StringType:
      if signal[:3] != 'SIG':
        signal = 'SIG' + signal
      self.name = signal
      try:
	self.sigval = getattr(self.signal, signal)
      except AttributeError:
        raise ValueError, "must be a valid signal name"
    else:
      raise TypeError, "must supply a string or integer"

    if handler is None:
      handler = self.ignore
    elif not callable(handler):
      raise TypeError, "must supply a callable object"

    self.handler = handler
    self.old_handler = self.func(self.sigval, handler)

  def __del__(self):
    try:
      self.clear()
    except:
      pass

  def clear(self):
    # having signal.signal be a class attribute keeps the function around
    # even if the signal module is destroyed
    self.func(self.sigval, self.old_handler)

class SignalSet:
  """Allow a set of signals be set and reset together."""
  entry_class = SignalHandler
  def __init__(self, handler, *signals):
    self.handler = handler
    self.signals = map(lambda sig, s=self: s.entry_class(sig, s.handler),
	               signals)

def _test():
  import os
  class TryMe(SignalHandler):
    def __init__(self, signal):
      SignalHandler.__init__(self, signal, self.tryme)
    def tryme(self, signo, frame):
      print 'Caught signal', self.name

  hndlr_term = SignalHandler('TERM')
  hndlr_hup = TryMe('HUP')
  os.kill(os.getpid(), 1)  # should see the printout
  os.kill(os.getpid(), 15) # shouldn't do anything
  hndlr_hup.clear()
  os.kill(os.getpid(), 15) # still shouldn't do anything
  os.kill(os.getpid(), 1)  # should exit

if __name__ == '__main__':
  _test()

