Simple Python Old Email Eraser From Inbox This is a old email eraser that deletes old unwanted email from your gmail or yahoo inbox, cause guys really we all had the nightmare of selecting and deleting 50 old emails at a time when their are thousands, well this one uses simple python imaplib library to do that, and even shows the email that its deleting with sender name And Deletes up to a certain date that we specify

try:
    import imaplib
    import argparse
    import email
    import webbrowser
except Exception as e:
    print e

def Connect_And_Login(uname, passwd, bedate):

    if uname.endswith('@gmail.com'):
        mail = imaplib.IMAP4_SSL('imap.gmail.com')
        inbox = "INBOX"
        url = 'https://www.google.com/settings/security/lesssecureapps'

    elif uname.endswith('@yahoo.com'):
        mail = imaplib.IMAP4_SSL('imap.mail.yahoo.com')
        inbox = "Inbox"
        url = 'https://login.yahoo.com/account/security#less-secure-apps'

    else:
        print 'Enter The Email Address Carefully'
        exit(0)

    try:
        mail.login(uname, passwd)

    except Exception as e:
        print "\n\nFailed to login Allow less secure apps from your email settings and try again\n\n"
        webbrowser.open(url)
        exit(0)

    Mark_emails_as_Deleted(mail, bedate, inbox)
    #print mail.list()
    return

def Mark_emails_as_Deleted(mail, bedate, inbox):
    mail.select(inbox)
    result, data = mail.search(None, '(BEFORE "'+str(bedate)+'")')

    if result == 'OK':
        for num in data[0].split():
            mail.store(num, '+FLAGS', '\\Deleted')

            res, msg_data = mail.fetch(str(num), '(RFC822)')
            for response_part in msg_data:
                if isinstance(response_part, tuple):
                    msg = email.message_from_string(response_part[1])
                    print '\nDleting.....\n'
                    for header in [ 'subject', 'to', 'from' ]:
                        print '%-8s: %s' % (header.upper(), msg[header])

        expunge_Them(mail)

    return

def expunge_Them(mail):

    mail.expunge()
    mail.close()
    mail.logout()
    return

def main():

    parser = argparse.ArgumentParser()
    parser.add_argument("uname", help="Your email username like shantanu@gmail.com")
    parser.add_argument("path", help="Enter your email password")
    parser.add_argument("bedate", help="Enter Date, Before this date all emails will be cleaned WRITE LIKE 01-Jan-2016")
    args = parser.parse_args()
    #print '(BEFORE "'+str(bedate)+'")'

    Connect_And_Login(args.uname, args.path, args.bedate)

if __name__ == "__main__":
    main()
One nice programming principle is the principle of least astonishment, which you violate with your code. Imagine someone using your module and calling Connect_And_Login. If it was me I would be very astonished that suddenly many of my emails are deleted! At least the function does not have today as a default bedate, otherwise it would be a disaster waiting to happen.

It would be a lot better if that function just returned the mail object, which the function Mark_emails_as_Deleted then takes as parameter.

mail, inbox = connect_and_login(args.uname, args.path)
mark_emails_as_deleted(mail, args.bedate, inbox)


(Before the return in Connect_And_Login was actually superfluous,  because python automatically returns None at the end of a function body



And here we are at another thing. You should use a consistent naming scheme for your variables. Python actually has an official style guide, PEP8, which programmers are encouraged to follow to improve readability. It recommends using lower_case for variables and functions (and PascalCase for classes).

It also recommends avoiding unnecessary white-space, such as between an if block and an else block, or between try and except.



Excepting an import error and then just printing the error does you no good. It will print the error, try to continue running the code, encounter wherever that module is actually needed and fail there with another cryptic error code. The output saying that it was actually an import error is buried above the stacktrace. It is better to just let it fail as an import error or at least terminate the program there.



The different email provider could be stored in a dictionary mapping to their data. This way it is more easily extendable to more providers:

supported_provider = {"gmail.com":{"mail": "imap.gmail.com",
                                   "inbox": "INBOX",
                                    "url": 'https://www.google.com/settings/security/lesssecureapps'},
                       "yahoo.com": {...}}
provider = supported_provider[uname.split("@")[-1]]
mail = imaplib.IMAP4_SSL(provider['mail'])
inbox = provider['inbox']
url = provider['url']




It is in general faster, more readable and extendable to use str.format instead of string additions:

result, data = mail.search(None, '(BEFORE "{}")'.format(bedate))




Lastly, you could use the nice functionality of argparse to validate your input as a date as explained e.g. in this answer:

parser.add_argument('bedate', type=lambda s: datetime.datetime.strptime(s, '%d-%b-%Y'), help="Enter Date, Before this date all emails will be cleaned. Format: 01-Jan-2016")


%b is the abbreviated month name *in the current locale. So in English it will beJan, Feb, ..., Dec, while e.g. in German it will beJan, Feb, ..., Dez`. I guess gmail/yahoo can actually handle this because it is also localized for the user.