def emit(self, record): fqdn = getfqdn() from_address = default_email_address() subject = '[sage-notebook] %s' % fqdn record.fqdn = fqdn message = self.format(record) recipients = self.conf['notification_recipients'] if recipients is None: recipients = [] for rcpt in recipients: to = rcpt.strip() send_mail(from_address, to, subject, message)
def email(to, subject, body = '', from_address = None, verbose = True, block = False, kill_on_exit = False): """ Send an email message. INPUT: to -- string; address of recipient subject -- string; subject of the email body -- string (default: ''); body of the email from_address -- string (default: username@hostname); address email will appear to be from verbose -- whether to print status information when the email is sent block -- bool (default: False); if True this function doesn't return until the email is actually sent. if False, the email gets sent in a background thread. kill_on_exit -- bool (default: False): if True, guarantee that the sending mail subprocess is killed when you exit sage, even if it failed to send the message. If False, then the subprocess might keep running for a while. This should never be a problem, but might be useful for certain users. EXAMPLES:: sage: email('*****@*****.**', 'The calculation finished!') # not tested Child process ... is sending email to [email protected] NOTE: This function does not require configuring an email server or anything else at all, since Sage already includes by default a sophisticated email server (which is part of Twisted). """ # We use Fork to make this work, because we have to start the # Twisted reactor in order to use it's powerful sendmail # capabilities. Unfortunately, Twisted is purposely designed so # that its reactors cannot be restarted. Thus if we don't fork, # one could send at most one email. Of course, forking means this # won't work on native Windows. It might be possible to get this # to work using threads instead, but I did not do so, since Python # threading with Twisted is not fun, and would likely have many # of the same problems. Plus the below works extremely well. try: pid = os.fork() except: print("Fork not possible -- the email command is not supported on this platform.") return if from_address is None: # Use a default email address as the from: line. from_address = default_email_address() if pid: # We're the parent process if kill_on_exit: # Tell the Sage cleaner about this subprocess, just in case somehow it fails # to properly quit (e.g., smtp is taking a long time), so it will get killed # no matter what when sage exits. Zombies are bad bad bad, no matter what! from sagenb.misc.misc import register_with_cleaner register_with_cleaner(pid) # register pid of forked process with cleaner if verbose: print("Child process %s is sending email to %s..." % (pid, to)) # Now wait for the fake subprocess to finish. os.waitpid(pid,0) return if not block: # Do a non-block sendmail, which is typically what a user wants, since it can take # a while to send an email. # Use the old "double fork" trick -- otherwise there would *definitely* be a zombie # every time. Here's a description from the web of this trick: # "If you can't stand zombies, you can get rid of them with a double fork(). # The forked child immediately forks again while its parent calls waitpid(). # The first forked process exits, and the parent's waitpid() returns, right # away. That leaves an orphaned process whose parent reverts to 1 ("init")." pid = os.fork() if pid: # OK, we're in the subprocess of the subprocess -- we # again register the subprocess we just spawned with the # zombie cleaner just in case, then we kill ourself, as # explained above. if kill_on_exit: from sagenb.misc.misc import register_with_cleaner register_with_cleaner(pid) # register pid of forked process with cleaner os.kill(os.getpid(),9) # suicide # Now we're the child process. Let's do stuff with Twisetd! from smtpsend import send_mail, reactor # First define two callback functions. Each one optionally prints # some information, then kills the subprocess dead. def on_success(result): """ Callback in case of a successfully sent email. """ if verbose: print("Successfully sent an email to %s." % to) reactor.stop() os.kill(os.getpid(),9) # suicide def on_failure(error): """ Callback in case of a failure sending an email. """ if verbose: print("Failed to send email to %s." % to) print("-" * 70) print(error.getErrorMessage()) print("-" * 70) reactor.stop() os.kill(os.getpid(),9) # suicide # Finally, call the send_mail function. This is code that sets up # a twisted deferred, which actually happens when we run the # reactor. send_mail(from_address, to, subject, body, on_success, on_failure) # Start the twisted reactor. reactor.run()
def email(to, subject, body='', from_address=None, verbose=True, block=False, kill_on_exit=False): """ Send an email message. INPUT: to -- string; address of recipient subject -- string; subject of the email body -- string (default: ''); body of the email from_address -- string (default: username@hostname); address email will appear to be from verbose -- whether to print status information when the email is sent block -- bool (default: False); if True this function doesn't return until the email is actually sent. if False, the email gets sent in a background thread. kill_on_exit -- bool (default: False): if True, guarantee that the sending mail subprocess is killed when you exit sage, even if it failed to send the message. If False, then the subprocess might keep running for a while. This should never be a problem, but might be useful for certain users. EXAMPLES:: sage: email('*****@*****.**', 'The calculation finished!') # not tested Child process ... is sending email to [email protected] NOTE: This function does not require configuring an email server or anything else at all, since Sage already includes by default a sophisticated email server (which is part of Twisted). """ # We use Fork to make this work, because we have to start the # Twisted reactor in order to use it's powerful sendmail # capabilities. Unfortunately, Twisted is purposely designed so # that its reactors cannot be restarted. Thus if we don't fork, # one could send at most one email. Of course, forking means this # won't work on native Windows. It might be possible to get this # to work using threads instead, but I did not do so, since Python # threading with Twisted is not fun, and would likely have many # of the same problems. Plus the below works extremely well. try: pid = os.fork() except: print( "Fork not possible -- the email command is not supported on this platform." ) return if from_address is None: # Use a default email address as the from: line. from_address = default_email_address() if pid: # We're the parent process if kill_on_exit: # Tell the Sage cleaner about this subprocess, just in case somehow it fails # to properly quit (e.g., smtp is taking a long time), so it will get killed # no matter what when sage exits. Zombies are bad bad bad, no matter what! from sagenb.misc.misc import register_with_cleaner register_with_cleaner( pid) # register pid of forked process with cleaner if verbose: print("Child process %s is sending email to %s..." % (pid, to)) # Now wait for the fake subprocess to finish. os.waitpid(pid, 0) return if not block: # Do a non-block sendmail, which is typically what a user wants, since it can take # a while to send an email. # Use the old "double fork" trick -- otherwise there would *definitely* be a zombie # every time. Here's a description from the web of this trick: # "If you can't stand zombies, you can get rid of them with a double fork(). # The forked child immediately forks again while its parent calls waitpid(). # The first forked process exits, and the parent's waitpid() returns, right # away. That leaves an orphaned process whose parent reverts to 1 ("init")." pid = os.fork() if pid: # OK, we're in the subprocess of the subprocess -- we # again register the subprocess we just spawned with the # zombie cleaner just in case, then we kill ourself, as # explained above. if kill_on_exit: from sagenb.misc.misc import register_with_cleaner register_with_cleaner( pid) # register pid of forked process with cleaner os.kill(os.getpid(), 9) # suicide # Now we're the child process. Let's do stuff with Twisetd! from smtpsend import send_mail, reactor # First define two callback functions. Each one optionally prints # some information, then kills the subprocess dead. def on_success(result): """ Callback in case of a successfully sent email. """ if verbose: print("Successfully sent an email to %s." % to) reactor.stop() os.kill(os.getpid(), 9) # suicide def on_failure(error): """ Callback in case of a failure sending an email. """ if verbose: print("Failed to send email to %s." % to) print("-" * 70) print(error.getErrorMessage()) print("-" * 70) reactor.stop() os.kill(os.getpid(), 9) # suicide # Finally, call the send_mail function. This is code that sets up # a twisted deferred, which actually happens when we run the # reactor. send_mail(from_address, to, subject, body, on_success, on_failure) # Start the twisted reactor. reactor.run()