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-
('[email protected](loremipsumdolorsitametc)', '[email protected](loremipsumdolorsitametc)\n'),
3307-
('(loremipsumdolorsitametc)[email protected]', '(loremipsumdolorsitametc)[email protected]\n'),
3308-
# Entire line is > 40 characters, 41 characters
3309-
# Folding triggered
3310-
('[email protected](loremipsumdolorsitametco)','[email protected]\n (loremipsumdolorsitametco)\n'),
3311-
('(loremipsumdolorsitametco)[email protected]', '(loremipsumdolorsitametco)[email protected]\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-
('[email protected](loremipsumdolorsitametconsecteturadip)',
3317-
3318-
' (loremipsumdolorsitametconsecteturadip)\n'),
3319-
('(loremipsumdolorsitametconsecteturadip)[email protected]',
3320-
'(loremipsumdolorsitametconsecteturadip)[email protected]\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-
('[email protected](loremipsumdolorsitametconsecteturadipi)',
3326-
3327-
' (loremipsumdolorsitametconsecteturadipi)\n'),
3328-
('(loremipsumdolorsitametconsecteturadipi)[email protected]',
3329-
'(loremipsumdolorsitametconsecteturadipi)[email protected]\n'),
3330-
3331-
# 2. Wrappable comments
3332-
3333-
# Entire line is <= 40 characters, 40 characters exactly
3334-
# No folding
3335-
('[email protected](loremipsumd olorsitamet)', '[email protected](loremipsumd olorsitamet)\n'),
3336-
('(loremipsumd olorsitamet)[email protected]', '(loremipsumd olorsitamet)[email protected]\n'),
3337-
# Entire line is > 40 characters, 41 characters
3338-
# Folding triggered
3339-
# Comment part < 40 characters
3340-
('[email protected](loremipsumd olorsitametc)', '[email protected]\n (loremipsumd olorsitametc)\n'),
3341-
('(loremipsumd olorsitametc)[email protected]', '(loremipsumd olorsitametc)[email protected]\n'),
3342-
# Entire line is > 40 characters, 56 characters
3343-
# Folding triggered
3344-
# Comment part > 40 characters, 41 characters
3345-
('[email protected](loremipsumd loremipsumdolorsitametconse)', '[email protected](loremipsumd\n loremipsumdolorsitametconse)\n'),
3346-
('(loremipsumd loremipsumdolorsitametconse)[email protected]', '(loremipsumd\n loremipsumdolorsitametconse)[email protected]\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-
('[email protected](loremipsumd loremipsumdolorsitametconsecteturadipisci)', '[email protected](loremipsumd\n loremipsumdolorsitametconsecteturadipisci)\n'),
3352-
('(loremipsumd loremipsumdolorsitametconsecteturadipisci)[email protected]', '(loremipsumd\n loremipsumdolorsitametconsecteturadipisci)[email protected]\n'),
3353-
3354-
# 3. Nested comments
3355-
3356-
('[email protected]((loremipsumdolorsitametconsecteturadi))', '[email protected](\n (loremipsumdolorsitametconsecteturadi))\n'),
3357-
('[email protected]((loremipsumdolorsitametconsecteturadip))', '[email protected](\n (loremipsumdolorsitametconsecteturadip)\n )\n'),
3358-
('[email protected]((loremipsumdolorsitam)(loremipsumdolorsitam))', '[email protected]((loremipsumdolorsitam)\n (loremipsumdolorsitam))\n'),
3359-
('[email protected]((loremipsumdolorsitametc)(loremipsumdolorsitametc))', '[email protected](\n (loremipsumdolorsitametc)\n (loremipsumdolorsitametc))\n'),
3360-
('[email protected](loremipsumdolorsitametc(loremipsumdolorsitametc))', '[email protected](loremipsumdolorsitametc\n (loremipsumdolorsitametc))\n'),
3361-
('[email protected]((loremipsumdolorsitametc)loremipsumdolorsitametc)', '[email protected](\n (loremipsumdolorsitametc)\n loremipsumdolorsitametc)\n'),
3301+
('(loremipsumdolorsitametconsecteturadipi)<[email protected]>',
3302+
'(loremipsumdolorsitametconsecteturadipi)<[email protected]>\n'),
3303+
('<[email protected]>(loremipsumdolorsitametconsecteturadipi)',
3304+
'<[email protected]>(loremipsumdolorsitametconsecteturadipi)\n'),
3305+
('(loremipsum dolorsitametconsecteturadipi)<[email protected]>',
3306+
'(loremipsum dolorsitametconsecteturadipi)<[email protected]>\n'),
3307+
('<[email protected]>(loremipsum dolorsitametconsecteturadipi)',
3308+
'<[email protected]>(loremipsum\n dolorsitametconsecteturadipi)\n'),
3309+
('(Escaped \\( \\) chars \\\\ in comments stay escaped)<[email protected]>',
3310+
'(Escaped \\( \\) chars \\\\ in comments stay\n escaped)<[email protected]>\n'),
3311+
('((loremipsum)(loremipsum)(loremipsum)(loremipsum))<[email protected]>',
3312+
'((loremipsum)(loremipsum)(loremipsum)(loremipsum))<[email protected]>\n'),
33623313
]
33633314
for (to, folded) in cases:
33643315
with self.subTest(to=to):

0 commit comments

Comments
 (0)