@@ -2766,10 +2766,13 @@ def _refold_parse_tree(parse_tree, *, policy):
2766
2766
# max_line_length 0/None means no limit, ie: infinitely long.
2767
2767
maxlen = policy .max_line_length or sys .maxsize
2768
2768
encoding = 'utf-8' if policy .utf8 else 'us-ascii'
2769
- lines = ['' ]
2770
- last_ew = None
2769
+ lines = ['' ] # Folded lines to be output
2770
+ prepend_whitespace = '' # When we have whitespace between two encoded
2771
+ # words, we may need to encode the whitespace
2772
+ last_ew = None # Points to the last encoded character if there's an ew on
2773
+ # the line
2771
2774
wrap_as_ew_blocked = 0
2772
- want_encoding = False
2775
+ want_encoding = False # True if we need to encode this part
2773
2776
end_ew_not_allowed = Terminal ('' , 'wrap_as_ew_blocked' )
2774
2777
parts = list (parse_tree )
2775
2778
while parts :
@@ -2793,10 +2796,12 @@ def _refold_parse_tree(parse_tree, *, policy):
2793
2796
# 'charset' property on the policy.
2794
2797
charset = 'utf-8'
2795
2798
want_encoding = True
2799
+
2796
2800
if part .token_type == 'mime-parameters' :
2797
2801
# Mime parameter folding (using RFC2231) is extra special.
2798
2802
_fold_mime_parameters (part , lines , maxlen , encoding )
2799
2803
continue
2804
+
2800
2805
if want_encoding and not wrap_as_ew_blocked :
2801
2806
if not part .as_ew_allowed :
2802
2807
want_encoding = False
@@ -2823,20 +2828,24 @@ def _refold_parse_tree(parse_tree, *, policy):
2823
2828
# It's a terminal, wrap it as an encoded word, possibly
2824
2829
# combining it with previously encoded words if allowed.
2825
2830
last_ew = _fold_as_ew (tstr , lines , maxlen , last_ew ,
2826
- part .ew_combine_allowed , charset )
2831
+ part .ew_combine_allowed , charset , prepend_whitespace )
2832
+ prepend_whitespace = ''
2827
2833
want_encoding = False
2828
2834
continue
2835
+
2829
2836
if len (tstr ) <= maxlen - len (lines [- 1 ]):
2830
2837
lines [- 1 ] += tstr
2831
2838
continue
2832
2839
# This part is too long to fit. The RFC wants us to break at
2833
2840
# "major syntactic breaks", so unless we don't consider this
2834
2841
# to be one, check if it will fit on the next line by itself.
2842
+ prepend_whitespace = ''
2835
2843
if (part .syntactic_break and
2836
2844
len (tstr ) + 1 <= maxlen ):
2837
2845
newline = _steal_trailing_WSP_if_exists (lines )
2838
2846
if newline or part .startswith_fws ():
2839
2847
lines .append (newline + tstr )
2848
+ prepend_whitespace = ' ' # part.value
2840
2849
last_ew = None
2841
2850
continue
2842
2851
if not hasattr (part , 'encode' ):
@@ -2860,9 +2869,10 @@ def _refold_parse_tree(parse_tree, *, policy):
2860
2869
else :
2861
2870
# We can't fold it onto the next line either...
2862
2871
lines [- 1 ] += tstr
2872
+
2863
2873
return policy .linesep .join (lines ) + policy .linesep
2864
2874
2865
- def _fold_as_ew (to_encode , lines , maxlen , last_ew , ew_combine_allowed , charset ):
2875
+ def _fold_as_ew (to_encode , lines , maxlen , last_ew , ew_combine_allowed , charset , prepend_whitespace ):
2866
2876
"""Fold string to_encode into lines as encoded word, combining if allowed.
2867
2877
Return the new value for last_ew, or None if ew_combine_allowed is False.
2868
2878
@@ -2877,14 +2887,15 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset):
2877
2887
to_encode = str (
2878
2888
get_unstructured (lines [- 1 ][last_ew :] + to_encode ))
2879
2889
lines [- 1 ] = lines [- 1 ][:last_ew ]
2880
- if to_encode [0 ] in WSP :
2890
+ elif to_encode [0 ] in WSP :
2881
2891
# We're joining this to non-encoded text, so don't encode
2882
2892
# the leading blank.
2883
2893
leading_wsp = to_encode [0 ]
2884
2894
to_encode = to_encode [1 :]
2885
2895
if (len (lines [- 1 ]) == maxlen ):
2886
2896
lines .append (_steal_trailing_WSP_if_exists (lines ))
2887
2897
lines [- 1 ] += leading_wsp
2898
+
2888
2899
trailing_wsp = ''
2889
2900
if to_encode [- 1 ] in WSP :
2890
2901
# Likewise for the trailing space.
@@ -2904,11 +2915,20 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset):
2904
2915
2905
2916
while to_encode :
2906
2917
remaining_space = maxlen - len (lines [- 1 ])
2907
- text_space = remaining_space - chrome_len
2918
+ text_space = remaining_space - chrome_len - len ( prepend_whitespace )
2908
2919
if text_space <= 0 :
2909
2920
lines .append (' ' )
2910
2921
continue
2911
2922
2923
+ # If we are at the start of a continuation line, prepend whitespace
2924
+ # (we only want to do this when the line starts with an encoded word
2925
+ # but if we're folding in this helper function, then we know that we
2926
+ # are going to be writing out an encoded word.)
2927
+ if len (lines ) > 1 and len (lines [- 1 ]) == 1 and prepend_whitespace :
2928
+ encoded_word = _ew .encode (prepend_whitespace , charset = encode_as )
2929
+ lines [- 1 ] += encoded_word
2930
+ prepend_whitespace = ''
2931
+
2912
2932
to_encode_word = to_encode [:text_space ]
2913
2933
encoded_word = _ew .encode (to_encode_word , charset = encode_as )
2914
2934
excess = len (encoded_word ) - remaining_space
0 commit comments