[Twisted-Python] How to use defer.fail better...

Hoi all, I'm writing a twisted/python SMTP server that accepts emails from a MTA using twisted. All works well, except that when I return a defer.fail(None) from processEmail to messageHandler.eomReceived (see below), twisted dumps the trace back of the last exception on stdout. Is there a way to avoid this? I would like to handle the exception gracefully (with an exception handler) and would not like to see any trace back from it in my logs. I've read that using None as the parameter to defer.fail does exactly that... what should I use instead? The other thing I would like to do better is to have a way to return smtp code 421 (service temporarily unavailable) rather than 550 (fatal) in those cases where I would like to indicate a (temporary) failure to receive the email. I've patched smtp.py (replace 550 -> 421) for this but would gladly hear about a way to do this without patching a standard component on my system. thanks, Jan class messageHandler(object): implements(smtp.IMessage) def __init__(self, addressee): self.lines = [] self.noisy = False self.emailMessage = None self.emailAddress = str(addressee) #__init__ def lineReceived(self, line): self.lines.append(line) #lineReceived def eomReceived(self): # message is complete, store it self.lines.append('') # add a trailing newline messageData = '\n'.join(self.lines) emailMessage = message_from_string(messageData) return processEmail(self.emailAddress, emailMessage) #eomReceived def connectionLost(self): log(1, 'Connection lost unexpectedly!') # unexpected loss of connection; don't save del(self.lines) del(self.emailMessage) #connectionLost #messageHandler class localDelivery(object): implements(smtp.IMessageDelivery) def __init__(self): pass #end __init__ def validateFrom(self, helo, originAddress): log (1, 'Incoming email from %s', str(originAddress)) # accept mail from anywhere. To reject an address, raise # smtp.SMTPBadSender here. return originAddress #end validateFrom def receivedHeader(self, helo, origin, recipients): myHostname, clientIP = helo headerValue = "by %s from %s with ESMTP ; %s" % (myHostname, clientIP, smtp.rfc822date()) log(1, '...received: %s', headerValue) # email.Header.Header used for automatic wrapping of long lines return "Received: %s" % Header(headerValue) #end receivedHeader def validateTo(self, user): #if not user.dest.domain in valid_domains: # print "Not accepting mail for %s" % user.dest # raise smtp.SMTPBadRcpt(user) log(1, 'Accepting email for %s', user.dest) return lambda: messageHandler(user.dest) #end validateTo #localDelivery class SMTPFactory(protocol.ServerFactory): def __init__(self): pass #end __init__ def buildProtocol(self, addr): delivery = localDelivery() smtpProtocol = smtp.SMTP(delivery) smtpProtocol.factory = self return smtpProtocol #buildProtocol #SMTPFactory

On 09:42 am, jan.bakuwel@omiha.com wrote:
Hoi all,
I'm writing a twisted/python SMTP server that accepts emails from a MTA using twisted. All works well, except that when I return a defer.fail(None) from processEmail to messageHandler.eomReceived (see below), twisted dumps the trace back of the last exception on stdout. Is there a way to avoid this? I would like to handle the exception gracefully (with an exception handler) and would not like to see any trace back from it in my logs. I've read that using None as the parameter to defer.fail does exactly that... what should I use instead?
By the time you return, you need to have handled any exceptions. If you want to separate exception handling into a separate function, use addErrback on the Deferred you have created.
The other thing I would like to do better is to have a way to return smtp code 421 (service temporarily unavailable) rather than 550 (fatal) in those cases where I would like to indicate a (temporary) failure to receive the email. I've patched smtp.py (replace 550 -> 421) for this but would gladly hear about a way to do this without patching a standard component on my system.
This is what twisted.python.failure.Failure.trap is for; you can catch specific exception types in your errbacks. Please feel free to ask more specifically if this doesn't answer your question :).

Hi Glyph,
I'm writing a twisted/python SMTP server that accepts emails from a MTA using twisted. All works well, except that when I return a defer.fail(None) from processEmail to messageHandler.eomReceived (see below), twisted dumps the trace back of the last exception on stdout. Is there a way to avoid this? I would like to handle the exception gracefully (with an exception handler) and would not like to see any trace back from it in my logs. I've read that using None as the parameter to defer.fail does exactly that... what should I use instead?
By the time you return, you need to have handled any exceptions.
I have... My processEmail returns either defer.succeed(None) or return defer.fail(None) based on some criteria. defer.succeed(None) translates into telling the MTA that I've accepted the email (250); defer.fail(None) translates into telling the MTA that I've not accepted the email this time (421) but please try again later. So... all is working as I would like it to work... except that twisted dumps the exception trace back of the exception that occurred last on stdout. There is no need for that... the exception has been handled already. Can I make twisted not do that dump by passing another parameter (instead of None) to defer.fail?
If you want to separate exception handling into a separate function, use addErrback on the Deferred you have created.
I am not using call backs explicitly.... I am returning either defer.succeed(None) or defer.fail(None) based on some criteria.
The other thing I would like to do better is to have a way to return smtp code 421 (service temporarily unavailable) rather than 550 (fatal) in those cases where I would like to indicate a (temporary) failure to receive the email. I've patched smtp.py (replace 550 -> 421) for this but would gladly hear about a way to do this without patching a standard component on my system.
This is what twisted.python.failure.Failure.trap is for; you can catch specific exception types in your errbacks.
Would you be able to give me an example how I can do this? I was thinking about somehow (?) overriding _messageHandled? Is that not the right way to go about it?
Please feel free to ask more specifically if this doesn't answer your question :).
Thanks! Please see above :-) Jan
participants (2)
-
glyph@divmod.com
-
Jan Bakuwel