Adding support for APIStar >= 0.4.0

master
androiddrew 7 years ago
parent a7992a1bb0
commit 0a3a377f92

@ -1 +1,7 @@
# HISTORY # HISTORY
0.3.0 Added support for APIStar version >= 0.4.0
0.2.1 Pinned APIStar requirement to 0.3.9
APIStar 0.4.0 introduced breaking changes to the framework. Since Components have completely changed we are pinning the requirement for this release.

@ -17,15 +17,13 @@ Provides a simple interface to set up SMTP with your [APIStar](https://github.co
### Example Setup ### Example Setup
To send mail messages from your view functions you must include the 'MAIL' dictionary in your settings, the mail_component in your component list, and the Mail component as a dependency in your view. Here we have a minimally viable app capable of sending an email message and returning a 204 response code: To send mail messages from your view functions you must include a dictionary of mail options to the `MailComponent. Here we have a minimally viable app capable of sending an email message and returning a 204 response code:
```python ```python
from apistar import Route from apistar import App, Route
from apistar.frameworks.wsgi import WSGIApp as App from apistar_mail import MailComponent, Mail, Message
from apistar_mail import mail_component, Mail, Message
settings = { mail_options = {
'MAIL': {
'MAIL_SERVER': 'smtp.example.com', 'MAIL_SERVER': 'smtp.example.com',
'MAIL_USERNAME': 'me@example.com', 'MAIL_USERNAME': 'me@example.com',
'MAIL_PASSWORD': 'dontcommitthistoversioncontrol', 'MAIL_PASSWORD': 'dontcommitthistoversioncontrol',
@ -33,7 +31,6 @@ settings = {
'MAIL_USE_TLS': True, 'MAIL_USE_TLS': True,
'MAIL_DEFAULT_SENDER': 'me@example.com' 'MAIL_DEFAULT_SENDER': 'me@example.com'
} }
}
def send_a_message(mail: Mail): def send_a_message(mail: Mail):
@ -49,17 +46,16 @@ routes = [
] ]
components = [ components = [
mail_component MailComponent(**mail_options)
] ]
app = App( app = App(
settings=settings,
routes=routes, routes=routes,
components=components components=components
) )
if __name__ == '__main__': if __name__ == '__main__':
app.main() app.serve('127.0.0.1', 5000, debug=True)
``` ```

@ -4,4 +4,4 @@ __author__ = """Drew Bednar"""
__email__ = 'drew@androiddrew.com' __email__ = 'drew@androiddrew.com'
__version__ = '0.2.1' __version__ = '0.2.1'
from .mail import mail_component, Message, Mail # NOQA: F401 from .mail import MailComponent, Message, Mail # NOQA: F401

@ -11,7 +11,7 @@ from email.mime.text import MIMEText
from email.header import Header from email.header import Header
from email.utils import formatdate, formataddr, make_msgid, parseaddr from email.utils import formatdate, formataddr, make_msgid, parseaddr
from apistar import Settings, Component from apistar import Component
from .exc import MailUnicodeDecodeError, BadHeaderError from .exc import MailUnicodeDecodeError, BadHeaderError
@ -415,25 +415,25 @@ class Connection:
class Mail: class Mail:
"""Manages email messaging""" """Manages email messaging"""
def __init__(self, settings: Settings): def __init__(self, **mail_options):
""" """
Configure a new Mail manager Configure a new Mail manager
Args: Args:
settings: The application settings dictionary mail_options: A components setting dictionary
""" """
mail_config = settings.get('MAIL')
self.mail_server = mail_config.get('MAIL_SERVER', 'localhost') self.mail_server = mail_options.get('MAIL_SERVER', 'localhost')
self.mail_user = mail_config.get('MAIL_USERNAME') self.mail_user = mail_options.get('MAIL_USERNAME')
self.mail_password = mail_config.get('MAIL_PASSWORD') self.mail_password = mail_options.get('MAIL_PASSWORD')
self.mail_port = mail_config.get('MAIL_PORT', 25) self.mail_port = mail_options.get('MAIL_PORT', 25)
self.mail_use_tls = mail_config.get('MAIL_USE_TLS', False) self.mail_use_tls = mail_options.get('MAIL_USE_TLS', False)
self.mail_use_ssl = mail_config.get('MAIL_USE_SSL', False) self.mail_use_ssl = mail_options.get('MAIL_USE_SSL', False)
self.mail_default_sender = mail_config.get('MAIL_DEFAULT_SENDER') self.mail_default_sender = mail_options.get('MAIL_DEFAULT_SENDER')
self.mail_debug = mail_config.get('MAIL_DEBUG', False) self.mail_debug = mail_options.get('MAIL_DEBUG', False)
self.mail_max_emails = mail_config.get('MAIL_MAX_EMAILS') self.mail_max_emails = mail_options.get('MAIL_MAX_EMAILS')
self.mail_suppress_send = mail_config.get('MAIL_SUPPRESS_SEND', False) self.mail_suppress_send = mail_options.get('MAIL_SUPPRESS_SEND', False)
self.mail_ascii_attachments = mail_config.get('MAIL_ASCII_ATTACHMENTS', False) self.mail_ascii_attachments = mail_options.get('MAIL_ASCII_ATTACHMENTS', False)
def send(self, message): def send(self, message):
""" """
@ -461,4 +461,11 @@ class Mail:
return Connection(self) return Connection(self)
mail_component = Component(Mail, preload=True) class MailComponent(Component):
"""A component that injects an instance of `Mail` for sending emails"""
def __init__(self, **mail_options) -> None:
self.mail = Mail(**mail_options)
def resolve(self) -> Mail:
return self.mail

@ -7,7 +7,7 @@ with open('HISTORY.md') as history_file:
history = history_file.read() history = history_file.read()
requirements = [ requirements = [
'apistar~=0.3.9', 'apistar>=0.4',
] ]
test_requirements = [ test_requirements = [
@ -21,6 +21,7 @@ setup(
version='0.2.1', version='0.2.1',
description="A simple email Component for APIStar", description="A simple email Component for APIStar",
long_description=readme + '\n\n' + history, long_description=readme + '\n\n' + history,
long_description_content_type='text/markdown',
author="Drew Bednar", author="Drew Bednar",
author_email='drew@androiddrew.com', author_email='drew@androiddrew.com',
url='https://github.com/androiddrew/apistar-mail', url='https://github.com/androiddrew/apistar-mail',

@ -9,8 +9,7 @@ from apistar_mail.exc import MailUnicodeDecodeError, BadHeaderError
import pytest import pytest
settings = { test_mail_options = {
'MAIL': {
'MAIL_SERVER': 'smtp.example.com', 'MAIL_SERVER': 'smtp.example.com',
'MAIL_USERNAME': 'fake@example.com', 'MAIL_USERNAME': 'fake@example.com',
'MAIL_PASSWORD': 'secret', 'MAIL_PASSWORD': 'secret',
@ -19,7 +18,6 @@ settings = {
'MAIL_SUPPRESS_SEND': True, 'MAIL_SUPPRESS_SEND': True,
'MAIL_DEFAULT_SENDER': 'fake@example.com' 'MAIL_DEFAULT_SENDER': 'fake@example.com'
} }
}
def test_force_text_with_bytes_type(): def test_force_text_with_bytes_type():
@ -400,7 +398,7 @@ def test_message_charset():
def test_empty_subject_header(): def test_empty_subject_header():
mail = Mail(settings) mail = Mail(**test_mail_options)
msg = Message(sender="from@example.com", msg = Message(sender="from@example.com",
recipients=["foo@bar.com"]) recipients=["foo@bar.com"])
msg.body = "normal ascii text" msg.body = "normal ascii text"
@ -411,13 +409,13 @@ def test_empty_subject_header():
def test_message_default_sender(): def test_message_default_sender():
msg = Message(recipients=["foo@bar.com"]) msg = Message(recipients=["foo@bar.com"])
msg.body = "normal ascii text" msg.body = "normal ascii text"
mail = Mail(settings) mail = Mail(**test_mail_options)
mail.send(msg) mail.send(msg)
assert msg.sender == 'fake@example.com' assert msg.sender == 'fake@example.com'
def test_mail_send_message(): def test_mail_send_message():
mail = Mail(settings) mail = Mail(**test_mail_options)
mail.send = MagicMock() mail.send = MagicMock()
mail.send_message(sender="from@example.com", mail.send_message(sender="from@example.com",
recipients=["foo@bar.com"], recipients=["foo@bar.com"],
@ -426,7 +424,7 @@ def test_mail_send_message():
def test_message_ascii_attachments_config(): def test_message_ascii_attachments_config():
mail = Mail(settings) mail = Mail(**test_mail_options)
mail.mail_ascii_attachments = True mail.mail_ascii_attachments = True
msg = Message(sender="from@example.com", msg = Message(sender="from@example.com",
subject="subject", subject="subject",
@ -447,7 +445,7 @@ def test_message_as_bytes():
@patch('apistar_mail.mail.smtplib.SMTP') @patch('apistar_mail.mail.smtplib.SMTP')
def test_connection_configure_host_non_ssl(mock_smtp): def test_connection_configure_host_non_ssl(mock_smtp):
mail = Mail(settings) mail = Mail(**test_mail_options)
mail.mail_suppress_send = False mail.mail_suppress_send = False
mail.mail_use_tls = True mail.mail_use_tls = True
mock_smtp.return_value = MagicMock() mock_smtp.return_value = MagicMock()
@ -459,7 +457,7 @@ def test_connection_configure_host_non_ssl(mock_smtp):
@patch('apistar_mail.mail.smtplib.SMTP_SSL') @patch('apistar_mail.mail.smtplib.SMTP_SSL')
def test_connection_configure_host_ssl(mock_smtp_ssl): def test_connection_configure_host_ssl(mock_smtp_ssl):
mail = Mail(settings) mail = Mail(**test_mail_options)
mail.mail_suppress_send = False mail.mail_suppress_send = False
mail.mail_use_tls = False mail.mail_use_tls = False
mail.mail_use_ssl = True mail.mail_use_ssl = True
@ -469,7 +467,7 @@ def test_connection_configure_host_ssl(mock_smtp_ssl):
def test_connection_send_message(): def test_connection_send_message():
mail = Mail(settings) mail = Mail(**test_mail_options)
with mail.connect() as conn: with mail.connect() as conn:
conn.send = MagicMock() conn.send = MagicMock()
conn.send_message(sender="from@example.com", conn.send_message(sender="from@example.com",
@ -480,7 +478,7 @@ def test_connection_send_message():
@patch('apistar_mail.mail.smtplib.SMTP') @patch('apistar_mail.mail.smtplib.SMTP')
def test_connection_send_single(mock_smtp): def test_connection_send_single(mock_smtp):
mail = Mail(settings) mail = Mail(**test_mail_options)
mail.mail_suppress_send = False mail.mail_suppress_send = False
msg = Message(sender="from@example.com", msg = Message(sender="from@example.com",
recipients=["foo@bar.com"], recipients=["foo@bar.com"],
@ -494,7 +492,7 @@ def test_connection_send_single(mock_smtp):
def test_connection_send_ascii_recipient_single(): def test_connection_send_ascii_recipient_single():
mail = Mail(settings) mail = Mail(**test_mail_options)
msg = Message(sender="from@example.com", msg = Message(sender="from@example.com",
recipients=["foo@bar.com"], recipients=["foo@bar.com"],
body="normal ascii text") body="normal ascii text")
@ -506,7 +504,7 @@ def test_connection_send_ascii_recipient_single():
def test_connection_send_non_ascii_recipient_single(): def test_connection_send_non_ascii_recipient_single():
mail = Mail(settings) mail = Mail(**test_mail_options)
with mail.connect() as conn: with mail.connect() as conn:
with patch.object(conn, 'host') as host: with patch.object(conn, 'host') as host:
msg = Message(subject="testing", msg = Message(subject="testing",
@ -526,7 +524,7 @@ def test_connection_send_non_ascii_recipient_single():
@patch('apistar_mail.mail.smtplib.SMTP') @patch('apistar_mail.mail.smtplib.SMTP')
def test_connection_send_many(mock_smtp): def test_connection_send_many(mock_smtp):
mail = Mail(settings) mail = Mail(**test_mail_options)
mail.mail_suppress_send = False mail.mail_suppress_send = False
mail.mail_max_emails = 50 mail.mail_max_emails = 50
mock_smtp.return_value = MagicMock(spec=SMTP) mock_smtp.return_value = MagicMock(spec=SMTP)
@ -545,7 +543,7 @@ def test_connection_send_many(mock_smtp):
def test_bad_header_subject(): def test_bad_header_subject():
mail = Mail(settings) mail = Mail(**test_mail_options)
msg = Message(subject="testing\r\n", msg = Message(subject="testing\r\n",
body="testing", body="testing",
recipients=["to@example.com"]) recipients=["to@example.com"])
@ -555,7 +553,7 @@ def test_bad_header_subject():
def test_bad_header_subject_whitespace(): def test_bad_header_subject_whitespace():
mail = Mail(settings) mail = Mail(**test_mail_options)
msg = Message(subject="\t\r\n", msg = Message(subject="\t\r\n",
body="testing", body="testing",
recipients=["to@example.com"]) recipients=["to@example.com"])
@ -570,7 +568,7 @@ def test_bad_header_subject_with_no_trailing_whitespace():
This is a bit of a strange test but we aren't changing the bad_header check from flask_mail This is a bit of a strange test but we aren't changing the bad_header check from flask_mail
""" """
mail = Mail(settings) mail = Mail(**test_mail_options)
msg = Message(subject="testing\r\ntesting", msg = Message(subject="testing\r\ntesting",
body="testing", body="testing",
recipients=["to@example.com"]) recipients=["to@example.com"])
@ -580,7 +578,7 @@ def test_bad_header_subject_with_no_trailing_whitespace():
def test_bad_header_subject_trailing_whitespace(): def test_bad_header_subject_trailing_whitespace():
mail = Mail(settings) mail = Mail(**test_mail_options)
msg = Message(subject="testing\r\n\t", msg = Message(subject="testing\r\n\t",
body="testing", body="testing",
recipients=["to@example.com"]) recipients=["to@example.com"])
@ -590,7 +588,7 @@ def test_bad_header_subject_trailing_whitespace():
def test_bad_header_with_a_newline(): def test_bad_header_with_a_newline():
mail = Mail(settings) mail = Mail(**test_mail_options)
msg = Message(subject="\ntesting\r\ntesting", msg = Message(subject="\ntesting\r\ntesting",
body="testing", body="testing",
recipients=["to@example.com"]) recipients=["to@example.com"])
@ -600,7 +598,7 @@ def test_bad_header_with_a_newline():
def test_bad_header_with_newline_in_sender(): def test_bad_header_with_newline_in_sender():
mail = Mail(settings) mail = Mail(**test_mail_options)
msg = Message(subject="testing", msg = Message(subject="testing",
body="testing", body="testing",
sender='me\n@example.com', sender='me\n@example.com',

Loading…
Cancel
Save