how to send mail in python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# smtplib -> https://docs.python.org/3/library/smtplib.html
import smtplib
# contextlib -> https://docs.python.org/3/library/contextlib.html
from contextlib import contextmanager
# email -> https://docs.python.org/3/library/email.examples.html#email-examples
from email import encoders
from email.header import Header
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddr

SMTP_SERVERS = ['your smtp servers']


def _format_addr(s):
    ''' format the address

    '''
    name, addr = parseaddr(s)
    return formataddr((Header(name, 'utf-8').encode(), addr))


def generate_mail(from_addr,
                  to_addr,
                  cc_addr,
                  subject,
                  body,
                  mime_type='plain'):
    '''plain/text text/html
    '''
    msg = MIMEText(body, mime_type, 'utf-8')
    msg['From'] = _format_addr(from_addr)
    msg['To'] = ",".join([_format_addr(x) for x in to_addr])
    msg['Cc'] = ",".join([_format_addr(x) for x in cc_addr])
    msg['Subject'] = Header(subject, 'utf-8').encode()
    return msg


def send_mail(from_addr, to_addr, cc_addr, subject, body, mime_type='plain'):
    server = smtplib.SMTP(SMTP_SERVERS[0], 25)
    server.set_debuglevel(1)
    msg = generate_mail(from_addr, to_addr, cc_addr, subject, body, mime_type)
    server.sendmail(from_addr, to_addr + cc_addr, msg.as_string())
    server.quit()

# 1. to implement context manager, use decorator @contextmanager
# and yield

@contextmanager
def send_mail_context():
    '''context manager

    with send_mail_context()
    '''
    try:
        # 1 doing work BEFORE the code goes into with block
        print('before yield')
        server = smtplib.SMTP(SMTP_SERVERS[0], 25)
        # 2 yield back to with/caller
        yield server
    except Exception as e:
        print(e)
    finally:
        # 4. doing cleanup work
        server.quit()
        print('in finally')


# 2 implement contextmanager with class
class SMTPCT(object):
    '''

    1. override __enter__ __exit__
    2. in __enter__, return value
    '''
    def __init__(self):
        self.smtp_server = None

    def __enter__(self):
        # 1 doing work BEFORE the code goes into with block
        print('in enter')
        self.smtp_server = smtplib.SMTP(SMTP_SERVERS[0], 25)
        # 2 return back to with/caller
        return self.smtp_server

    def __exit__(self, type, value, traceback):
        # 4. doing cleanup work
        print('in exit')
        self.smtp_server.quit()


def send_mail_in_context_class(from_addr,
                               to_addr,
                               cc_addr,
                               subject,
                               body,
                               mime_type='plain'):
    msg = generate_mail(from_addr, to_addr, cc_addr, subject, body, mime_type)
    with SMTPCT() as server:
        # 3. do the actual job
        server.sendmail(from_addr, to_addr + cc_addr, msg.as_string())
        print('in with block')
    print('sent')


def send_mail_in_context_yield(from_addr,
                         to_addr,
                         cc_addr,
                         subject,
                         body,
                         mime_type='plain'):
    msg = generate_mail(from_addr, to_addr, cc_addr, subject, body, mime_type)
    with send_mail_context() as server:
        # 3. do the actual job
        server.sendmail(from_addr, to_addr + cc_addr, msg.as_string())
        print('in with block')
    print('sent')


if __name__ == '__main__':
    pass
    # 1. send mail with contextmanager implemented in class
    #  send_mail_in_context_class('your_from_address',
                               #  ['to_address'], [],
                               #  'test', 'this is test mail')
    # 2. send mail with contextmanager implemented with yield
    #  send_mail_in_context_yield('your_from_address',
                               #  ['to_addr'], [],
                               #  'test', 'this is test mail')