3
0
Fork 0

feat: add minimal attachments support for email messages

as of now, caller must provide a fully proper MIME attachment.  later
we will add some magic normalization logic so caller can just provide
attachment file paths
This commit is contained in:
Lance Edgar 2025-08-10 11:03:59 -05:00
parent 763dd510f6
commit 22d3ba97c9
2 changed files with 36 additions and 2 deletions

View file

@ -166,6 +166,10 @@ class Message:
.. attribute:: html_body
String with the ``text/html`` body content.
.. attribute:: attachments
List of file attachments for the message.
"""
def __init__(
@ -179,6 +183,7 @@ class Message:
replyto=None,
txt_body=None,
html_body=None,
attachments=None,
):
self.key = key
self.sender = sender
@ -189,6 +194,7 @@ class Message:
self.replyto = replyto
self.txt_body = txt_body
self.html_body = html_body
self.attachments = attachments or []
def set_recips(self, name, value):
""" """
@ -224,6 +230,13 @@ class Message:
if not msg:
raise ValueError("message has no body parts")
if self.attachments:
for attachment in self.attachments:
if isinstance(attachment, str):
raise ValueError("must specify valid MIME attachments; this class cannot "
"auto-create them from file path etc.")
msg = MIMEMultipart(_subtype='mixed', _subparts=[msg] + self.attachments)
msg['Subject'] = self.subject
msg['From'] = self.sender

View file

@ -1,5 +1,6 @@
# -*- coding: utf-8; -*-
from email.mime.text import MIMEText
from unittest import TestCase
from unittest.mock import patch, MagicMock
@ -8,7 +9,7 @@ import pytest
from wuttjamaican import email as mod
from wuttjamaican.util import resource_path
from wuttjamaican.exc import ConfigurationError
from wuttjamaican.testing import ConfigTestCase
from wuttjamaican.testing import ConfigTestCase, FileTestCase
class TestEmailSetting(ConfigTestCase):
@ -24,7 +25,7 @@ class TestEmailSetting(ConfigTestCase):
self.assertEqual(setting.sample_data(), {})
class TestMessage(TestCase):
class TestMessage(FileTestCase):
def make_message(self, **kwargs):
return mod.Message(**kwargs)
@ -77,6 +78,26 @@ class TestMessage(TestCase):
complete = msg.as_string()
self.assertIn('From: bob@example.com', complete)
# html + attachment
csv_part = MIMEText("foo,bar\n1,2", 'csv', 'utf_8')
msg = self.make_message(sender='bob@example.com',
html_body="<p>hello world</p>",
attachments=[csv_part])
complete = msg.as_string()
self.assertIn('Content-Type: multipart/mixed; boundary=', complete)
self.assertIn('Content-Type: text/csv; charset="utf_8"', complete)
# error if improper attachment
csv_path = self.write_file('data.csv', "foo,bar\n1,2")
msg = self.make_message(sender='bob@example.com',
html_body="<p>hello world</p>",
attachments=[csv_path])
self.assertRaises(ValueError, msg.as_string)
try:
msg.as_string()
except ValueError as err:
self.assertIn("must specify valid MIME attachments", str(err))
# everything
msg = self.make_message(sender='bob@example.com',
subject='meeting follow-up',