Skip to main content

Email delivery for asyncio.

Project description

Mailers for asyncio

PyPI GitHub Workflow Status GitHub Libraries.io dependency status for latest release PyPI - Downloads GitHub Release Date Lines of code

Installation

pip install mailers

Usage

The package uses two main concepts: mailers and transports. The mailer is a class which abstracts you from the underlying transport and the transport does the actual message delivery.

from mailers import add_mailer, EmailMessage, send

add_mailer('smtp://user:password@localhost:25?timeout=2', name='default')

message = EmailMessage(
    to='user@localhost', from_address='from@localhost',
    subject='Hello', text_body='World!'
)
await send('user@localhost', message)

Or if you prefer more control on what is going one, take this more verbose path:

from mailers import Mailer, SMTPTransport, EmailMessage

mailer = Mailer(SMTPTransport('localhost', 25))

message = EmailMessage(
    to='user@localhost', from_address='from@localhost',
    subject='Hello', text_body='World!'
)
await mailer.send(message)

Compose messages

The arguments and methods of EmailMessage class are self-explanatory so here is some basic example:

from mailers import EmailMessage, Attachment

message = EmailMessage(
    to='user@localhost',
    from_address='from@example.tld',
    cc='cc@example.com',
    bcc=['bcc@example.com'],
    text_body='Hello world!',
    html_body='<b>Hello world!</b>',
    attachments=[
        Attachment('CONTENTS', 'file.txt', 'text/plain'),
    ]
)

# attachments can be added on demand:

with open('file.txt', 'r') as f:
    message.attach(f.read(), f.name, 'text/plain')

    # alternatively
    message.add_attachment(
        Attachment(f.read(), f.name, 'text/plain')
    )

cc, bcc, to, reply_to can be either strings or lists of strings.

A note about attachments

Accessing files is a blocking operation. You may want to use aiofiles or alternate library which reads files in non-blocking mode.

This package does not implement direct access to files at moment. This is something to do at later stage.

Transports

Preinstalled transports

All transport classes can be found in mailers.transports module.

Class Example URL Description
SMTPTransport smtp://user:pass@hostname:port?timeout=&use_tls=1 Sends mails using SMTP protocol.
InMemoryTransport not available Stores sent messages in the local variable. See an example below.
FileTransport file:///path/to/directory Writes sent messages into directory.
NullTransport null:// Does not perform any sending.
StreamTransport not available Writes message to an open stream. See an example below.
ConsoleTransport console:// Prints messages into stdout.
GMailTransport gmail://username:password Sends via GMail.
MailgunTransport mailgun://username:password Sends via Mailgun.

Special notes

InMemoryTransport

InMemoryTransport takes a list and writes outgoing mail into it. Read this list to inspect the outbox.

from mailers import InMemoryTransport, EmailMessage

message = EmailMessage(from_address='noreply@localhost')
mailbox = []
transport = InMemoryTransport(mailbox)
await transport.send(message)

assert message in mailbox

StreamTransport

Writes messages into the open stream.

from mailers import StreamTransport, EmailMessage
from io import StringIO

message = EmailMessage(from_address='noreply@localhost')

transport = StreamTransport(output=StringIO())
await transport.send(message)

output is any IO compatible object.

Custom transports.

Each transport must implement async def send(self, message: EmailMessage) -> None method. Preferably, inherit from BaseTransport class:

from mailers import BaseTransport, Mailer, EmailMessage


class PrintTransport(BaseTransport):
    async def send(self, message: EmailMessage) -> None
        print(str(message))


mailer = Mailer(transport=PrintTransport())

In order to make your transport to accept EmailURL instances, your transport class has to implement from_url class method:

from mailers import BaseTransport, EmailURL


class PrintTransport(BaseTransport):
    @classmethod
    def from_url(cls, url: EmailURL) -> "PrintTransport":
        return cls()

Add custom transport protocols.

Once you build a custom transport you can add it's URL to enable URL-based configurations.

from mailers import add_protocol_handler, Mailer, BaseTransport, EmailURL, create_from_url

class PrintTransport(BaseTransport):
    @classmethod
    def from_url(cls, url: EmailURL) -> "PrintTransport":
        return cls()

add_protocol_handler('myprotocol', PrintTransport)

mailer = Mailer(transport=create_from_url('myprotocol://'))

Note that the transport must to implement from_url method to accept URL parameters. Otherwise it will be constructed without any arguments passed to the __init__ method.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mailers-1.0.0.tar.gz (11.7 kB view hashes)

Uploaded Source

Built Distribution

mailers-1.0.0-py3-none-any.whl (10.7 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page