Opened 10 years ago
Closed 9 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 , 10 years ago
Triage Stage: | Unreviewed → Accepted |
---|
by , 10 years ago
Attachment: | 24623-test.diff added |
---|
comment:2 by , 10 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:3 by , 10 years ago
Owner: | removed |
---|---|
Status: | assigned → new |
by , 9 years ago
Attachment: | 24623_workaround_1.diff added |
---|
comment:4 by , 9 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 a 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 , 9 years ago
Would you mind creating a pull request with your patch, including the test from Tim?
comment:7 by , 9 years ago
Has patch: | set |
---|
comment:8 by , 9 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
comment:9 by , 9 years ago
Patch needs improvement: | set |
---|---|
Triage Stage: | Ready for checkin → Accepted |
comment:10 by , 9 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 - should it be handled silently if possible)?
comment:11 by , 9 years ago
Patch needs improvement: | unset |
---|
comment:12 by , 9 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).