fix: move email
stuff from subpackage to module
This commit is contained in:
parent
902412322e
commit
6c8f1c973d
|
@ -1,6 +0,0 @@
|
||||||
|
|
||||||
``wuttjamaican.email.handler``
|
|
||||||
==============================
|
|
||||||
|
|
||||||
.. automodule:: wuttjamaican.email.handler
|
|
||||||
:members:
|
|
|
@ -1,6 +0,0 @@
|
||||||
|
|
||||||
``wuttjamaican.email.message``
|
|
||||||
==============================
|
|
||||||
|
|
||||||
.. automodule:: wuttjamaican.email.message
|
|
||||||
:members:
|
|
|
@ -189,7 +189,7 @@ Glossary
|
||||||
The :term:`handler` responsible for sending email on behalf of
|
The :term:`handler` responsible for sending email on behalf of
|
||||||
the :term:`app`.
|
the :term:`app`.
|
||||||
|
|
||||||
Default is :class:`~wuttjamaican.email.handler.EmailHandler`.
|
Default is :class:`~wuttjamaican.email.EmailHandler`.
|
||||||
|
|
||||||
email key
|
email key
|
||||||
String idenfier for a certain :term:`email type`. Each email key
|
String idenfier for a certain :term:`email type`. Each email key
|
||||||
|
|
|
@ -82,8 +82,6 @@ Contents
|
||||||
api/wuttjamaican.db.sess
|
api/wuttjamaican.db.sess
|
||||||
api/wuttjamaican.db.util
|
api/wuttjamaican.db.util
|
||||||
api/wuttjamaican.email
|
api/wuttjamaican.email
|
||||||
api/wuttjamaican.email.handler
|
|
||||||
api/wuttjamaican.email.message
|
|
||||||
api/wuttjamaican.enum
|
api/wuttjamaican.enum
|
||||||
api/wuttjamaican.exc
|
api/wuttjamaican.exc
|
||||||
api/wuttjamaican.install
|
api/wuttjamaican.install
|
||||||
|
|
|
@ -50,9 +50,8 @@ for one or both of the ``text/plain`` and ``text/html`` content-types
|
||||||
(using ``txt`` and ``html`` as shorthand name, respectively).
|
(using ``txt`` and ``html`` as shorthand name, respectively).
|
||||||
|
|
||||||
Template files must use the :doc:`Mako template language <mako:index>`
|
Template files must use the :doc:`Mako template language <mako:index>`
|
||||||
and be named based on the
|
and be named based on the :attr:`~wuttjamaican.email.Message.key` for
|
||||||
:attr:`~wuttjamaican.email.message.Message.key` for the email type, as
|
the email type, as well as content-type.
|
||||||
well as content-type.
|
|
||||||
|
|
||||||
Therefore a new email of type ``poser_alert_foo`` would need one or
|
Therefore a new email of type ``poser_alert_foo`` would need one or
|
||||||
both of these defined:
|
both of these defined:
|
||||||
|
|
|
@ -768,7 +768,7 @@ class AppHandler:
|
||||||
|
|
||||||
See also :meth:`send_email()`.
|
See also :meth:`send_email()`.
|
||||||
|
|
||||||
:rtype: :class:`~wuttjamaican.email.handler.EmailHandler`
|
:rtype: :class:`~wuttjamaican.email.EmailHandler`
|
||||||
"""
|
"""
|
||||||
if 'email' not in self.handlers:
|
if 'email' not in self.handlers:
|
||||||
spec = self.config.get(f'{self.appname}.email.handler',
|
spec = self.config.get(f'{self.appname}.email.handler',
|
||||||
|
@ -823,7 +823,7 @@ class AppHandler:
|
||||||
Send an email message.
|
Send an email message.
|
||||||
|
|
||||||
This is a convenience wrapper around
|
This is a convenience wrapper around
|
||||||
:meth:`~wuttjamaican.email.handler.EmailHandler.send_email()`.
|
:meth:`~wuttjamaican.email.EmailHandler.send_email()`.
|
||||||
"""
|
"""
|
||||||
self.get_email_handler().send_email(*args, **kwargs)
|
self.get_email_handler().send_email(*args, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -26,15 +26,146 @@ Email Handler
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import smtplib
|
import smtplib
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
|
||||||
from wuttjamaican.app import GenericHandler
|
from wuttjamaican.app import GenericHandler
|
||||||
from wuttjamaican.util import resource_path
|
from wuttjamaican.util import resource_path
|
||||||
from wuttjamaican.email.message import Message
|
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Message:
|
||||||
|
"""
|
||||||
|
Represents an email message to be sent.
|
||||||
|
|
||||||
|
:param to: Recipient(s) for the message. This may be either a
|
||||||
|
string, or list of strings. If a string, it will be converted
|
||||||
|
to a list since that is how the :attr:`to` attribute tracks it.
|
||||||
|
Similar logic is used for :attr:`cc` and :attr:`bcc`.
|
||||||
|
|
||||||
|
All attributes shown below may also be specified via constructor.
|
||||||
|
|
||||||
|
.. attribute:: key
|
||||||
|
|
||||||
|
Unique key indicating the "type" of message. An "ad-hoc"
|
||||||
|
message created arbitrarily may not have/need a key; however
|
||||||
|
one created via
|
||||||
|
:meth:`~wuttjamaican.email.EmailHandler.make_auto_message()`
|
||||||
|
will always have a key.
|
||||||
|
|
||||||
|
This key is not used for anything within the ``Message`` class
|
||||||
|
logic. It is used by
|
||||||
|
:meth:`~wuttjamaican.email.EmailHandler.make_auto_message()`
|
||||||
|
when constructing the message, and the key is set on the final
|
||||||
|
message only as a reference.
|
||||||
|
|
||||||
|
.. attribute:: sender
|
||||||
|
|
||||||
|
Sender (``From:``) address for the message.
|
||||||
|
|
||||||
|
.. attribute:: subject
|
||||||
|
|
||||||
|
Subject text for the message.
|
||||||
|
|
||||||
|
.. attribute:: to
|
||||||
|
|
||||||
|
List of ``To:`` recipients for the message.
|
||||||
|
|
||||||
|
.. attribute:: cc
|
||||||
|
|
||||||
|
List of ``Cc:`` recipients for the message.
|
||||||
|
|
||||||
|
.. attribute:: bcc
|
||||||
|
|
||||||
|
List of ``Bcc:`` recipients for the message.
|
||||||
|
|
||||||
|
.. attribute:: replyto
|
||||||
|
|
||||||
|
Optional reply-to (``Reply-To:``) address for the message.
|
||||||
|
|
||||||
|
.. attribute:: txt_body
|
||||||
|
|
||||||
|
String with the ``text/plain`` body content.
|
||||||
|
|
||||||
|
.. attribute:: html_body
|
||||||
|
|
||||||
|
String with the ``text/html`` body content.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
key=None,
|
||||||
|
sender=None,
|
||||||
|
subject=None,
|
||||||
|
to=None,
|
||||||
|
cc=None,
|
||||||
|
bcc=None,
|
||||||
|
replyto=None,
|
||||||
|
txt_body=None,
|
||||||
|
html_body=None,
|
||||||
|
):
|
||||||
|
self.key = key
|
||||||
|
self.sender = sender
|
||||||
|
self.subject = subject
|
||||||
|
self.set_recips('to', to)
|
||||||
|
self.set_recips('cc', cc)
|
||||||
|
self.set_recips('bcc', bcc)
|
||||||
|
self.replyto = replyto
|
||||||
|
self.txt_body = txt_body
|
||||||
|
self.html_body = html_body
|
||||||
|
|
||||||
|
def set_recips(self, name, value):
|
||||||
|
""" """
|
||||||
|
if value:
|
||||||
|
if isinstance(value, str):
|
||||||
|
value = [value]
|
||||||
|
if not isinstance(value, (list, tuple)):
|
||||||
|
raise ValueError("must specify a string, tuple or list value")
|
||||||
|
else:
|
||||||
|
value = []
|
||||||
|
setattr(self, name, list(value))
|
||||||
|
|
||||||
|
def as_string(self):
|
||||||
|
"""
|
||||||
|
Returns the complete message as string. This is called from
|
||||||
|
within
|
||||||
|
:meth:`~wuttjamaican.email.EmailHandler.deliver_message()` to
|
||||||
|
obtain the SMTP payload.
|
||||||
|
"""
|
||||||
|
msg = None
|
||||||
|
|
||||||
|
if self.txt_body and self.html_body:
|
||||||
|
txt = MIMEText(self.txt_body, _charset='utf_8')
|
||||||
|
html = MIMEText(self.html_body, _subtype='html', _charset='utf_8')
|
||||||
|
msg = MIMEMultipart(_subtype='alternative', _subparts=[txt, html])
|
||||||
|
|
||||||
|
elif self.txt_body:
|
||||||
|
msg = MIMEText(self.txt_body, _charset='utf_8')
|
||||||
|
|
||||||
|
elif self.html_body:
|
||||||
|
msg = MIMEText(self.html_body, 'html', _charset='utf_8')
|
||||||
|
|
||||||
|
if not msg:
|
||||||
|
raise ValueError("message has no body parts")
|
||||||
|
|
||||||
|
msg['Subject'] = self.subject
|
||||||
|
msg['From'] = self.sender
|
||||||
|
|
||||||
|
for addr in self.to:
|
||||||
|
msg['To'] = addr
|
||||||
|
for addr in self.cc:
|
||||||
|
msg['Cc'] = addr
|
||||||
|
for addr in self.bcc:
|
||||||
|
msg['Bcc'] = addr
|
||||||
|
|
||||||
|
if self.replyto:
|
||||||
|
msg.add_header('Reply-To', self.replyto)
|
||||||
|
|
||||||
|
return msg.as_string()
|
||||||
|
|
||||||
|
|
||||||
class EmailHandler(GenericHandler):
|
class EmailHandler(GenericHandler):
|
||||||
"""
|
"""
|
||||||
Base class and default implementation for the :term:`email
|
Base class and default implementation for the :term:`email
|
||||||
|
@ -89,7 +220,7 @@ class EmailHandler(GenericHandler):
|
||||||
This is the "raw" factory which is simply a wrapper around the
|
This is the "raw" factory which is simply a wrapper around the
|
||||||
class constructor. See also :meth:`make_auto_message()`.
|
class constructor. See also :meth:`make_auto_message()`.
|
||||||
|
|
||||||
:returns: :class:`~wuttjamaican.email.message.Message` object.
|
:returns: :class:`~wuttjamaican.email.Message` object.
|
||||||
"""
|
"""
|
||||||
return Message(**kwargs)
|
return Message(**kwargs)
|
||||||
|
|
||||||
|
@ -112,7 +243,7 @@ class EmailHandler(GenericHandler):
|
||||||
:param \**kwargs: Any remaining kwargs are passed as-is to
|
:param \**kwargs: Any remaining kwargs are passed as-is to
|
||||||
:meth:`make_message()`. More on this below.
|
:meth:`make_message()`. More on this below.
|
||||||
|
|
||||||
:returns: :class:`~wuttjamaican.email.message.Message` object.
|
:returns: :class:`~wuttjamaican.email.Message` object.
|
||||||
|
|
||||||
This method may invoke some others, to gather the message
|
This method may invoke some others, to gather the message
|
||||||
attributes. Each will check config, or render a template, or
|
attributes. Each will check config, or render a template, or
|
||||||
|
@ -147,8 +278,8 @@ class EmailHandler(GenericHandler):
|
||||||
def get_auto_sender(self, key):
|
def get_auto_sender(self, key):
|
||||||
"""
|
"""
|
||||||
Returns automatic
|
Returns automatic
|
||||||
:attr:`~wuttjamaican.email.message.Message.sender` address for
|
:attr:`~wuttjamaican.email.Message.sender` address for a
|
||||||
a message, as determined by config.
|
message, as determined by config.
|
||||||
"""
|
"""
|
||||||
# prefer configured sender specific to key
|
# prefer configured sender specific to key
|
||||||
sender = self.config.get(f'{self.config.appname}.email.{key}.sender')
|
sender = self.config.get(f'{self.config.appname}.email.{key}.sender')
|
||||||
|
@ -160,9 +291,8 @@ class EmailHandler(GenericHandler):
|
||||||
|
|
||||||
def get_auto_subject(self, key, context={}, rendered=True):
|
def get_auto_subject(self, key, context={}, rendered=True):
|
||||||
"""
|
"""
|
||||||
Returns automatic
|
Returns automatic :attr:`~wuttjamaican.email.Message.subject`
|
||||||
:attr:`~wuttjamaican.email.message.Message.subject` line for a
|
line for a message, as determined by config.
|
||||||
message, as determined by config.
|
|
||||||
|
|
||||||
This calls :meth:`get_auto_subject_template()` and then
|
This calls :meth:`get_auto_subject_template()` and then
|
||||||
renders the result using the given context.
|
renders the result using the given context.
|
||||||
|
@ -200,25 +330,22 @@ class EmailHandler(GenericHandler):
|
||||||
|
|
||||||
def get_auto_to(self, key):
|
def get_auto_to(self, key):
|
||||||
"""
|
"""
|
||||||
Returns automatic
|
Returns automatic :attr:`~wuttjamaican.email.Message.to`
|
||||||
:attr:`~wuttjamaican.email.message.Message.to` recipient
|
recipient address(es) for a message, as determined by config.
|
||||||
address(es) for a message, as determined by config.
|
|
||||||
"""
|
"""
|
||||||
return self.get_auto_recips(key, 'to')
|
return self.get_auto_recips(key, 'to')
|
||||||
|
|
||||||
def get_auto_cc(self, key):
|
def get_auto_cc(self, key):
|
||||||
"""
|
"""
|
||||||
Returns automatic
|
Returns automatic :attr:`~wuttjamaican.email.Message.cc`
|
||||||
:attr:`~wuttjamaican.email.message.Message.cc` recipient
|
recipient address(es) for a message, as determined by config.
|
||||||
address(es) for a message, as determined by config.
|
|
||||||
"""
|
"""
|
||||||
return self.get_auto_recips(key, 'cc')
|
return self.get_auto_recips(key, 'cc')
|
||||||
|
|
||||||
def get_auto_bcc(self, key):
|
def get_auto_bcc(self, key):
|
||||||
"""
|
"""
|
||||||
Returns automatic
|
Returns automatic :attr:`~wuttjamaican.email.Message.bcc`
|
||||||
:attr:`~wuttjamaican.email.message.Message.bcc` recipient
|
recipient address(es) for a message, as determined by config.
|
||||||
address(es) for a message, as determined by config.
|
|
||||||
"""
|
"""
|
||||||
return self.get_auto_recips(key, 'bcc')
|
return self.get_auto_recips(key, 'bcc')
|
||||||
|
|
||||||
|
@ -239,10 +366,9 @@ class EmailHandler(GenericHandler):
|
||||||
|
|
||||||
def get_auto_txt_body(self, key, context={}):
|
def get_auto_txt_body(self, key, context={}):
|
||||||
"""
|
"""
|
||||||
Returns automatic
|
Returns automatic :attr:`~wuttjamaican.email.Message.txt_body`
|
||||||
:attr:`~wuttjamaican.email.message.Message.txt_body` content
|
content for a message, as determined by config. This renders
|
||||||
for a message, as determined by config. This renders a
|
a template with the given context.
|
||||||
template with the given context.
|
|
||||||
"""
|
"""
|
||||||
template = self.get_auto_body_template(key, 'txt')
|
template = self.get_auto_body_template(key, 'txt')
|
||||||
if template:
|
if template:
|
||||||
|
@ -251,9 +377,9 @@ class EmailHandler(GenericHandler):
|
||||||
def get_auto_html_body(self, key, context={}):
|
def get_auto_html_body(self, key, context={}):
|
||||||
"""
|
"""
|
||||||
Returns automatic
|
Returns automatic
|
||||||
:attr:`~wuttjamaican.email.message.Message.html_body` content
|
:attr:`~wuttjamaican.email.Message.html_body` content for a
|
||||||
for a message, as determined by config. This renders a
|
message, as determined by config. This renders a template
|
||||||
template with the given context.
|
with the given context.
|
||||||
"""
|
"""
|
||||||
template = self.get_auto_body_template(key, 'html')
|
template = self.get_auto_body_template(key, 'html')
|
||||||
if template:
|
if template:
|
||||||
|
@ -327,10 +453,9 @@ class EmailHandler(GenericHandler):
|
||||||
"""
|
"""
|
||||||
Deliver a message via SMTP smarthost.
|
Deliver a message via SMTP smarthost.
|
||||||
|
|
||||||
:param message: Either a
|
:param message: Either a :class:`~wuttjamaican.email.Message`
|
||||||
:class:`~wuttjamaican.email.message.Message` object or
|
object or similar, or a string representing the complete
|
||||||
similar, or a string representing the complete message to
|
message to be sent as-is.
|
||||||
be sent as-is.
|
|
||||||
|
|
||||||
:param sender: Optional sender address to use for delivery.
|
:param sender: Optional sender address to use for delivery.
|
||||||
If not specified, will be read from ``message``.
|
If not specified, will be read from ``message``.
|
||||||
|
@ -339,10 +464,10 @@ class EmailHandler(GenericHandler):
|
||||||
If not specified, will be read from ``message``.
|
If not specified, will be read from ``message``.
|
||||||
|
|
||||||
A general rule here is that you can either provide a proper
|
A general rule here is that you can either provide a proper
|
||||||
:class:`~wuttjamaican.email.message.Message` object, **or**
|
:class:`~wuttjamaican.email.Message` object, **or** you *must*
|
||||||
you *must* provide ``sender`` and ``recips``. The logic is
|
provide ``sender`` and ``recips``. The logic is not smart
|
||||||
not smart enough (yet?) to parse sender/recips from a simple
|
enough (yet?) to parse sender/recips from a simple string
|
||||||
string message.
|
message.
|
||||||
|
|
||||||
Note also, this method does not (yet?) have robust error
|
Note also, this method does not (yet?) have robust error
|
||||||
handling, so if an error occurs with the SMTP session, it will
|
handling, so if an error occurs with the SMTP session, it will
|
|
@ -1,33 +0,0 @@
|
||||||
# -*- coding: utf-8; -*-
|
|
||||||
################################################################################
|
|
||||||
#
|
|
||||||
# WuttJamaican -- Base package for Wutta Framework
|
|
||||||
# Copyright © 2023-2024 Lance Edgar
|
|
||||||
#
|
|
||||||
# This file is part of Wutta Framework.
|
|
||||||
#
|
|
||||||
# Wutta Framework is free software: you can redistribute it and/or modify it
|
|
||||||
# under the terms of the GNU General Public License as published by the Free
|
|
||||||
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
||||||
# later version.
|
|
||||||
#
|
|
||||||
# Wutta Framework is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
# more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License along with
|
|
||||||
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
################################################################################
|
|
||||||
"""
|
|
||||||
Email Utilities
|
|
||||||
|
|
||||||
The following are available in this ``wuttjamaican.email`` namespace:
|
|
||||||
|
|
||||||
* :class:`~wuttjamaican.email.handler.EmailHandler`
|
|
||||||
* :class:`~wuttjamaican.email.message.Message`
|
|
||||||
"""
|
|
||||||
|
|
||||||
from .handler import EmailHandler
|
|
||||||
from .message import Message
|
|
|
@ -1,158 +0,0 @@
|
||||||
# -*- coding: utf-8; -*-
|
|
||||||
################################################################################
|
|
||||||
#
|
|
||||||
# WuttJamaican -- Base package for Wutta Framework
|
|
||||||
# Copyright © 2023-2024 Lance Edgar
|
|
||||||
#
|
|
||||||
# This file is part of Wutta Framework.
|
|
||||||
#
|
|
||||||
# Wutta Framework is free software: you can redistribute it and/or modify it
|
|
||||||
# under the terms of the GNU General Public License as published by the Free
|
|
||||||
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
||||||
# later version.
|
|
||||||
#
|
|
||||||
# Wutta Framework is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
# more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License along with
|
|
||||||
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
################################################################################
|
|
||||||
"""
|
|
||||||
Email Message
|
|
||||||
"""
|
|
||||||
|
|
||||||
from email.mime.multipart import MIMEMultipart
|
|
||||||
from email.mime.text import MIMEText
|
|
||||||
|
|
||||||
|
|
||||||
class Message:
|
|
||||||
"""
|
|
||||||
Represents an email message to be sent.
|
|
||||||
|
|
||||||
:param to: Recipient(s) for the message. This may be either a
|
|
||||||
string, or list of strings. If a string, it will be converted
|
|
||||||
to a list since that is how the :attr:`to` attribute tracks it.
|
|
||||||
Similar logic is used for :attr:`cc` and :attr:`bcc`.
|
|
||||||
|
|
||||||
All attributes shown below may also be specified via constructor.
|
|
||||||
|
|
||||||
.. attribute:: key
|
|
||||||
|
|
||||||
Unique key indicating the "type" of message. An "ad-hoc"
|
|
||||||
message created arbitrarily may not have/need a key; however
|
|
||||||
one created via
|
|
||||||
:meth:`~wuttjamaican.email.handler.EmailHandler.make_auto_message()`
|
|
||||||
will always have a key.
|
|
||||||
|
|
||||||
This key is not used for anything within the ``Message`` class
|
|
||||||
logic. It is used by
|
|
||||||
:meth:`~wuttjamaican.email.handler.EmailHandler.make_auto_message()`
|
|
||||||
when constructing the message, and the key is set on the final
|
|
||||||
message only as a reference.
|
|
||||||
|
|
||||||
.. attribute:: sender
|
|
||||||
|
|
||||||
Sender (``From:``) address for the message.
|
|
||||||
|
|
||||||
.. attribute:: subject
|
|
||||||
|
|
||||||
Subject text for the message.
|
|
||||||
|
|
||||||
.. attribute:: to
|
|
||||||
|
|
||||||
List of ``To:`` recipients for the message.
|
|
||||||
|
|
||||||
.. attribute:: cc
|
|
||||||
|
|
||||||
List of ``Cc:`` recipients for the message.
|
|
||||||
|
|
||||||
.. attribute:: bcc
|
|
||||||
|
|
||||||
List of ``Bcc:`` recipients for the message.
|
|
||||||
|
|
||||||
.. attribute:: replyto
|
|
||||||
|
|
||||||
Optional reply-to (``Reply-To:``) address for the message.
|
|
||||||
|
|
||||||
.. attribute:: txt_body
|
|
||||||
|
|
||||||
String with the ``text/plain`` body content.
|
|
||||||
|
|
||||||
.. attribute:: html_body
|
|
||||||
|
|
||||||
String with the ``text/html`` body content.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
key=None,
|
|
||||||
sender=None,
|
|
||||||
subject=None,
|
|
||||||
to=None,
|
|
||||||
cc=None,
|
|
||||||
bcc=None,
|
|
||||||
replyto=None,
|
|
||||||
txt_body=None,
|
|
||||||
html_body=None,
|
|
||||||
):
|
|
||||||
self.key = key
|
|
||||||
self.sender = sender
|
|
||||||
self.subject = subject
|
|
||||||
self.set_recips('to', to)
|
|
||||||
self.set_recips('cc', cc)
|
|
||||||
self.set_recips('bcc', bcc)
|
|
||||||
self.replyto = replyto
|
|
||||||
self.txt_body = txt_body
|
|
||||||
self.html_body = html_body
|
|
||||||
|
|
||||||
def set_recips(self, name, value):
|
|
||||||
""" """
|
|
||||||
if value:
|
|
||||||
if isinstance(value, str):
|
|
||||||
value = [value]
|
|
||||||
if not isinstance(value, (list, tuple)):
|
|
||||||
raise ValueError("must specify a string, tuple or list value")
|
|
||||||
else:
|
|
||||||
value = []
|
|
||||||
setattr(self, name, list(value))
|
|
||||||
|
|
||||||
def as_string(self):
|
|
||||||
"""
|
|
||||||
Returns the complete message as string. This is called from
|
|
||||||
within
|
|
||||||
:meth:`~wuttjamaican.email.handler.EmailHandler.deliver_message()`
|
|
||||||
to obtain the SMTP payload.
|
|
||||||
"""
|
|
||||||
msg = None
|
|
||||||
|
|
||||||
if self.txt_body and self.html_body:
|
|
||||||
txt = MIMEText(self.txt_body, _charset='utf_8')
|
|
||||||
html = MIMEText(self.html_body, _subtype='html', _charset='utf_8')
|
|
||||||
msg = MIMEMultipart(_subtype='alternative', _subparts=[txt, html])
|
|
||||||
|
|
||||||
elif self.txt_body:
|
|
||||||
msg = MIMEText(self.txt_body, _charset='utf_8')
|
|
||||||
|
|
||||||
elif self.html_body:
|
|
||||||
msg = MIMEText(self.html_body, 'html', _charset='utf_8')
|
|
||||||
|
|
||||||
if not msg:
|
|
||||||
raise ValueError("message has no body parts")
|
|
||||||
|
|
||||||
msg['Subject'] = self.subject
|
|
||||||
msg['From'] = self.sender
|
|
||||||
|
|
||||||
for addr in self.to:
|
|
||||||
msg['To'] = addr
|
|
||||||
for addr in self.cc:
|
|
||||||
msg['Cc'] = addr
|
|
||||||
for addr in self.bcc:
|
|
||||||
msg['Bcc'] = addr
|
|
||||||
|
|
||||||
if self.replyto:
|
|
||||||
msg.add_header('Reply-To', self.replyto)
|
|
||||||
|
|
||||||
return msg.as_string()
|
|
|
@ -1,76 +0,0 @@
|
||||||
# -*- coding: utf-8; -*-
|
|
||||||
|
|
||||||
from unittest import TestCase
|
|
||||||
|
|
||||||
from wuttjamaican.email import message as mod
|
|
||||||
|
|
||||||
|
|
||||||
class TestMessage(TestCase):
|
|
||||||
|
|
||||||
def make_message(self, **kwargs):
|
|
||||||
return mod.Message(**kwargs)
|
|
||||||
|
|
||||||
def test_set_recips(self):
|
|
||||||
msg = self.make_message()
|
|
||||||
self.assertEqual(msg.to, [])
|
|
||||||
|
|
||||||
# set as list
|
|
||||||
msg.set_recips('to', ['sally@example.com'])
|
|
||||||
self.assertEqual(msg.to, ['sally@example.com'])
|
|
||||||
|
|
||||||
# set as tuple
|
|
||||||
msg.set_recips('to', ('barney@example.com',))
|
|
||||||
self.assertEqual(msg.to, ['barney@example.com'])
|
|
||||||
|
|
||||||
# set as string
|
|
||||||
msg.set_recips('to', 'wilma@example.com')
|
|
||||||
self.assertEqual(msg.to, ['wilma@example.com'])
|
|
||||||
|
|
||||||
# set as null
|
|
||||||
msg.set_recips('to', None)
|
|
||||||
self.assertEqual(msg.to, [])
|
|
||||||
|
|
||||||
# otherwise error
|
|
||||||
self.assertRaises(ValueError, msg.set_recips, 'to', {'foo': 'foo@example.com'})
|
|
||||||
|
|
||||||
def test_as_string(self):
|
|
||||||
|
|
||||||
# error if no body
|
|
||||||
msg = self.make_message()
|
|
||||||
self.assertRaises(ValueError, msg.as_string)
|
|
||||||
|
|
||||||
# txt body
|
|
||||||
msg = self.make_message(sender='bob@example.com',
|
|
||||||
txt_body="hello world")
|
|
||||||
complete = msg.as_string()
|
|
||||||
self.assertIn('From: bob@example.com', complete)
|
|
||||||
|
|
||||||
# html body
|
|
||||||
msg = self.make_message(sender='bob@example.com',
|
|
||||||
html_body="<p>hello world</p>")
|
|
||||||
complete = msg.as_string()
|
|
||||||
self.assertIn('From: bob@example.com', complete)
|
|
||||||
|
|
||||||
# txt + html body
|
|
||||||
msg = self.make_message(sender='bob@example.com',
|
|
||||||
txt_body="hello world",
|
|
||||||
html_body="<p>hello world</p>")
|
|
||||||
complete = msg.as_string()
|
|
||||||
self.assertIn('From: bob@example.com', complete)
|
|
||||||
|
|
||||||
# everything
|
|
||||||
msg = self.make_message(sender='bob@example.com',
|
|
||||||
subject='meeting follow-up',
|
|
||||||
to='sally@example.com',
|
|
||||||
cc='marketing@example.com',
|
|
||||||
bcc='bob@example.com',
|
|
||||||
replyto='sales@example.com',
|
|
||||||
txt_body="hello world",
|
|
||||||
html_body="<p>hello world</p>")
|
|
||||||
complete = msg.as_string()
|
|
||||||
self.assertIn('From: bob@example.com', complete)
|
|
||||||
self.assertIn('Subject: meeting follow-up', complete)
|
|
||||||
self.assertIn('To: sally@example.com', complete)
|
|
||||||
self.assertIn('Cc: marketing@example.com', complete)
|
|
||||||
self.assertIn('Bcc: bob@example.com', complete)
|
|
||||||
self.assertIn('Reply-To: sales@example.com', complete)
|
|
|
@ -5,8 +5,7 @@ from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from wuttjamaican.email import handler as mod
|
from wuttjamaican import email as mod
|
||||||
from wuttjamaican.email import Message
|
|
||||||
from wuttjamaican.conf import WuttaConfig
|
from wuttjamaican.conf import WuttaConfig
|
||||||
from wuttjamaican.util import resource_path
|
from wuttjamaican.util import resource_path
|
||||||
from wuttjamaican.exc import ConfigurationError
|
from wuttjamaican.exc import ConfigurationError
|
||||||
|
@ -36,28 +35,28 @@ class TestEmailHandler(TestCase):
|
||||||
|
|
||||||
# provider may specify paths as list
|
# provider may specify paths as list
|
||||||
providers = {
|
providers = {
|
||||||
'wuttatest': MagicMock(email_templates=['wuttjamaican.email:templates']),
|
'wuttatest': MagicMock(email_templates=['wuttjamaican:email-templates']),
|
||||||
}
|
}
|
||||||
with patch.object(self.app, 'providers', new=providers):
|
with patch.object(self.app, 'providers', new=providers):
|
||||||
handler = self.make_handler()
|
handler = self.make_handler()
|
||||||
path = resource_path('wuttjamaican.email:templates')
|
path = resource_path('wuttjamaican:email-templates')
|
||||||
self.assertEqual(handler.txt_templates.directories, [path])
|
self.assertEqual(handler.txt_templates.directories, [path])
|
||||||
self.assertEqual(handler.html_templates.directories, [path])
|
self.assertEqual(handler.html_templates.directories, [path])
|
||||||
|
|
||||||
# provider may specify paths as string
|
# provider may specify paths as string
|
||||||
providers = {
|
providers = {
|
||||||
'wuttatest': MagicMock(email_templates='wuttjamaican.email:templates'),
|
'wuttatest': MagicMock(email_templates='wuttjamaican:email-templates'),
|
||||||
}
|
}
|
||||||
with patch.object(self.app, 'providers', new=providers):
|
with patch.object(self.app, 'providers', new=providers):
|
||||||
handler = self.make_handler()
|
handler = self.make_handler()
|
||||||
path = resource_path('wuttjamaican.email:templates')
|
path = resource_path('wuttjamaican:email-templates')
|
||||||
self.assertEqual(handler.txt_templates.directories, [path])
|
self.assertEqual(handler.txt_templates.directories, [path])
|
||||||
self.assertEqual(handler.html_templates.directories, [path])
|
self.assertEqual(handler.html_templates.directories, [path])
|
||||||
|
|
||||||
def test_make_message(self):
|
def test_make_message(self):
|
||||||
handler = self.make_handler()
|
handler = self.make_handler()
|
||||||
msg = handler.make_message()
|
msg = handler.make_message()
|
||||||
self.assertIsInstance(msg, Message)
|
self.assertIsInstance(msg, mod.Message)
|
||||||
|
|
||||||
def test_make_auto_message(self):
|
def test_make_auto_message(self):
|
||||||
handler = self.make_handler()
|
handler = self.make_handler()
|
||||||
|
@ -70,7 +69,7 @@ class TestEmailHandler(TestCase):
|
||||||
|
|
||||||
# message is empty by default
|
# message is empty by default
|
||||||
msg = handler.make_auto_message('foo')
|
msg = handler.make_auto_message('foo')
|
||||||
self.assertIsInstance(msg, Message)
|
self.assertIsInstance(msg, mod.Message)
|
||||||
self.assertEqual(msg.key, 'foo')
|
self.assertEqual(msg.key, 'foo')
|
||||||
self.assertEqual(msg.sender, 'bob@example.com')
|
self.assertEqual(msg.sender, 'bob@example.com')
|
||||||
self.assertEqual(msg.subject, "Automated message")
|
self.assertEqual(msg.subject, "Automated message")
|
||||||
|
@ -85,7 +84,7 @@ class TestEmailHandler(TestCase):
|
||||||
# then we should get back a more complete message
|
# then we should get back a more complete message
|
||||||
self.config.setdefault('wutta.email.test_foo.subject', "hello foo")
|
self.config.setdefault('wutta.email.test_foo.subject', "hello foo")
|
||||||
self.config.setdefault('wutta.email.test_foo.to', 'sally@example.com')
|
self.config.setdefault('wutta.email.test_foo.to', 'sally@example.com')
|
||||||
self.config.setdefault('wutta.email.templates', 'tests.email:templates')
|
self.config.setdefault('wutta.email.templates', 'tests:email-templates')
|
||||||
handler = self.make_handler()
|
handler = self.make_handler()
|
||||||
msg = handler.make_auto_message('test_foo')
|
msg = handler.make_auto_message('test_foo')
|
||||||
self.assertEqual(msg.key, 'test_foo')
|
self.assertEqual(msg.key, 'test_foo')
|
||||||
|
@ -240,7 +239,7 @@ class TestEmailHandler(TestCase):
|
||||||
|
|
||||||
# but returns a template if it exists
|
# but returns a template if it exists
|
||||||
providers = {
|
providers = {
|
||||||
'wuttatest': MagicMock(email_templates=['tests.email:templates']),
|
'wuttatest': MagicMock(email_templates=['tests:email-templates']),
|
||||||
}
|
}
|
||||||
with patch.object(self.app, 'providers', new=providers):
|
with patch.object(self.app, 'providers', new=providers):
|
||||||
handler = self.make_handler()
|
handler = self.make_handler()
|
||||||
|
@ -257,7 +256,7 @@ class TestEmailHandler(TestCase):
|
||||||
|
|
||||||
# but returns body if template exists
|
# but returns body if template exists
|
||||||
providers = {
|
providers = {
|
||||||
'wuttatest': MagicMock(email_templates=['tests.email:templates']),
|
'wuttatest': MagicMock(email_templates=['tests:email-templates']),
|
||||||
}
|
}
|
||||||
with patch.object(self.app, 'providers', new=providers):
|
with patch.object(self.app, 'providers', new=providers):
|
||||||
handler = self.make_handler()
|
handler = self.make_handler()
|
||||||
|
@ -273,7 +272,7 @@ class TestEmailHandler(TestCase):
|
||||||
|
|
||||||
# but returns body if template exists
|
# but returns body if template exists
|
||||||
providers = {
|
providers = {
|
||||||
'wuttatest': MagicMock(email_templates=['tests.email:templates']),
|
'wuttatest': MagicMock(email_templates=['tests:email-templates']),
|
||||||
}
|
}
|
||||||
with patch.object(self.app, 'providers', new=providers):
|
with patch.object(self.app, 'providers', new=providers):
|
||||||
handler = self.make_handler()
|
handler = self.make_handler()
|
||||||
|
@ -456,3 +455,74 @@ class TestEmailHandler(TestCase):
|
||||||
self.config.setdefault('wutta.email.default.enabled', False)
|
self.config.setdefault('wutta.email.default.enabled', False)
|
||||||
handler.send_email('bar', sender='bar@example.com', txt_body="hello world")
|
handler.send_email('bar', sender='bar@example.com', txt_body="hello world")
|
||||||
self.assertFalse(deliver_message.called)
|
self.assertFalse(deliver_message.called)
|
||||||
|
|
||||||
|
|
||||||
|
class TestMessage(TestCase):
|
||||||
|
|
||||||
|
def make_message(self, **kwargs):
|
||||||
|
return mod.Message(**kwargs)
|
||||||
|
|
||||||
|
def test_set_recips(self):
|
||||||
|
msg = self.make_message()
|
||||||
|
self.assertEqual(msg.to, [])
|
||||||
|
|
||||||
|
# set as list
|
||||||
|
msg.set_recips('to', ['sally@example.com'])
|
||||||
|
self.assertEqual(msg.to, ['sally@example.com'])
|
||||||
|
|
||||||
|
# set as tuple
|
||||||
|
msg.set_recips('to', ('barney@example.com',))
|
||||||
|
self.assertEqual(msg.to, ['barney@example.com'])
|
||||||
|
|
||||||
|
# set as string
|
||||||
|
msg.set_recips('to', 'wilma@example.com')
|
||||||
|
self.assertEqual(msg.to, ['wilma@example.com'])
|
||||||
|
|
||||||
|
# set as null
|
||||||
|
msg.set_recips('to', None)
|
||||||
|
self.assertEqual(msg.to, [])
|
||||||
|
|
||||||
|
# otherwise error
|
||||||
|
self.assertRaises(ValueError, msg.set_recips, 'to', {'foo': 'foo@example.com'})
|
||||||
|
|
||||||
|
def test_as_string(self):
|
||||||
|
|
||||||
|
# error if no body
|
||||||
|
msg = self.make_message()
|
||||||
|
self.assertRaises(ValueError, msg.as_string)
|
||||||
|
|
||||||
|
# txt body
|
||||||
|
msg = self.make_message(sender='bob@example.com',
|
||||||
|
txt_body="hello world")
|
||||||
|
complete = msg.as_string()
|
||||||
|
self.assertIn('From: bob@example.com', complete)
|
||||||
|
|
||||||
|
# html body
|
||||||
|
msg = self.make_message(sender='bob@example.com',
|
||||||
|
html_body="<p>hello world</p>")
|
||||||
|
complete = msg.as_string()
|
||||||
|
self.assertIn('From: bob@example.com', complete)
|
||||||
|
|
||||||
|
# txt + html body
|
||||||
|
msg = self.make_message(sender='bob@example.com',
|
||||||
|
txt_body="hello world",
|
||||||
|
html_body="<p>hello world</p>")
|
||||||
|
complete = msg.as_string()
|
||||||
|
self.assertIn('From: bob@example.com', complete)
|
||||||
|
|
||||||
|
# everything
|
||||||
|
msg = self.make_message(sender='bob@example.com',
|
||||||
|
subject='meeting follow-up',
|
||||||
|
to='sally@example.com',
|
||||||
|
cc='marketing@example.com',
|
||||||
|
bcc='bob@example.com',
|
||||||
|
replyto='sales@example.com',
|
||||||
|
txt_body="hello world",
|
||||||
|
html_body="<p>hello world</p>")
|
||||||
|
complete = msg.as_string()
|
||||||
|
self.assertIn('From: bob@example.com', complete)
|
||||||
|
self.assertIn('Subject: meeting follow-up', complete)
|
||||||
|
self.assertIn('To: sally@example.com', complete)
|
||||||
|
self.assertIn('Cc: marketing@example.com', complete)
|
||||||
|
self.assertIn('Bcc: bob@example.com', complete)
|
||||||
|
self.assertIn('Reply-To: sales@example.com', complete)
|
Loading…
Reference in a new issue