|Build Status|

Mail'Em
=======

Full-featured e-mailing system: flexible, slim and sexy.

-  Unicode
-  Easy attachments
-  Inline images
-  E-Mail templates
-  Tools for unit-tests
-  Made perfect once and for all. Simple and cute :)

Here'a a full example:

.. code:: python

    from mailem import Message, Postman, Attachment, ImageAttachment
    from mailem.connection import SMTPConnection

    # Create the message
    messages = [
        # Message with attachments
        Message(
            ['kolypto@gmail.com'],
            u"Mail'em test",
            u"<b>yeah baby, it works!</b>",
            attachments = [
                Attachment(u'test.txt', open('test.txt').read())
            ]
        ),
        # Message with inline images (!)
        Message(
            ['kolypto@gmail.com'],
            u"Mail'em test with inline images",
            u"Cute: <img src='cid:cute.jpg' />",  # cid:<filename>
            attachments = [
                ImageAttachment('cute.jpg', open('cute.jpg').read(), 'inline')
            ]
        ),
    ]

    # Initialize a postman with SMTP connection to GMail
    postman = Postman('user@gmail.com',
                      SMTPConnection(
                          'smtp.gmail.com', 587,
                          'user@gmail.com', 'pass',
                          tls=True
                      ))

    # Send everything we have
    with postman.connect() as c:
        map(c.sendmail, messages)

Also see `Template <#template>`__.

Table of Contents
=================

Message
=======

.. code:: python

    Message(recipients, subject, html=None,
            text=None, sender=None, cc=None,
            bcc=None, attachments=None,
            reply_to=None, date=None, headers=None)

Construct a Message object.

Notes:

-  Full unicode support, and Unicode is the default
-  You can provide ``html`` or ``text`` contents. If both are specified
   -- the message will have an 'alternative' container, so the user will
   receive both HTML and plaintext. The client will choose which one to
   display.
-  E-Mail addresses, such as ``recipients`` and ``sender``, can be
   specified in one of the following formats:

   -  ``'user@example.com'``: Just an e-mail address
   -  ``('user@example.com', u'Honored User')``: email address with name

Arguments:

-  ``recipients``: List of recipients
-  ``subject``: Message subject
-  ``html``: Message body, HTML
-  ``text``: Message body, Text
-  ``sender``: Sender e-mail address. If not set explicitly, the default
   will be used on send
-  ``cc``: CC list
-  ``bcc``: BCC list
-  ``attachments``: List of attachments
-  ``reply_to``: Reply-to address
-  ``date``: Send date
-  ``headers``: Additional headers

Attachment
----------

.. code:: python

    Attachment(filename, data,
               content_type='application/octet-stream',
               disposition='attachment', headers=None)

File attachment information.

This can be provided to the ```Message`` <#message>`__ object on
construction.

-  ``filename``: Filename of attachment
-  ``data``: Taw file data
-  ``content_type``: File mimetype
-  ``disposition``: Content-Disposition: 'attachment', 'inline', ...
-  ``headers``: Additional headers for the attachment

ImageAttachment
---------------

.. code:: python

    ImageAttachment(filename, data,
                    disposition='attachment', headers=None)

Image attachment.

-  It guesses the Content-Type from the data stream
-  Supports 'inline' images: images embedded in the email. Useful for
   templates.

   Once an 'inline' image is created, its filename is used for
   'Content-ID', which allows to reference it in the HTML body:

   .. code:: python

       from mailem import Message, Attachment, ImageAttachment

       msg = Message(
           ['test@example.com'],
           'Hello',
           '<img src="cid:flowers.jpg" />',  # Referenced with "cid:<filename>"
           attachments=[
               ImageAttachment('flowers.jpg', open('flowers.jpg').read(), 'inline')
           ]
       )

Arguments:

-  ``filename``: Image attachment filename. Will also become
   'Content-ID' when inlined.
-  ``data``: The raw file data

Postman
=======

.. code:: python

    Postman(sender, connection)

Postman is the object you use to send messages through a configured
Connection object.

Example:

.. code:: python

    from mailem import Message, Postman
    from mailem.connection import SMTPConnection

    # Construct the message
    msg = Message(
        ['kolypto@gmail.com'],
        u"Mail'em test",
        u"<b>yeah baby, it works!</b>"
    )

    # Create the postman (see SMTPConnection)
    postman = Postman('user@gmail.com',
                      SMTPConnection(...))

    # Connect, and send the message
    with postman.connect() as c:
        c.sendmail(msg)

-  ``sender``: Default sender: e-mail or (name, email). Is used for
   messages which do not specify the sender address explicitly.
-  ``connection``: Connection object to use. See below.

``Postman.connect()``
~~~~~~~~~~~~~~~~~~~~~

Get connected Postman context manager.

``Postman.loopback()``
~~~~~~~~~~~~~~~~~~~~~~

Get a context manager which installs a LoopbackConnection on this
postman.

This allows you to record outgoing messages by mocking a Postman. See
```LoopbackConnection`` <#loopbackconnection>`__.

