[Python-checkins] cpython (3.5): #28047: Fix calculation of base64 line length.

r.david.murray python-checkins at python.org
Fri Sep 9 15:00:29 EDT 2016


https://hg.python.org/cpython/rev/99db6a25444b
changeset:   103435:99db6a25444b
branch:      3.5
parent:      103431:b31e803d5ad3
user:        R David Murray <rdmurray at bitdance.com>
date:        Fri Sep 09 15:00:09 2016 -0400
summary:
  #28047: Fix calculation of base64 line length.

This is buggy in the old email code as well, but it doesn't affect anything
there because only the default line length is ever used there.

files:
  Lib/email/contentmanager.py           |   9 +++--
  Lib/test/test_email/__init__.py       |  12 ++++++-
  Lib/test/test_email/test_inversion.py |  23 ++++++++++++++-
  Misc/NEWS                             |   3 +
  4 files changed, 40 insertions(+), 7 deletions(-)


diff --git a/Lib/email/contentmanager.py b/Lib/email/contentmanager.py
--- a/Lib/email/contentmanager.py
+++ b/Lib/email/contentmanager.py
@@ -126,12 +126,13 @@
             msg.set_param(key, value)
 
 
-# XXX: This is a cleaned-up version of base64mime.body_encode.  It would
-# be nice to drop both this and quoprimime.body_encode in favor of
-# enhanced binascii routines that accepted a max_line_length parameter.
+# XXX: This is a cleaned-up version of base64mime.body_encode (including a bug
+# fix in the calculation of unencoded_bytes_per_line).  It would be nice to
+# drop both this and quoprimime.body_encode in favor of enhanced binascii
+# routines that accepted a max_line_length parameter.
 def _encode_base64(data, max_line_length):
     encoded_lines = []
-    unencoded_bytes_per_line = max_line_length * 3 // 4
+    unencoded_bytes_per_line = max_line_length // 4 * 3
     for i in range(0, len(data), unencoded_bytes_per_line):
         thisline = data[i:i+unencoded_bytes_per_line]
         encoded_lines.append(binascii.b2a_base64(thisline).decode('ascii'))
diff --git a/Lib/test/test_email/__init__.py b/Lib/test/test_email/__init__.py
--- a/Lib/test/test_email/__init__.py
+++ b/Lib/test/test_email/__init__.py
@@ -121,6 +121,10 @@
     Note: if and only if the generated test name is a valid identifier can it
     be used to select the test individually from the unittest command line.
 
+    The values in the params dict can be a single value, a tuple, or a
+    dict.  If a single value of a tuple, it is passed to the test function
+    as positional arguments.  If a dict, it is a passed via **kw.
+
     """
     paramdicts = {}
     testers = collections.defaultdict(list)
@@ -149,8 +153,12 @@
             if name.startswith(paramsname):
                 testnameroot = 'test_' + name[len(paramsname):]
                 for paramname, params in paramsdict.items():
-                    test = (lambda self, name=name, params=params:
-                                    getattr(self, name)(*params))
+                    if hasattr(params, 'keys'):
+                        test = (lambda self, name=name, params=params:
+                                    getattr(self, name)(**params))
+                    else:
+                        test = (lambda self, name=name, params=params:
+                                        getattr(self, name)(*params))
                     testname = testnameroot + '_' + paramname
                     test.__name__ = testname
                     testfuncs[testname] = test
diff --git a/Lib/test/test_email/test_inversion.py b/Lib/test/test_email/test_inversion.py
--- a/Lib/test/test_email/test_inversion.py
+++ b/Lib/test/test_email/test_inversion.py
@@ -7,6 +7,7 @@
 import io
 import unittest
 from email import policy, message_from_bytes
+from email.message import EmailMessage
 from email.generator import BytesGenerator
 from test.test_email import TestEmailBase, parameterize
 
@@ -23,7 +24,10 @@
 
 
 @parameterize
-class TestInversion(TestEmailBase, unittest.TestCase):
+class TestInversion(TestEmailBase):
+
+    policy = policy.default
+    message = EmailMessage
 
     def msg_as_input(self, msg):
         m = message_from_bytes(msg, policy=policy.SMTP)
@@ -44,6 +48,23 @@
 
             }
 
+    payload_params = {
+        'plain_text': dict(payload='This is a test\n'*20),
+        'base64_text': dict(payload=(('xy a'*40+'\n')*5), cte='base64'),
+        'qp_text': dict(payload=(('xy a'*40+'\n')*5), cte='quoted-printable'),
+        }
+
+    def payload_as_body(self, payload, **kw):
+        msg = self._make_message()
+        msg['From'] = 'foo'
+        msg['To'] = 'bar'
+        msg['Subject'] = 'payload round trip test'
+        msg.set_content(payload, **kw)
+        b = bytes(msg)
+        msg2 = message_from_bytes(b, policy=self.policy)
+        self.assertEqual(bytes(msg2), b)
+        self.assertEqual(msg2.get_content(), payload)
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -62,6 +62,9 @@
 Library
 -------
 
+- Issue #28047: Fixed calculation of line length used for the base64 CTE
+  in the new email policies.
+
 - Issue #27445: Don't pass str(_charset) to MIMEText.set_payload().
   Patch by Claude Paroz.
 

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list