Skip to content

Commit 7e1bac6

Browse files
[3.14] pythongh-135307: Fix email error when policy max_line_length is set to 0 or None (pythonGH-135367) (python#140915)
pythongh-135307: Fix email error when policy max_line_length is set to 0 or None (pythonGH-135367) (cherry picked from commit 6d45cd8) RDM: Like the change made in a earlier PR to the folder, we can/must use 'maxlen' as a stand in for 'unlimited' when computing line lengths when max_line_length is 0 or None; otherwise the computation results in a traceback. Co-authored-by: Jiucheng(Oliver) <[email protected]>
1 parent 6ab4dfb commit 7e1bac6

File tree

3 files changed

+35
-5
lines changed

3 files changed

+35
-5
lines changed

Lib/email/contentmanager.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import email.charset
33
import email.message
44
import email.errors
5+
import sys
56
from email import quoprimime
67

78
class ContentManager:
@@ -142,22 +143,23 @@ def _encode_base64(data, max_line_length):
142143

143144

144145
def _encode_text(string, charset, cte, policy):
146+
# If max_line_length is 0 or None, there is no limit.
147+
maxlen = policy.max_line_length or sys.maxsize
145148
lines = string.encode(charset).splitlines()
146149
linesep = policy.linesep.encode('ascii')
147150
def embedded_body(lines): return linesep.join(lines) + linesep
148151
def normal_body(lines): return b'\n'.join(lines) + b'\n'
149152
if cte is None:
150153
# Use heuristics to decide on the "best" encoding.
151-
if max((len(x) for x in lines), default=0) <= policy.max_line_length:
154+
if max(map(len, lines), default=0) <= maxlen:
152155
try:
153156
return '7bit', normal_body(lines).decode('ascii')
154157
except UnicodeDecodeError:
155158
pass
156159
if policy.cte_type == '8bit':
157160
return '8bit', normal_body(lines).decode('ascii', 'surrogateescape')
158161
sniff = embedded_body(lines[:10])
159-
sniff_qp = quoprimime.body_encode(sniff.decode('latin-1'),
160-
policy.max_line_length)
162+
sniff_qp = quoprimime.body_encode(sniff.decode('latin-1'), maxlen)
161163
sniff_base64 = binascii.b2a_base64(sniff)
162164
# This is a little unfair to qp; it includes lineseps, base64 doesn't.
163165
if len(sniff_qp) > len(sniff_base64):
@@ -172,9 +174,9 @@ def normal_body(lines): return b'\n'.join(lines) + b'\n'
172174
data = normal_body(lines).decode('ascii', 'surrogateescape')
173175
elif cte == 'quoted-printable':
174176
data = quoprimime.body_encode(normal_body(lines).decode('latin-1'),
175-
policy.max_line_length)
177+
maxlen)
176178
elif cte == 'base64':
177-
data = _encode_base64(embedded_body(lines), policy.max_line_length)
179+
data = _encode_base64(embedded_body(lines), maxlen)
178180
else:
179181
raise ValueError("Unknown content transfer encoding {}".format(cte))
180182
return cte, data

Lib/test/test_email/test_message.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,32 @@ def test_folding_with_long_nospace_http_policy_1(self):
10041004
parsed_msg = message_from_bytes(m.as_bytes(), policy=policy.default)
10051005
self.assertEqual(parsed_msg['Message-ID'], m['Message-ID'])
10061006

1007+
def test_no_wrapping_max_line_length(self):
1008+
# Test that falsey 'max_line_length' are converted to sys.maxsize.
1009+
for n in [0, None]:
1010+
with self.subTest(max_line_length=n):
1011+
self.do_test_no_wrapping_max_line_length(n)
1012+
1013+
def do_test_no_wrapping_max_line_length(self, falsey):
1014+
self.assertFalse(falsey)
1015+
pol = policy.default.clone(max_line_length=falsey)
1016+
subj = "S" * 100
1017+
body = "B" * 100
1018+
msg = EmailMessage(policy=pol)
1019+
msg["From"] = "[email protected]"
1020+
msg["To"] = "[email protected]"
1021+
msg["Subject"] = subj
1022+
msg.set_content(body)
1023+
1024+
raw = msg.as_bytes()
1025+
self.assertNotIn(b"=\n", raw,
1026+
"Found fold indicator; wrapping not disabled")
1027+
1028+
parsed = message_from_bytes(raw, policy=policy.default)
1029+
self.assertEqual(parsed["Subject"], subj)
1030+
parsed_body = parsed.get_body().get_content().rstrip('\n')
1031+
self.assertEqual(parsed_body, body)
1032+
10071033
def test_invalid_header_names(self):
10081034
invalid_headers = [
10091035
('Invalid Header', 'contains space'),
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:mod:`email`: Fix exception in ``set_content()`` when encoding text
2+
and max_line_length is set to ``0`` or ``None`` (unlimited).

0 commit comments

Comments
 (0)