Skip to content

Commit 6142a37

Browse files
beledouxdenissethmlarson
authored andcommitted
email: use same strategy as quoted-string to fold comments
- No forced space to indent, - use almost the same code as the `bare-quoted-string` block above, handle nested comments and escaped parenthesis in addition
1 parent 0b64fd5 commit 6142a37

File tree

2 files changed

+27
-69
lines changed

2 files changed

+27
-69
lines changed

Lib/email/_header_value_parser.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ def make_quoted_pairs(value):
101101
return str(value).replace('\\', '\\\\').replace('"', '\\"')
102102

103103

104+
def make_parenthesis_pairs(value):
105+
"""Escape parenthesis and backslash for use within a comment."""
106+
return str(value).replace('\\', '\\\\') \
107+
.replace('(', '\\(').replace(')', '\\)')
108+
109+
104110
def quote_string(value):
105111
escaped = make_quoted_pairs(value)
106112
return f'"{escaped}"'
@@ -2941,10 +2947,7 @@ def _refold_parse_tree(parse_tree, *, policy):
29412947
# the way encoded strings handle continuation lines, we need to
29422948
# be prepared to encode any whitespace if the next line turns
29432949
# out to start with an encoded word.
2944-
line = newline + tstr
2945-
if line[0] not in WSP:
2946-
line = ' ' + line
2947-
lines.append(line)
2950+
lines.append(newline + tstr)
29482951

29492952
whitespace_accumulator = []
29502953
for char in lines[-1]:
@@ -2966,6 +2969,13 @@ def _refold_parse_tree(parse_tree, *, policy):
29662969
[ValueTerminal(make_quoted_pairs(p), 'ptext')
29672970
for p in newparts] +
29682971
[ValueTerminal('"', 'ptext')])
2972+
if part.token_type == 'comment':
2973+
newparts = (
2974+
[ValueTerminal('(', 'ptext')] +
2975+
[ValueTerminal(make_parenthesis_pairs(p), 'ptext')
2976+
if p.token_type == 'ptext' else p
2977+
for p in newparts] +
2978+
[ValueTerminal(')', 'ptext')])
29692979
if not part.as_ew_allowed:
29702980
wrap_as_ew_blocked += 1
29712981
newparts.append(end_ew_not_allowed)
@@ -2980,10 +2990,7 @@ def _refold_parse_tree(parse_tree, *, policy):
29802990
# We can't figure out how to wrap, it, so give up.
29812991
newline = _steal_trailing_WSP_if_exists(lines)
29822992
if newline or part.startswith_fws():
2983-
line = newline + tstr
2984-
if line[0] not in WSP:
2985-
line = ' ' + line
2986-
lines.append(line)
2993+
lines.append(newline + tstr)
29872994
else:
29882995
# We can't fold it onto the next line either...
29892996
lines[-1] += tstr

Lib/test/test_email/test__header_value_parser.py

