From 94a7927cc6ad7c2fa986cc207f1f655eb017fa18 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Fri, 9 Sep 2016 15:00:09 -0400 Subject: #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. --- 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 index d363652..b98ce27 100644 --- a/Lib/email/contentmanager.py +++ b/Lib/email/contentmanager.py @@ -126,12 +126,13 @@ def _finalize_set(msg, disposition, filename, cid, params): 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 index d2f7d31..d4a03ee 100644 --- a/Lib/test/test_email/__init__.py +++ b/Lib/test/test_email/__init__.py @@ -121,6 +121,10 @@ def parameterize(cls): 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 @@ def parameterize(cls): 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 index f36e33d..8e8d676 100644 --- a/Lib/test/test_email/test_inversion.py +++ b/Lib/test/test_email/test_inversion.py @@ -7,6 +7,7 @@ producing RFC valid messages. 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 @@ def dedent(bstr): @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 @@ class TestInversion(TestEmailBase, unittest.TestCase): } + 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 index 1922432..86d499b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -62,6 +62,9 @@ Core and Builtins 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. -- cgit v0.12