Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to prevent SMTP Connection Timeout? smtplib, python

I have a script that I need to run indefinitely. The script is set to e-mail me confirmations of certain steps being completed on a daily basis. I am trying to use smtplib for this.

The initial connection is set up so that I will enter my login (written into the script) and a password using getpass at the very initiation of the script. I do not want to leave my password written into the script or even reference by the script say in a config file. Therefore, I want to enter the password at initiation and leave the smtp connection in place.

Re-connecting to the smtp connection as required in the script would defeat the point of being able to step away from the script entirely and leave it running indefinitely.

The example code that I am working with at the moment looks like this:

import smtplib
import getpass

smtpObj = smtplib.SMTP('smtp.gmail.com',587)
smtpObj.ehlo()
smtpObj.starttls()
smtpObj.login('[email protected]',password = getpass.getpass('Enter Password: '))

Then I enter the password and the output is:

(235, b'2.7.0 Accepted')

So this all works fine.

The problem is then that the script needs to pause for anywhere from a few minutes to potentially a few days depending on the time. This is achieved using a while loop with time conditions until a certain time when the send function will be called:

smtpObj.sendmail('[email protected]','[email protected]','This is a test')

However, after a period of about 20/30 mins it seems (i.e. if the pause is sufficient). Then the smptObj.sendmail call will fail due to a timeout error.

The specific error is as follows:

SMTPSenderRefused: (451, b'4.4.2 Timeout - closing connection. l22sm2469172wre.52 - gsmtp', '[email protected]')

I have so far tried the following:

Instantiating the connection object with the following timeout parameterisation:

smtpObj = smtplib.SMTP('smtp.gmail.com',587,timeout=None)
smtpObj = smtplib.SMTP('smtp.gmail.com',587,timeout=86400)

Neither of these seem to supress the 'timeout' of the connection (i.e. the same problem persists).

I have also tried this solution approach suggested in this post:

How can I hold a SMTP connection open with smtplib and Python?

However, this has not worked either!

I do want to try and avoid the solution where I would have to re-connect each time when I wanted to send the e-mail, because I only want to enter the password for the connection the once manually, rather than writing it into the script either directly or indirectly.

There surely is a way to deal with the timeout issue! If anyone can help here, then please let me know! Though, if you think that the more 'obvious' solution of re-connecting just before the script needs to send an e-mail is the better way to go, then please let me know.

Thank you!...

like image 519
agftrading Avatar asked Mar 09 '18 23:03

agftrading


People also ask

What does Smtplib SMTP do?

The smtplib module defines an SMTP client session object that can be used to send mail to any internet machine with an SMTP or ESMTP listener daemon. For details of SMTP and ESMTP operation, consult RFC 821 (Simple Mail Transfer Protocol) and RFC 1869 (SMTP Service Extensions).

What is SMTP connection timeout?

If you get a 'Connection Timed out' error when sending email via our SMTP relay, it means that you are using an smtp port that is blocked. By default, we recommend using port 25 when sending emails via our SMTP relay. However, some ISPs may block port 25.

Is Smtplib inbuilt in Python?

Python comes with the built-in smtplib module for sending emails using the Simple Mail Transfer Protocol (SMTP). smtplib uses the RFC 821 protocol for SMTP.


1 Answers

If you don't want to include sensitive credentials in a script, you should use env vars.

From a terminal shell (outside of python):

$ EXPORT secretVariable=mySecretValue
$ echo $secretVariable
$ mySecretValue
$

So to leverage this in your code...

>>> import os
>>> myPW = os.getenv('secretVariable')
>>> myPW
'mySecretVal'
>>>

By doing this, you don't have to manually type in the password. Beyond that, it's not very practical to attempt to leave an idle SMTP connection open for potentially days at a time, just implement a try/except structure..

import smtplib
import os

def smtp_connect():
    # Instantiate a connection object...
    password = os.getenv('secretVariable')
    smtpObj = smtplib.SMTP('smtp.gmail.com',587)
    smtpObj.ehlo()
    smtpObj.starttls()
    smtpObj.login('[email protected]',password=password)
    return smtpObj

def smtp_operations():
    try:
        # SMTP lib operations...
        smtpObj.sendmail('[email protected]','[email protected]','This is a test')
        # SMTP lib operations...

    except Exception: # replace this with the appropriate SMTPLib exception

        # Overwrite the stale connection object with a new one
        # Then, re-attempt the smtp_operations() method (now that you have a fresh connection object instantiated).
        smtpObj = smtp_connect() 
        smtp_operations()

smtpObj = smtp_connect()
smtp_operations()

By replacing except Exception with the actual SMTP Exception that gets raised when you have a stale connection, you'll be sure you're not catching exceptions that don't pertain to the connection being stale.

So, using try/except, the script will attempt to preform the SMTP operations. If the connection is stale, it will instantiate a fresh connection object and then attempt to re-execute itself with the fresh connection object.

like image 55
Ephexx Avatar answered Oct 17 '22 23:10

Ephexx