]> granicus.if.org Git - python/commitdiff
bpo-34246: Use no mutable default args in smtplib (GH-8554)
authorPablo Aguiar <scorphus@gmail.com>
Fri, 7 Sep 2018 22:04:48 +0000 (00:04 +0200)
committerPablo Galindo <Pablogsal@gmail.com>
Fri, 7 Sep 2018 22:04:48 +0000 (23:04 +0100)
Some methods of the SMTP class use mutable default arguments. Specially
`send_message` is affected as it mutates one of the args by appending items
to it, which has side effects on further calls.

Doc/library/smtplib.rst
Lib/smtplib.py
Lib/test/test_smtplib.py
Misc/ACKS
Misc/NEWS.d/next/Library/2018-07-29-15-25-15.bpo-34246.xiKq-Q.rst [new file with mode: 0644]

index e5effd0306a4012222263bfeb649740156fdc447..805217252ae87393d8847086d97b46192f09a7d4 100644 (file)
@@ -419,7 +419,7 @@ An :class:`SMTP` instance has the following methods:
       :exc:`SMTPException`.
 
 
-.. method:: SMTP.sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[])
+.. method:: SMTP.sendmail(from_addr, to_addrs, msg, mail_options=(), rcpt_options=())
 
    Send mail.  The required arguments are an :rfc:`822` from-address string, a list
    of :rfc:`822` to-address strings (a bare string will be treated as a list with 1
@@ -491,7 +491,7 @@ An :class:`SMTP` instance has the following methods:
 
 
 .. method:: SMTP.send_message(msg, from_addr=None, to_addrs=None, \
-                              mail_options=[], rcpt_options=[])
+                              mail_options=(), rcpt_options=())
 
    This is a convenience method for calling :meth:`sendmail` with the message
    represented by an :class:`email.message.Message` object.  The arguments have
index b679875fd2c5399ec470fcc2f724df347a7c27fa..048c6bfb0671b1a1eb1db35eeece8e75f757e1b8 100755 (executable)
@@ -513,7 +513,7 @@ class SMTP:
         """SMTP 'noop' command -- doesn't do anything :>"""
         return self.docmd("noop")
 
-    def mail(self, sender, options=[]):
+    def mail(self, sender, options=()):
         """SMTP 'mail' command -- begins mail xfer session.
 
         This method may raise the following exceptions:
@@ -534,7 +534,7 @@ class SMTP:
         self.putcmd("mail", "FROM:%s%s" % (quoteaddr(sender), optionlist))
         return self.getreply()
 
-    def rcpt(self, recip, options=[]):
+    def rcpt(self, recip, options=()):
         """SMTP 'rcpt' command -- indicates 1 recipient for this mail."""
         optionlist = ''
         if options and self.does_esmtp:
@@ -785,8 +785,8 @@ class SMTP:
             raise SMTPResponseException(resp, reply)
         return (resp, reply)
 
-    def sendmail(self, from_addr, to_addrs, msg, mail_options=[],
-                 rcpt_options=[]):
+    def sendmail(self, from_addr, to_addrs, msg, mail_options=(),
+                 rcpt_options=()):
         """This command performs an entire mail transaction.
 
         The arguments are:
@@ -890,7 +890,7 @@ class SMTP:
         return senderrs
 
     def send_message(self, msg, from_addr=None, to_addrs=None,
-                mail_options=[], rcpt_options={}):
+                     mail_options=(), rcpt_options=()):
         """Converts message to a bytestring and passes it to sendmail.
 
         The arguments are as for sendmail, except that msg is an
@@ -958,7 +958,7 @@ class SMTP:
             if international:
                 g = email.generator.BytesGenerator(
                     bytesmsg, policy=msg.policy.clone(utf8=True))
-                mail_options += ['SMTPUTF8', 'BODY=8BITMIME']
+                mail_options = (*mail_options, 'SMTPUTF8', 'BODY=8BITMIME')
             else:
                 g = email.generator.BytesGenerator(bytesmsg)
             g.flatten(msg_copy, linesep='\r\n')
index 495764f9aca902b6f36739108ebc0de59e784c4f..8a29e98a4f303a6c081fa302f89eb863835af720 100644 (file)
@@ -20,6 +20,7 @@ import threading
 import unittest
 from test import support, mock_socket
 from test.support import HOST, HOSTv4, HOSTv6
+from unittest.mock import Mock
 
 
 if sys.platform == 'darwin':
@@ -578,6 +579,33 @@ class NonConnectingTests(unittest.TestCase):
                           "localhost:bogus")
 
 
+class DefaultArgumentsTests(unittest.TestCase):
+
+    def setUp(self):
+        self.msg = EmailMessage()
+        self.msg['From'] = 'Páolo <főo@bar.com>'
+        self.smtp = smtplib.SMTP()
+        self.smtp.ehlo = Mock(return_value=(200, 'OK'))
+        self.smtp.has_extn, self.smtp.sendmail = Mock(), Mock()
+
+    def testSendMessage(self):
+        expected_mail_options = ('SMTPUTF8', 'BODY=8BITMIME')
+        self.smtp.send_message(self.msg)
+        self.smtp.send_message(self.msg)
+        self.assertEqual(self.smtp.sendmail.call_args_list[0][0][3],
+                         expected_mail_options)
+        self.assertEqual(self.smtp.sendmail.call_args_list[1][0][3],
+                         expected_mail_options)
+
+    def testSendMessageWithMailOptions(self):
+        mail_options = ['STARTTLS']
+        expected_mail_options = ('STARTTLS', 'SMTPUTF8', 'BODY=8BITMIME')
+        self.smtp.send_message(self.msg, None, None, mail_options)
+        self.assertEqual(mail_options, ['STARTTLS'])
+        self.assertEqual(self.smtp.sendmail.call_args_list[0][0][3],
+                         expected_mail_options)
+
+
 # test response of client to a non-successful HELO message
 class BadHELOServerTests(unittest.TestCase):
 
index 82fbc921feaa91d179f363fefdba69cd8d2175c9..75047d89010e3742d42275ddc23cbaedc853bfb5 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -22,6 +22,7 @@ Eitan Adler
 Anton Afanasyev
 Ali Afshar
 Nitika Agarwal
+Pablo S. Blum de Aguiar
 Jim Ahlstrom
 Farhan Ahmad
 Matthew Ahrens
diff --git a/Misc/NEWS.d/next/Library/2018-07-29-15-25-15.bpo-34246.xiKq-Q.rst b/Misc/NEWS.d/next/Library/2018-07-29-15-25-15.bpo-34246.xiKq-Q.rst
new file mode 100644 (file)
index 0000000..50c91ec
--- /dev/null
@@ -0,0 +1,2 @@
+:meth:`smtplib.SMTP.send_message` no longer modifies the content of the
+*mail_options* argument. Patch by Pablo S. Blum de Aguiar.