python: sending a mail, fails when inside a "with" block
Question: I am wondering why this code

    
    
    test = smtplib.SMTP('smtp.gmail.com', 587)
    test.ehlo()
    test.starttls()
    test.ehlo()
    test.login('address','passw')
    test.sendmail(sender, recipients, composed)
    test.close()
    

works, but when written like this

    
    
    with smtplib.SMTP('smtp.gmail.com', 587) as s:
        s.ehlo()
        s.starttls()
        s.ehlo()
        s.login('address','passw')
        s.sendmail(sender, recipients, composed)
        s.close()
    

it fails with the message

    
    
    Unable to send the email. Error:  <class 'AttributeError'>
    Traceback (most recent call last):
      File "py_script.py", line 100, in <module>
        with smtplib.SMTP('smtp.gmail.com', 587) as s:
    AttributeError: __exit__
    

Why is this happening? (python3 on a raspberry pi) Thx


Answer: You are not using Python 3.3 or up. In your version of Python,
`smtplib.SMTP()` is not a context manager and cannot be using in a `with`
statement.

The traceback is directly caused because there is no [`__exit__`
method](https://docs.python.org/2/library/stdtypes.html#contextmanager.__exit__),
a requirement for context managers.

From the [`smptlib.SMTP()`
documentation](https://docs.python.org/3/library/smtplib.html#smtplib.SMTP):

> _Changed in version 3.3_ : Support for the `with` statement was added.

You can wrap the object in a context manager with
[`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager):

    
    
    from contextlib import contextmanager
    from smtplib import SMTPResponseException, SMTPServerDisconnected
    
    @contextmanager
    def quitting_smtp_cm(smtp):
        try:
            yield smtp
        finally:
            try:
                code, message = smtp.docmd("QUIT")
                if code != 221:
                    raise SMTPResponseException(code, message)
            except SMTPServerDisconnected:
                pass
            finally:
                smtp.close()
    

This uses the same exit behaviour as was added in Python 3.3. Use it like
this:

    
    
    with quitting_smtp_cm(smtplib.SMTP('smtp.gmail.com', 587)) as s:
        s.ehlo()
        s.starttls()
        s.ehlo()
        s.login('address','passw')
        s.sendmail(sender, recipients, composed)
    

Note that it'll close the connection for you.

