"""
    mailthon.postman
    ~~~~~~~~~~~~~~~~

    This module implements the central Postman object.

    :copyright: (c) 2015 by Eeo Jun
    :license: MIT, see LICENSE for details.
"""

from contextlib import contextmanager
from smtplib import SMTP
from .response import SendmailResponse
from .helpers import encode_address


class Session(object):
    def __init__(self, **kwargs):
        self.conn = SMTP(**kwargs)

    def setup(self):
        self.conn.ehlo()

    def teardown(self):
        self.conn.quit()

    def send(self, envelope):
        rejected = self.conn.sendmail(
            encode_address(envelope.sender),
            [encode_address(k) for k in envelope.receivers],
            envelope.string(),
        )
        return SendmailResponse(
            self.conn.noop(),
            rejected,
        )


class Postman(object): 
    """
    Encapsulates a connection to a server, created by
    some *session* class and provides middleware
    management and setup/teardown goodness.

    :param session: Session class to be used.
    :param middleware: Middlewares to use.
    :param **kwargs: Options to pass to session class.
    """

    def __init__(self, session=Session, middlewares=(), **options):
        self.session = session
        self.options = options
        self.middlewares = list(middlewares)

    def use(self, middleware):
        """
        Use a certain callable *middleware*, i.e.
        append it to the list of middlewares, and
        return it so it can be used as a decorator.
        """
        self.middlewares.append(middleware)
        return middleware

    @contextmanager
    def connection(self):
        """
        A context manager that returns a connection
        to the server using some *session*.
        """
        conn = self.session(**self.options)
        try:
            conn.setup()
            for item in self.middlewares:
                item(conn)
            yield conn
        finally:
            conn.teardown()

    def send(self, envelope):
        """
        Sends an *enclosure* and return a response
        object.
        """
        with self.connection() as conn:
            conn.send(envelope)

@@ -352,200 +352,1076 @@
     
-def __init__(self, **kwargs):%0A        self.conn = SMTP(**kwargs)%0A%0A    def setup(self):%0A        self.conn.ehlo()%0A%0A    def teardown(self):%0A        self.conn.quit()%0A%0A    def send(self, envelope):
+%22%22%22%0A    Represents a connection to some server or external%0A    service, e.g. some REST API. The underlying transport%0A    defaults to SMTP but can be subclassed.%0A%0A    :param **kwargs: Keyword arguments to be passed to%0A        the underlying transport.%0A    %22%22%22%0A%0A    def __init__(self, **kwargs):%0A        self.conn = SMTP(**kwargs)%0A%0A    def setup(self):%0A        %22%22%22%0A        Setup the connection. It is recommended that%0A        the connection is already made in the %60%60__init__%60%60%0A        method and not lazily until %60%60setup%60%60 is called.%0A        %22%22%22%0A        self.conn.ehlo()%0A%0A    def teardown(self):%0A        %22%22%22%0A        Tear down the connection. Is called regardless%0A        if an exception was raised or not.%0A        %22%22%22%0A        self.conn.quit()%0A%0A    def send(self, envelope):%0A        %22%22%22%0A        Send an *envelope* which may be an envelope%0A        or an enclosure-like object, see%0A        :class:%60~mailthon.enclosure.Enclosure%60 and%0A        :class:%60~mailthon.envelope.Envelope%60, and%0A        returns a :class:%60~mailthon.response.SendmailResponse%60%0A        object.%0A        %22%22%22
 %0A   
@@ -1878,16 +1878,117 @@
 oodness.
+ Basically%0A    is a layer of indirection over session objects,%0A    allowing for pluggable transports.
 %0A%0A    :p

add docstrings [skip ci]