Lines changed: 12 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3298,67 +3298,18 @@ def test_address_list_with_long_unwrapable_comment(self):
32983298
policy = self.policy.clone(max_line_length=40)
32993299
cases = [
33003300
# (to, folded)
3301-
3302-
# 1. Unwrappable Comments
3303-
3304-
# Entire line is <= 40 characters, 40 characters exactly
3305-
# No folding
3306-
('spy@example.org(loremipsumdolorsitametc)', 'spy@example.org(loremipsumdolorsitametc)\n'),
3307-
('(loremipsumdolorsitametc)spy@example.org', '(loremipsumdolorsitametc)spy@example.org\n'),
3308-
# Entire line is > 40 characters, 41 characters
3309-
# Folding triggered
3310-
('spy@example.org(loremipsumdolorsitametco)','spy@example.org\n (loremipsumdolorsitametco)\n'),
3311-
('(loremipsumdolorsitametco)spy@example.org', '(loremipsumdolorsitametco)spy@example.org\n'),
3312-
# Entire line is > 40 characters, 54 characters, `len(tstr) <= maxlen - len(lines[-1])` is `True`
3313-
# Folding triggered
3314-
# Comment part < 40 characthers, 39 characters, `len(tstr) + 1 <= maxlen` is `True`
3315-
# No attempt to fold the subpart
3316-
('spy@example.org(loremipsumdolorsitametconsecteturadip)',
3317-
'spy@example.org\n'
3318-
' (loremipsumdolorsitametconsecteturadip)\n'),
3319-
('(loremipsumdolorsitametconsecteturadip)spy@example.org',
3320-
'(loremipsumdolorsitametconsecteturadip)spy@example.org\n'),
3321-
# Entire line is > 40 characters, 55 characters, `len(tstr) <= maxlen - len(lines[-1])` is `True`
3322-
# Folding triggered
3323-
# Comment part >= 40 characters, 40 characters exactly, `len(tstr) + 1 <= maxlen` is `False`
3324-
# Attempt to fold the subpart
3325-
('spy@example.org(loremipsumdolorsitametconsecteturadipi)',
3326-
'spy@example.org\n'
3327-
' (loremipsumdolorsitametconsecteturadipi)\n'),
3328-
('(loremipsumdolorsitametconsecteturadipi)spy@example.org',
3329-
'(loremipsumdolorsitametconsecteturadipi)spy@example.org\n'),
3330-
3331-
# 2. Wrappable comments
3332-
3333-
# Entire line is <= 40 characters, 40 characters exactly
3334-
# No folding
3335-
('spy@example.org(loremipsumd olorsitamet)', 'spy@example.org(loremipsumd olorsitamet)\n'),
3336-
('(loremipsumd olorsitamet)spy@example.org', '(loremipsumd olorsitamet)spy@example.org\n'),
3337-
# Entire line is > 40 characters, 41 characters
3338-
# Folding triggered
3339-
# Comment part < 40 characters
3340-
('spy@example.org(loremipsumd olorsitametc)', 'spy@example.org\n (loremipsumd olorsitametc)\n'),
3341-
('(loremipsumd olorsitametc)spy@example.org', '(loremipsumd olorsitametc)spy@example.org\n'),
3342-
# Entire line is > 40 characters, 56 characters
3343-
# Folding triggered
3344-
# Comment part > 40 characters, 41 characters
3345-
('spy@example.org(loremipsumd loremipsumdolorsitametconse)', 'spy@example.org(loremipsumd\n loremipsumdolorsitametconse)\n'),
3346-
('(loremipsumd loremipsumdolorsitametconse)spy@example.org', '(loremipsumd\n loremipsumdolorsitametconse)spy@example.org\n'),
3347-
# Entire line is > 40 characters, 70 characters
3348-
# Folding triggered
3349-
# Comment part > 40 characters, 55 characters
3350-
# One word in the comment > 40 characters, 41 characters
3351-
('spy@example.org(loremipsumd loremipsumdolorsitametconsecteturadipisci)', 'spy@example.org(loremipsumd\n loremipsumdolorsitametconsecteturadipisci)\n'),
3352-
('(loremipsumd loremipsumdolorsitametconsecteturadipisci)spy@example.org', '(loremipsumd\n loremipsumdolorsitametconsecteturadipisci)spy@example.org\n'),
3353-
3354-
# 3. Nested comments
3355-
3356-
('spy@example.org((loremipsumdolorsitametconsecteturadi))', 'spy@example.org(\n (loremipsumdolorsitametconsecteturadi))\n'),
3357-
('spy@example.org((loremipsumdolorsitametconsecteturadip))', 'spy@example.org(\n (loremipsumdolorsitametconsecteturadip)\n )\n'),
3358-
('spy@example.org((loremipsumdolorsitam)(loremipsumdolorsitam))', 'spy@example.org((loremipsumdolorsitam)\n (loremipsumdolorsitam))\n'),
3359-
('spy@example.org((loremipsumdolorsitametc)(loremipsumdolorsitametc))', 'spy@example.org(\n (loremipsumdolorsitametc)\n (loremipsumdolorsitametc))\n'),
3360-
('spy@example.org(loremipsumdolorsitametc(loremipsumdolorsitametc))', 'spy@example.org(loremipsumdolorsitametc\n (loremipsumdolorsitametc))\n'),
3361-
('spy@example.org((loremipsumdolorsitametc)loremipsumdolorsitametc)', 'spy@example.org(\n (loremipsumdolorsitametc)\n loremipsumdolorsitametc)\n'),
3301+
('(loremipsumdolorsitametconsecteturadipi)<spy@example.org>',
3302+
'(loremipsumdolorsitametconsecteturadipi)<spy@example.org>\n'),
3303+
('<spy@example.org>(loremipsumdolorsitametconsecteturadipi)',
3304+
'<spy@example.org>(loremipsumdolorsitametconsecteturadipi)\n'),
3305+
('(loremipsum dolorsitametconsecteturadipi)<spy@example.org>',
3306+
'(loremipsum dolorsitametconsecteturadipi)<spy@example.org>\n'),
3307+
('<spy@example.org>(loremipsum dolorsitametconsecteturadipi)',
3308+
'<spy@example.org>(loremipsum\n dolorsitametconsecteturadipi)\n'),
3309+
('(Escaped \\( \\) chars \\\\ in comments stay escaped)<spy@example.org>',
3310+
'(Escaped \\( \\) chars \\\\ in comments stay\n escaped)<spy@example.org>\n'),
3311+
('((loremipsum)(loremipsum)(loremipsum)(loremipsum))<spy@example.org>',
3312+
'((loremipsum)(loremipsum)(loremipsum)(loremipsum))<spy@example.org>\n'),
33623313
]
33633314
for (to, folded) in cases:
33643315
with self.subTest(to=to):

0 commit comments

Comments
 (0)