code: def exc_emailer(send_mail_func, logger=None, catch=Exception, print_to_stderr=True):
      
      # if they don't give a logger, use our own
      if logger is None:
          logger = log
  
      @decorator
      def decorate(fn, *args, **kwargs):
          exc_info = None
          try:
              return fn(*args, **kwargs)
          except catch as e:
              body = format_exc()
              exc_info = sys.exc_info()
              error_msg = 'exc_mailer() caught an exception, email will be sent.'
              logger.exception(error_msg)
              if print_to_stderr:
                  print(error_msg + '  ' + str(e), file=sys.stderr)
              try:
                  send_mail_func(body)
              except Exception:
                  logger.exception('exc_mailer(): send_mail_func() threw an exception, '
                                   'logging it & then re-raising original exception')
                  six.reraise(exc_info[0], exc_info[1], exc_info[2])
          finally:
              # delete the traceback so we don't have garbage collection issues.
              # see warning at: http://docs.python.org/library/sys.html#sys.exc_info
              if exc_info is not None:
                  del exc_info
      return decorate
docs: Catch exceptions and email them using `send_mail_func` which should
          accept a single string argument which will be the traceback to be
          emailed. Will re-raise original exception if calling `send_mail_func`
          raises an exception.
  
          Provide a logging.Logger instance for `logger` if desired (recommended).
  
          The exceptions this decorator handled can be adjusted by setting `catch`
          to an Exception class or tuple of exception classes that should be
          handled.