Opened 2 months ago

Closed 2 months ago

#36478 closed Bug (fixed)

EmailMessage handles utf-8 encoded text attachments differently in constructor vs property

Reported by: Mike Edmunds Owned by: Mike Edmunds
Component: Core (Mail) Version: 5.2
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Mike Edmunds)

Encoded text attachments set directly in the EmailMessage.attachments property are not processed like the same content passed to EmailMessage(attachments=...) or EmailMessage.attach(...).

#27007 added EmailMessage.attach() handling for text content encoded as utf-8 bytes. That code is also used for the attachments constructor argument, but not for attachments set directly on the object property:

    # Existing test: passes
    def test_attach_text_as_bytes(self):
        msg = EmailMessage()
        msg.attach("file.txt", b"file content\n")
        filename, content, mimetype = self.get_decoded_attachments(msg)[0]
        self.assertEqual(filename, "file.txt")
        self.assertEqual(content, "file content\n")
        self.assertEqual(mimetype, "text/plain")

    # New test: passes
    def test_attach_text_as_bytes_using_constructor(self):
        msg = EmailMessage(
            attachments=[EmailAttachment("file.txt", b"file content\n", "text/plain")]
        )
        filename, content, mimetype = self.get_decoded_attachments(msg)[0]
        self.assertEqual(filename, "file.txt")
        self.assertEqual(content, "file content\n")
        self.assertEqual(mimetype, "text/plain")

    # New test: AttributeError: 'bytes' object has no attribute 'encode'
    def test_attach_text_as_bytes_using_property(self):
        msg = EmailMessage()
        msg.attachments = [EmailAttachment("file.txt", b"file content\n", "text/plain")]
        filename, content, mimetype = self.get_decoded_attachments(msg)[0]
        self.assertEqual(filename, "file.txt")
        self.assertEqual(content, "file content\n")
        self.assertEqual(mimetype, "text/plain")

The second added test fails with AttributeError: 'bytes' object has no attribute 'encode deep in SafeMIMEText.

The EmailMessage documentation implies that the second and third tests should be equivalent.

(#27007 also added a fallback to application/octet-stream for non-utf-8 text content, but didn't include a test case for that behavior.)

See also https://github.com/django/django/pull/18966#discussion_r2154081996

Change History (4)

comment:1 by Mike Edmunds, 2 months ago

Description: modified (diff)

comment:2 by Sarah Boyce, 2 months ago

Triage Stage: UnreviewedAccepted

comment:3 by Sarah Boyce, 2 months ago

Triage Stage: AcceptedReady for checkin

comment:4 by Sarah Boyce <42296566+sarahboyce@…>, 2 months ago

Resolution: fixed
Status: assignedclosed

In 23529b66:

Fixed #36478 -- Fixed inconsistent mail attachment handling.

Fixed an inconsistency between EmailMessage.attach() and .attachments
when attaching bytes content with a text/* mimetype. The attach()
function decodes UTF-8 bytes if possible and otherwise changes the
mimetype to application/octet-stream to preserve the content's unknown
encoding (refs #27007). Providing equivalent content directly in
EmailMessage.attachments did not apply the same logic, leading
to an "AttributeError: 'bytes' object has no attribute 'encode'"
in SafeMIMEText.set_payload().

Updated EmailMessage._create_mime_attachment() to match attach()'s
handling for text/* mimetypes with bytes content. Updated test cases
to accurately cover behavior on both paths.

Note: See TracTickets for help on using tickets.
Back to Top