Opened 11 years ago
Closed 10 years ago
#24623 closed Bug (fixed)
AttributeError when trying to send an utf-8 encoded email with text attachments (mime type: text/*)
| Reported by: | tkrapp | Owned by: | Konrad Świat |
|---|---|---|---|
| Component: | Core (Mail) | Version: | 1.7 |
| Severity: | Normal | Keywords: | email python3 SafeMIMEText MIMEText attach_file |
| Cc: | Triage Stage: | Accepted | |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
I get "AttributeError: 'bytes' object has no attribute 'encode'" when trying to send a utf-8 encoded email with text attachments (see stack trace below).
As far as I can see this happens when using python 3 (like we do) and utf-8 as encoding.
- If you attach a text/csv file for example, django tries to instantiate a SafeMIMEText object with the following parameters: (text: bytes(...), subtype: 'csv', charset: 'utf-8') (django/core/mail/message.py:338)
- SafeMIMEText calls init of MIMEText and sets the charset to None explicitly when charset was 'utf-8' before but does not convert text from bytes to str which is expected by MIMEText (django/core/mail/message.py:175)
- MIMEText then tries to encode the provided _text parameter as 'us-ascii' and fails since a bytes object has no encode-method in python3 (email/mime/text.py:33)
Example
from django.core.mail import EmailMessage
email = EmailMessage('subject', 'body', 'from@example.com', ['to@example.com'])
email.attach_file('/path/to/csv.file')
email.send()
Solutions I could think of:
- Convert text parameter of SafeMIMEText from bytes to str before calling init of MIMEText. This would imply that one knows the encoding of the text file or the file encoding is detected automatically.
- Convert the file content and call attach of EmailMessage.
Stack trace:
Traceback (most recent call last):
File "./manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "/usr/local/lib/python3.4/dist-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python3.4/dist-packages/django/core/management/__init__.py", line 377, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python3.4/dist-packages/django/core/management/base.py", line 288, in run_from_argv
self.execute(*args, **options.__dict__)
File "/usr/local/lib/python3.4/dist-packages/django/core/management/base.py", line 338, in execute
output = self.handle(*args, **options)
File "/mnt/storage/django/code/trouble_ticket/management/commands/send2ntt.py", line 41, in handle
raise(e)
File "/mnt/storage/django/code/trouble_ticket/management/commands/send2ntt.py", line 39, in handle
email.send()
File "/usr/local/lib/python3.4/dist-packages/django/core/mail/message.py", line 286, in send
return self.get_connection(fail_silently).send_messages([self])
File "/usr/local/lib/python3.4/dist-packages/django/core/mail/backends/smtp.py", line 99, in send_messages
sent = self._send(message)
File "/usr/local/lib/python3.4/dist-packages/django/core/mail/backends/smtp.py", line 113, in _send
message = email_message.message()
File "/usr/local/lib/python3.4/dist-packages/django/core/mail/message.py", line 253, in message
msg = self._create_message(msg)
File "/usr/local/lib/python3.4/dist-packages/django/core/mail/message.py", line 312, in _create_message
return self._create_attachments(msg)
File "/usr/local/lib/python3.4/dist-packages/django/core/mail/message.py", line 325, in _create_attachments
msg.attach(self._create_attachment(*attachment))
File "/usr/local/lib/python3.4/dist-packages/django/core/mail/message.py", line 367, in _create_attachment
attachment = self._create_mime_attachment(content, mimetype)
File "/usr/local/lib/python3.4/dist-packages/django/core/mail/message.py", line 338, in _create_mime_attachment
attachment = SafeMIMEText(content, subtype, encoding)
File "/usr/local/lib/python3.4/dist-packages/django/core/mail/message.py", line 175, in __init__
MIMEText.__init__(self, text, subtype, None)
File "/usr/lib/python3.4/email/mime/text.py", line 33, in __init__
_text.encode('us-ascii')
AttributeError: 'bytes' object has no attribute 'encode'
Attachments (2)
Change History (15)
comment:1 by , 11 years ago
| Triage Stage: | Unreviewed → Accepted |
|---|
by , 11 years ago
| Attachment: | 24623-test.diff added |
|---|
comment:2 by , 11 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
comment:3 by , 11 years ago
| Owner: | removed |
|---|---|
| Status: | assigned → new |
by , 10 years ago
| Attachment: | 24623_workaround_1.diff added |
|---|
comment:4 by , 10 years ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
https://docs.python.org/3/library/functions.html#open
Files opened in binary mode (including 'b' in the mode argument) return contents as bytes objects without any decoding.
EmailMessage.attach_file() opens an attachment file with 'rb' mode. which results in a bytes object content. In case of a text base mimetype, content is beeing sent to python's MimeText, and then encode() fails on bytes object.
I attached a simple workaround patch.
comment:5 by , 10 years ago
Would you mind creating a pull request with your patch, including the test from Tim?
comment:7 by , 10 years ago
| Has patch: | set |
|---|
comment:8 by , 10 years ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
comment:9 by , 10 years ago
| Patch needs improvement: | set |
|---|---|
| Triage Stage: | Ready for checkin → Accepted |
comment:10 by , 10 years ago
PR updated.
edit:
There are some test cases included, that maybe need a discussion - what should we allow for (e.g invalid binary mimetype for a text file)?
comment:11 by , 10 years ago
| Patch needs improvement: | unset |
|---|
comment:12 by , 10 years ago
@kswiat - you've got some tests in there already, are there any more edgecases that you can think of? It doesn't hurt to have more.
Reproduced with the attached test (
EmailMessage.attach_file()is untested at the moment).