Ticket #6572: 6572-2.patch

File 6572-2.patch, 6.2 KB (added by Kai Zhang, 8 years ago)

Revise the patch according to the comments.

  • twisted/mail/test/test_smtp.py

     
    44"""
    55Test cases for twisted.mail.smtp module.
    66"""
     7import inspect
    78
    89from zope.interface import implements, directlyProvides
    910
     
    1314from twisted.mail import smtp
    1415from twisted.internet import defer, protocol, reactor, interfaces
    1516from twisted.internet import address, error, task
    16 from twisted.test.proto_helpers import StringTransport
     17from twisted.test.proto_helpers import MemoryReactor, StringTransport
    1718
    1819from twisted import cred
    1920import twisted.cred.error
     
    16491650            warningsShown[0]['message'],
    16501651            "tlsMode attribute of twisted.mail.smtp.ESMTPClient "
    16511652            "is deprecated since Twisted 13.0")
     1653
     1654
     1655
     1656class StringTransport(StringTransport):
     1657    """
     1658    A version of C{StringTransport} that supports C{abortConnection}.
     1659    """
     1660    aborting = False
     1661
     1662
     1663    def abortConnection(self):
     1664        """
     1665        A testable version of the C{ITCPTransport.abortConnection} method.
     1666
     1667        Since this is a special case of closing the connection,
     1668        C{loseConnection} is also called.
     1669        """
     1670        self.aborting = True
     1671        self.loseConnection()
     1672
     1673
     1674
     1675class SendmailTestCase(unittest.TestCase):
     1676    """
     1677    Tests for L{twisted.mail.smtp.sendmail}.
     1678    """
     1679    def test_defaultReactorIsGlobalReactor(self):
     1680        """
     1681        The default C{_reactor} parameter of L{twisted.mail.smtp.sendmail} is
     1682        L{twisted.internet.reactor}.
     1683        """
     1684        args, varArgs, keywords, defaults = inspect.getargspec(smtp.sendmail)
     1685        index = len(args)-args.index("_reactor")+1
     1686        self.assertEqual(reactor, defaults[index])
     1687
     1688
     1689    def test_cancelBeforeConnectionMade(self):
     1690        """
     1691        When a user cancels L{twisted.mail.smtp.sendmail} before the connection
     1692        is made, the connection is closed by
     1693        L{twisted.internet.interfaces.IConnector.disconnect}.
     1694
     1695        @rtype: C{Deferred}
     1696        @return: A C{Deferred} returned by L{twisted.mail.smtp.sendmail}.
     1697        """
     1698        reactor = MemoryReactor()
     1699        d = smtp.sendmail("localhost", "source@address", "recipient@address",
     1700                          "message", _reactor=reactor)
     1701        connector = reactor.connectors[0]
     1702        d.cancel()
     1703        self.assertEqual(connector._disconnected, True)
     1704        self.assertFailure(d, defer.CancelledError)
     1705        return d
     1706
     1707
     1708    def test_cancelAfterConnectionMade(self):
     1709        """
     1710        When a user cancels L{twisted.mail.smtp.sendmail} after the connection
     1711        is made, the connection is closed by
     1712        L{twisted.internet.interfaces.ITransport.abortConnection}.
     1713
     1714        @rtype: C{Deferred}
     1715        @return: A C{Deferred} returned by L{twisted.mail.smtp.sendmail}.
     1716        """
     1717        reactor = MemoryReactor()
     1718        transport = StringTransport()
     1719        d = smtp.sendmail("localhost", "source@address", "recipient@address",
     1720                          "message", _reactor=reactor)
     1721        factory = reactor.tcpClients[0][2]
     1722        connector = reactor.connectors[0]
     1723        p = factory.buildProtocol(None)
     1724        p.makeConnection(transport)
     1725        d.cancel()
     1726        self.assertEqual(transport.aborting, True)
     1727        self.assertEqual(transport.disconnecting, True)
     1728        self.assertFailure(d, defer.CancelledError)
     1729        return d
  • twisted/mail/smtp.py

     
    17521752        self.result = deferred
    17531753        self.result.addBoth(self._removeDeferred)
    17541754        self.sendFinished = 0
     1755        self.p = None
    17551756
    17561757        self.retries = -retries
    17571758        self.timeout = timeout
     
    17861787        p = self.protocol(self.domain, self.nEmails*2+2)
    17871788        p.factory = self
    17881789        p.timeout = self.timeout
     1790        self.p = p
     1791        self.result.addBoth(self._removeProtocol)
    17891792        return p
    17901793
     1794    def _removeProtocol(self, argh):
     1795        if self.p:
     1796            del self.p
     1797            self.p = None
     1798        return argh
    17911799
    17921800
     1801
    17931802from twisted.mail.imap4 import IClientAuthentication
    17941803from twisted.mail.imap4 import CramMD5ClientAuthenticator, LOGINAuthenticator
    17951804from twisted.mail.imap4 import LOGINCredentials as _lcredentials
     
    18941903        p.timeout = self.timeout
    18951904        return p
    18961905
    1897 def sendmail(smtphost, from_addr, to_addrs, msg, senderDomainName=None, port=25):
     1906def sendmail(smtphost, from_addr, to_addrs, msg,
     1907             senderDomainName=None, port=25, _reactor=reactor):
    18981908    """Send an email
    18991909
    19001910    This interface is intended to be a direct replacement for
     
    19211931
    19221932    @param port: Remote port to which to connect.
    19231933
     1934    @param _reactor: The reactor used to make TCP connection.
     1935
    19241936    @rtype: L{Deferred}
    1925     @returns: A L{Deferred}, its callback will be called if a message is sent
    1926         to ANY address, the errback if no message is sent.
     1937    @returns: A cancellable L{Deferred}, its callback will be called if a
     1938        message is sent to ANY address, the errback if no message is sent. When
     1939        the C{cancel} method is called, it will stop retry and disconnect the
     1940        connection immediately.
    19271941
    19281942        The callback will be called with a tuple (numOk, addresses) where numOk
    19291943        is the number of successful recipient addresses and addresses is a list
     
    19341948        # It's not a file
    19351949        msg = StringIO(str(msg))
    19361950
    1937     d = defer.Deferred()
     1951    def cancel(d):
     1952        """
     1953        Cancel the L{twisted.mail.smtp.sendmail} call, tell the factory not to
     1954        retry and disconnect the connection.
     1955        """
     1956        factory.sendFinished = 1
     1957        if factory.p:
     1958            factory.p.transport.abortConnection()
     1959        else:
     1960            # Connection hasn't been made yet
     1961            connector.disconnect()
     1962    d = defer.Deferred(cancel)
    19381963    factory = SMTPSenderFactory(from_addr, to_addrs, msg, d)
    19391964
    19401965    if senderDomainName is not None:
    19411966        factory.domain = senderDomainName
    19421967
    1943     reactor.connectTCP(smtphost, port, factory)
     1968    connector = _reactor.connectTCP(smtphost, port, factory)
    19441969
    19451970    return d
    19461971