Merge #17498: Defer SMTPServerDisconnected errors until the next command.

This commit is contained in:
R David Murray 2014-04-14 18:22:00 -04:00
commit 4d8eee9705
3 changed files with 33 additions and 3 deletions

View File

@ -478,6 +478,18 @@ class SMTP:
"""SMTP 'rset' command -- resets session."""
return self.docmd("rset")
def _rset(self):
"""Internal 'rset' command which ignores any SMTPServerDisconnected error.
Used internally in the library, since the server disconnected error
should appear to the application when the *next* command is issued, if
we are doing an internal "safety" reset.
"""
try:
self.rset()
except SMTPServerDisconnected:
pass
def noop(self):
"""SMTP 'noop' command -- doesn't do anything :>"""
return self.docmd("noop")
@ -762,7 +774,7 @@ class SMTP:
if code == 421:
self.close()
else:
self.rset()
self._rset()
raise SMTPSenderRefused(code, resp, from_addr)
senderrs = {}
if isinstance(to_addrs, str):
@ -776,14 +788,14 @@ class SMTP:
raise SMTPRecipientsRefused(senderrs)
if len(senderrs) == len(to_addrs):
# the server refused all our recipients
self.rset()
self._rset()
raise SMTPRecipientsRefused(senderrs)
(code, resp) = self.data(msg)
if code != 250:
if code == 421:
self.close()
else:
self.rset()
self._rset()
raise SMTPDataError(code, resp)
#if we got here then somebody got our mail
return senderrs

View File

@ -619,6 +619,7 @@ class SimSMTPChannel(smtpd.SMTPChannel):
data_response = None
rcpt_count = 0
rset_count = 0
disconnect = 0
def __init__(self, extra_features, *args, **kw):
self._extrafeatures = ''.join(
@ -684,6 +685,8 @@ class SimSMTPChannel(smtpd.SMTPChannel):
super().smtp_MAIL(arg)
else:
self.push(self.mail_response)
if self.disconnect:
self.close_when_done()
def smtp_RCPT(self, arg):
if self.rcpt_response is None:
@ -875,6 +878,16 @@ class SMTPSimTests(unittest.TestCase):
#TODO: add tests for correct AUTH method fallback now that the
#test infrastructure can support it.
# Issue 17498: make sure _rset does not raise SMTPServerDisconnected exception
def test__rest_from_mail_cmd(self):
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=15)
smtp.noop()
self.serv._SMTPchannel.mail_response = '451 Requested action aborted'
self.serv._SMTPchannel.disconnect = True
with self.assertRaises(smtplib.SMTPSenderRefused):
smtp.sendmail('John', 'Sally', 'test message')
self.assertIsNone(smtp.sock)
# Issue 5713: make sure close, not rset, is called if we get a 421 error
def test_421_from_mail_cmd(self):
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=15)

View File

@ -46,6 +46,11 @@ Core and Builtins
Library
-------
- Issue #17498: Some SMTP servers disconnect after certain errors, violating
strict RFC conformance. Instead of losing the error code when we issue the
subsequent RSET, smtplib now returns the error code and defers raising the
SMTPServerDisconnected error until the next command is issued.
- Issue #17826: setting an iterable side_effect on a mock function created by
create_autospec now works. Patch by Kushal Das.