Connection
==========

Connection object represents a connection to a service which can send
e-mail messages for us.

SMTPConnection
--------------

.. code:: python

    SMTPConnection(host, port, username,
                   password, local_hostname=None,
                   ssl=False, tls=False)

SMTP connection.

See `smtplib <https://docs.python.org/2/library/smtplib.html>`__ for the
list of exceptions that may occur.

Example:

.. code:: python

    from mailem import Postman
    from mailem.connection import SMTPConnection

    postman = Postman('user@gmail.com',
                  SMTPConnection(
                      'smtp.gmail.com', 587,
                      'user@gmail.com', 'pass',
                      tls=True
                  ))

    with postman.connect() as c:
        c.sendmail(msg)

Arguments:

-  ``host``: SMTP server hostname
-  ``port``: SMTP server port number.
-  ``username``: User name to authenticate with
-  ``password``: Password
-  ``local_hostname``: FQDN of the local host for the HELO/EHLO command.
   When ``None``, is detected automatically.
-  ``ssl``: Use SSL protocol?
-  ``tls``: Use TLS handshake?

LoopbackConnection
------------------

.. code:: python

    LoopbackConnection()

Loopback connection allows to record all outgoing messages instead of
sending them.

You can install it manually:

.. code:: python

    from mailem import Postman
    from mailem.connection import LoopbackConnection

    lo = LoopbackConnection()
    postman = Postman('user@example.com', lo)
    #... send
    messages = lo.get_messages()

or you can mock an existing Postman with ``loopback()`` helper:

.. code:: python

    from mailem import Postman
    from mailem.connection import SMTPConnection

    postman = Postman('user@example.com',
                  SMTPConnection(...))

    with postman.loopback() as lo:
        # Send
        with postman.connect() as c:  # mocked!
            c.sendmail(msg)

    # Get
    sent_messages = lo.get_messages()

Loopback can be installed multiple times, and only top-level loopback
will catch the messages:

.. code:: python

    with postman.loopback() as lo1:
        with postman.loopback() as lo2:
            with postman.connect() as c:
                c.sendmail(msg)

    len(lo1)  #-> 0
    len(lo2)  #-> 1

Also note that ``LoopbackConnection`` subclasses ``list``, so all list
methods, including iteration, is available.

Template
========

.. code:: python

    Template(subject=None, html=None,
             text=None, attachments=None,
             defaults=None)

A templated e-mail.

By default, the Template uses Python's ``Template`` renderer, which
allows simple PHP-style substitution, but this can be overridden using
set\_renderer().

First, a template is defined:

.. code:: python

    from mailem import Attachment
    from mailem.template import Template

    signup = Template('Congrats $user, you've signed up!',
        'Welcome to our website!<br><img src="cid:logo.jpg" /> -- $domain',
        attachments=[
            Attachment('logo.jpg', open('logo.jpg').read(), 'inline'))
        ],
        defaults={'domain': 'localhost'}  # default template values
    )

Now, having the template, you render it to a ```Message`` <#message>`__
by calling it:

.. code:: python

    message = signup(['user@gmail.com'], dict(user='Honored User',))

Ready for sending! :)

-  ``subject``: Message subject template
-  ``html``: HTML message template, if any
-  ``text``: Text message template, if any
-  ``attachments``: Attachments for the template. Most probably, inline
   elements.
-  ``defaults``: Default template values, if required. The user can
   override these later.

``Template.set_renderer(Renderer)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Set renderer to be used with this template.

A Renderer is any class that can be constructed with a template string
argument, and called with template values dict to render it.

When no renderer was explicitly set, it defaults to
PythonTemplateRenderer.

-  ``Renderer``: Renderer class.

``Template.__call__(recipients, values, **kwargs)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Create a ``Message`` object using the template values.

-  ``recipients``: Message recipients list
-  ``values``: Dictionary with template values
-  ``**kwargs``: keyword arguments for the ```Message`` <#message>`__
   constructor

``Template.from_directory(path, subject_name='subject.txt', html_name='index.htm', text_name='index.txt', inline_rex='^i-(.*)')``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Convenience class method to import a directory as a template:

-  ``subject.txt`` is the subject string template
-  ``index.htm`` is the HTML template
-  ``index.txt`` is the plaintext template
-  All files matching the 'i-(\*)' format are attached as 'inline', and
   hence can be referenced in the template:

   E.g. file 'i-flower.jpg' can be inlined as
   ``<img src="cid:flower.jpg" />``.

-  All other files are just attachments.

Example:

.. code:: python

    signup = Template.from_directory('templates/signup/')

-  ``path``: Path to the directory
-  ``subject_name``: Subject template filename
-  ``html_name``: Html template filename
-  ``text_name``: Plaintext template filename
-  ``inline_rex``: Regular expression to match files that should be
   inlined.

   If the RegExp defines capture groups, group $1 will be used as the
   fact filename.

.. |Build Status| image:: https://api.travis-ci.org/kolypto/py-mailem.png?branch=master
   :target: https://travis-ci.org/kolypto/py-mailem
