You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PS: The issue's versions field is filled with "Python 2.4". Shouldn't it
be "Python 2.5" ?
paulproteus commented on Sep 27, 2008
paulproteusmannequin
Mannequin
More actions
Another way to see this issue is that the email module double-encodes
when one attempts to use quoted-printable encoding. This has to be
worked around by e.g. MoinMoin.
It's easy to get proper base64-encoded output of email.mime.text:
>>> email.encoders.encode_quopri(mt)
>>> 'Content-Transfer-Encoding: quoted-printable' in mt.as_string()
True
Great! Except it's actually double-encoded, and the headers admit to as
much. You see here that, in addition to the quoted-printable header
just discovered, there is also a base64-related header, and the result
is not strictly QP encoding but QP(base64(payload)).
>>> 'Content-Transfer-Encoding: base64' in mt.as_string()
True
>>> mt.as_string().split('\n')[-2]
'VGEgbcOocmU=3D'
If we can come to a decision as to how this *should* work, I could
contribute a patch and/or tests to fix it. I could even perhaps write a
new section of the Python documentation of the email module explaining this.
So the encoded text looks good now, but there are still duplicate headers.
Old output (python2.7) is identical to what Asheesh Laroia (paulproteus)
reported for python2.5:
---
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Transfer-Encoding: quoted-printable
One piece of this is still broken at main: the duplicate-header half. email.encoders.encode_base64/encode_quopri/encode_7or8bit all append Content-Transfer-Encoding instead of replacing it, so calling any of them on a message that already carries one — MIMEText sets it by default — produces two Content-Transfer-Encoding: headers in the output. The get_payload(decode=True) change linked above did fix the payload half (that landed back in bpo-18324), but I still get two CTE headers at 3.16 on a MIMEText payload, so the duplicate header is what's left. The fix is one line per encoder — del msg['Content-Transfer-Encoding'] before the assignment (del on a missing header is a no-op, so the normal path is unchanged). I can send the patch with tests.
Activity
ThomasAH commentedon Jul 20, 2006
Gives
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
=3D
With the email package from python2.4.3 and 2.4.4c0 the
last '=3D' becomes just '=', so an extra
msg.body_encode(text) is needed.
ThomasAH commentedon Jul 20, 2006
Logged In: YES
user_id=839582
One program which got hit by this is MoinMoin, see
http://moinmoin.wikiwikiweb.de/MoinMoinBugs/ResetPasswordEmailImproperlyEncoded
rdemetrescu commentedon Dec 6, 2007
I am not sure if it is related, but anyway...
MIMEText behaviour has changed from python 2.4 to 2.5.
# Python 2.4
abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc=
abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc ab=
c abc abc abc abc abc abc abc abc abc abc abc abc=20
# Python 2.5
abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc
abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc
abc abc abc abc abc abc abc abc abc abc abc abc abc abc
However, if we initialize MIMEText with the text, we get the correct output:
# python 2.5
abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc=
abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc ab=
c abc abc abc abc abc abc abc abc abc abc abc abc=20
If I want to set payload after MIMEText is already created, I need to
use this workaround::
PS: The issue's versions field is filled with "Python 2.4". Shouldn't it
be "Python 2.5" ?
paulproteus commentedon Sep 27, 2008
Another way to see this issue is that the email module double-encodes
when one attempts to use quoted-printable encoding. This has to be
worked around by e.g. MoinMoin.
It's easy to get proper base64-encoded output of email.mime.text:
>>> mt = email.mime.text.MIMEText('Ta mère', 'plain', 'utf-8') >>> 'Content-Transfer-Encoding: base64' in mt.as_string() True >>> mt.as_string().split('\n')[-2] 'VGEgbcOocmU='There we go, all nice and base64'd.
I can *not* figure out how to get quoted-printable-encoding. I found
http://docs.python.org/lib/module-email.encoders.html , so I thought
great - I'll just encode my MIMEText object:
Great! Except it's actually double-encoded, and the headers admit to as
much. You see here that, in addition to the quoted-printable header
just discovered, there is also a base64-related header, and the result
is not strictly QP encoding but QP(base64(payload)).
>>> 'Content-Transfer-Encoding: base64' in mt.as_string() True >>> mt.as_string().split('\n')[-2] 'VGEgbcOocmU=3D'It should look like:
>>> quopri.encodestring('Ta mère') 'Ta m=C3=A8re'I raised this issue on the Baypiggies list
<http://mail.python.org/pipermail/baypiggies/2008-September/003983.html\>, but
luckily I found this here bug. This is with Python 2.5.2-0ubuntu1 from
Ubuntu 8.04.
If we can come to a decision as to how this *should* work, I could
contribute a patch and/or tests to fix it. I could even perhaps write a
new section of the Python documentation of the email module explaining this.
ThomasAH commentedon May 5, 2010
Roger Demetrescu, I filed the issue with "Python 2.4", because the behavior changed somewhere between 2.4.2 and 2.4.3
The updated link to the MoinMoin bug entry is:
http://moinmo.in/MoinMoinBugs/ResetPasswordEmailImproperlyEncoded
The workaround I use to be compatible with <= 2.4.2 and >= 2.4.3 is:
18 remaining items
python-dev commentedon Mar 19, 2013
New changeset ba500b179c3a by R David Murray in branch '3.2':
bpo-1525919: Document MIMEText+set_payload encoding behavior.
http://hg.python.org/cpython/rev/ba500b179c3a
New changeset fcbc28ef96a3 by R David Murray in branch '3.3':
Merge: bpo-1525919: Document MIMEText+set_payload encoding behavior.
http://hg.python.org/cpython/rev/fcbc28ef96a3
New changeset b9e07f20832e by R David Murray in branch 'default':
Merge: bpo-1525919: Document MIMEText+set_payload encoding behavior.
http://hg.python.org/cpython/rev/b9e07f20832e
bitdancer commentedon Mar 19, 2013
I've committed the doc change. I'm going to be lazy and leave this issue open to deal with the encodings module fix.
[-]email package quoted printable behaviour changed[/-][+]email package content-transfer-encoding behaviour changed[/+][-]email package quoted printable behaviour changed[/-][+]email package content-transfer-encoding behaviour changed[/+]iritkatriel commentedon Dec 12, 2021
The encoding functions are now doing
Does this fix the double-encoding issue?
This change was made in
00ae435
ThomasAH commentedon Dec 13, 2021
VGEgbcOocmU=
>>> email.encoders.encode_quopri(mt) >>> print(mt.as_string()) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Content-Transfer-Encoding: quoted-printableSo the encoded text looks good now, but there are still duplicate headers.
Old output (python2.7) is identical to what Asheesh Laroia (paulproteus)
reported for python2.5:
---
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Transfer-Encoding: quoted-printable
soreavis commentedon Jul 19, 2026
One piece of this is still broken at main: the duplicate-header half.
email.encoders.encode_base64/encode_quopri/encode_7or8bitall appendContent-Transfer-Encodinginstead of replacing it, so calling any of them on a message that already carries one —MIMETextsets it by default — produces twoContent-Transfer-Encoding:headers in the output. Theget_payload(decode=True)change linked above did fix the payload half (that landed back in bpo-18324), but I still get two CTE headers at 3.16 on aMIMETextpayload, so the duplicate header is what's left. The fix is one line per encoder —del msg['Content-Transfer-Encoding']before the assignment (delon a missing header is a no-op, so the normal path is unchanged). I can send the patch with tests.Reproducer at current main (6df4993)