diff --git a/docs/examples.rst b/docs/examples.rst index fd51fad..d4ad6dc 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -67,7 +67,7 @@ Send and get response from smtp server: r = message.send(to=('John Brown', 'jbrown@gmail.com'), render={'name': 'John'}, - smtp={'host':'smtp.mycompany.com', 'port': 465, 'ssl': True}) + smtp={'host':'smtp.mycompany.com', 'port': 465, 'ssl': True, 'user': 'john', 'password': '***'}) assert r.status_code == 250 diff --git a/emails/message.py b/emails/message.py index d059267..cca653f 100644 --- a/emails/message.py +++ b/emails/message.py @@ -136,12 +136,17 @@ class BaseMessage(object): date = property(get_date, set_date) message_date = date + @property def message_id(self): mid = self._message_id if mid is False: return None return is_callable(mid) and mid() or mid + @message_id.setter + def message_id(self, value): + self._message_id = value + @property def attachments(self): if self._attachments is None: @@ -208,7 +213,7 @@ class MessageBuildMixin(object): msg.preamble = self.ROOT_PREAMBLE self.set_header(msg, 'Date', self.date, encode=False) - self.set_header(msg, 'Message-ID', self.message_id(), encode=False) + self.set_header(msg, 'Message-ID', self.message_id, encode=False) if self._headers: for (name, value) in self._headers.items(): @@ -219,7 +224,8 @@ class MessageBuildMixin(object): self.set_header(msg, 'Subject', subject) self.set_header(msg, 'From', self.encode_address_header(self._mail_from), encode=False) - self.set_header(msg, 'To', self._mail_to and self.encode_address_header(self._mail_to[0]) or None, encode=False) + self.set_header(msg, 'To', self._mail_to and ", ".join([self.encode_address_header(addr) + for addr in self._mail_to]) or None, encode=False) return msg diff --git a/emails/testsuite/message/test_message.py b/emails/testsuite/message/test_message.py index 7384852..3a16434 100644 --- a/emails/testsuite/message/test_message.py +++ b/emails/testsuite/message/test_message.py @@ -159,6 +159,10 @@ def test_message_id(): m = Message(**params) assert not m.as_message()['Message-ID'] + # Check message-id property setter + m.message_id = 'ZZZ' + assert m.as_message()['Message-ID'] == 'ZZZ' + # Check message-id exists when argument specified m = Message(message_id=MessageID(), **params) assert m.as_message()['Message-ID'] @@ -166,3 +170,13 @@ def test_message_id(): m = Message(message_id='XXX', **params) assert m.as_message()['Message-ID'] == 'XXX' + +def test_several_recipients_in_to_header(): + params = dict(html='...', mail_from='a@b.c') + + m = Message(mail_to=['d@e.f', 'g@h.i'], **params) + assert m.as_message()['To'] == 'd@e.f, g@h.i' + + m = Message(mail_to=[('♡', 'd@e.f'), ('웃', 'g@h.i')], **params) + assert m.as_message()['To'] == '=?utf-8?b?4pmh?= , =?utf-8?b?7JuD?= ' + diff --git a/emails/testsuite/message/test_send.py b/emails/testsuite/message/test_send.py index 305438c..90c576a 100644 --- a/emails/testsuite/message/test_send.py +++ b/emails/testsuite/message/test_send.py @@ -31,5 +31,6 @@ def test_send_letters(smtp_servers): for tag, server in smtp_servers.items(): server.patch_message(m) response = m.send(smtp=server.params, render=render) + print(server.params) assert response.success or response.status_code in (421, 451) # gmail not always like test emails server.sleep() diff --git a/emails/testsuite/smtp_servers.py b/emails/testsuite/smtp_servers.py index 5ab34e4..4aedb08 100644 --- a/emails/testsuite/smtp_servers.py +++ b/emails/testsuite/smtp_servers.py @@ -13,12 +13,11 @@ SERVERS = { host='mx.yandex.ru', port=25), #'mailtrap.io': dict(from_email=_from, port=25, **_mailtrap), - #'mailtrap.io-tls': dict(from_email=_from, tls=True, port=465, **_mailtrap), + # mailtrap disabled because of timeouts on Travis 'outlook.com': dict(from_email=_from, to_email='lavr@outlook.com', host='mx1.hotmail.com'), - 'me.com': dict(from_email=_from, to_email='s.lavrinenko@me.com', host='mx3.mail.icloud.com'), + #'me.com': dict(from_email=_from, to_email='s.lavrinenko@me.com', host='mx3.mail.icloud.com'), + # icloud.com disabled because of timeouts on Travis } - - diff --git a/emails/testsuite/transformer/test_transformer.py b/emails/testsuite/transformer/test_transformer.py index d42c035..d6e8f50 100644 --- a/emails/testsuite/transformer/test_transformer.py +++ b/emails/testsuite/transformer/test_transformer.py @@ -102,6 +102,15 @@ def test_image_inline(): t.save() assert '", local_loader=SimpleLoader(data={'a.gif': 'xxx'})) + t.load_and_transform() + t.attachment_store['a.gif'].content_disposition = 'inline' + t.synchronize_inline_images() + t.save() + assert "url(cid:a.gif)" in t.html + + def test_absolute_url(): t = Transformer(html="", base_url="https://host1.tld/a/b") diff --git a/emails/transformer.py b/emails/transformer.py index af9aa49..6b41678 100644 --- a/emails/transformer.py +++ b/emails/transformer.py @@ -212,12 +212,14 @@ class BaseTransformer(HTMLParser): return url - def _attribute_value(self, el): + def attribute_value(self, el): return el is not None \ and hasattr(el, 'attrib') \ and el.attrib.get(self.html_attribute_name) \ or None + _attribute_value = attribute_value # deprecated + def _default_attachment_check(self, el, hints): if hints['attrib'] == 'ignore': return False @@ -235,7 +237,7 @@ class BaseTransformer(HTMLParser): # Default callback: skip images with data-emails="ignore" attribute callback = lambda _, hints: hints['attrib'] != 'ignore' - attribute_value = self._attribute_value(element) or '' + attribute_value = self.attribute_value(element) or '' # If callback returns False, skip attachment loading if not callback(element, hints={'attrib': attribute_value}):