envelopes
envelopes copied to clipboard
Fails when attaching HTML file
I am trying to attach an HTML file to an email.
I get the following error:
/usr/local/lib/python2.7/email/message.pyc in get(self, name, failobj)
358 is missing.
359 """
--> 360 name = name.lower()
361 for k, v in self._headers:
362 if k.lower() == name:
AttributeError: 'slice' object has no attribute 'lower'
Stepping into the code with ipython debugger it seems that it detects the MIME type of the file, correctly, as text/html
but this causes it to follow a different codepath which is where it fails: https://github.com/tomekwojcik/envelopes/blob/master/envelopes/envelope.py#L292
If I force it to use a generic mime type instead:
envelope.add_attachment("myfile.html", mimetype="application/octet-stream")
...then I can send the message but something weird happens - Gmail shows the message as having the file attached twice, even though if I go back and check the envelope
object it seems I have only one attachment:
In [18]: envelope._parts
Out[18]:
[('text/plain', 'my body text', 'utf-8'),
('application/octet-stream',
<email.mime.base.MIMEBase instance at 0x270cd40>)]
the 'double' attachments showing up in Gmail seems to be a bug in Gmail ... the message source appears to be correct :)
The problem is that the code makes a wrong assumption in line 291 of envelope.py (https://github.com/tomekwojcik/envelopes/blob/master/envelopes/envelope.py#L291).
for part in self._parts:
type_maj, type_min = part[0].split('/')
# Small correction. See thakkarparth007's comment on:
# https://github.com/tomekwojcik/envelopes/issues/18
if type_maj == 'text' and type_min in ('html', 'plain'):
msg.attach(MIMEText(part[1], type_min, self._charset))
else:
msg.attach(part[1])
self._parts
contains the (mimetype, part, charset) tuples for the "main"
content - the text/html part and the text/plain part. Here, part
is a
(unicode?) string. But, it also contains the attachments - the (mimetype, part)
tuples which are added in the add_attachment()
function. Here, the part
is a MIMEBase object. Now, consider what happens when you attach a text/html
or text/plain file - you'll be appending a ('text/html',MIMEBase) tuple
to the self._parts
list. Now, in the above loop, the condition is satisfied
by this text-ish file. So, you try to create a MIMEText object with the first
argument being a MIMEBase instead of a (unicode?) string. This causes all the
trouble.
A simple (but maybe hackish) solution is to add another condition and make that if statement look something like this:
if type_maj == 'text' and type_min in ('html', 'plain') and\
(isinstance(part[1], str) or isinstance(part[1], unicode)):
msg.attach(MIMEText(part[1], type_min, self._charset))
else:
msg.attach(part[1])
This will work (at least for now).