1
\$\begingroup\$

I made this small program which takes some inputs (emails) and after looking if they exist it looks for the respective id and delete it. It works (I use it with crontab) but seems redundant. I had several problems with split and decode function, mainly because decode gives me lists of strings spaced by a tab. I would like to optimize the code and get rid of all these for loops, because the code seems long and sloppy. Any tips?

bastards = [
"team@mail.kamernet.nl",
"supportdesk@kamernet.nl",
"noreply@pararius.nl",
]

with imaplib.IMAP4_SSL(host='imap.gmail.com',port=993) as imap:
    imap.login(email,password)
    imap.select('INBOX',readonly=False)
    listy = []
    for bastard in bastards:
        resp_code, respy = imap.search(None, f'FROM {bastard}')
        respy = respy[0].decode().split()
        listy.append(respy)
    listy = [x for x in listy if x]
    for i in listy:
        for j in i:
            imap.store(j,'+FLAGS','\\Deleted')
            print('email_deleted')
    imap.expunge()
    imap.close()
    imap.logout()

\$\endgroup\$
1
\$\begingroup\$

The listy comprehension could be refactored into a generator:

def search_emails(imap, senders):
    for sender in senders:
        typ, data = imap.search(None, f'FROM {sender}')
        msg_nums = data[0].decode().split()
        
        # check for content here
        if msg_nums:
            yield msg_nums


with imaplib.IMAP4_SSL(host='imap.gmail.com', port=993) as imap:
    imap.login(email, password)
    imap.select('INBOX', readonly=False)

    # also, visually breaking up the program with newlines
    # makes it more readable
    for email in search_emails(imap, senders):
        for j in email:
            imap.store(j, '+FLAGS', '\\Deleted')
            print('email_deleted')

This evaluates lazily, so you don't have to keep all of your emails in a list that you filter out later. It filters them as you iterate over the generator.

for j in email

This inner loop doesn't need to be there, per the docs on IMAP4.store, it takes a set of messages, not one at a time:

    ~snip~
    for emails in search_emails(imap, senders):
        imap.store(emails, '+FLAGS', '\\Deleted')
        print('emails_deleted')

The official docs also don't have the decode section when parsing the output from IMAP4.search:

typ, data = M.search(None, 'ALL')

for num in data[0].split(): # <------ Here
   M.store(num, '+FLAGS', '\\Deleted')

I haven't used this library, so I won't speculate on why your snippet is different, but just a note.

Variable Names

Things like for i in iterable and for j in iterable usually imply indices. I'd change the names here to more accurately represent what they are, it makes the code more readable

Function Params

Add spaces in between your function parameters:

# go from this
imap.login(email,password)

# to this
imap.login(email, password)

Passwords

You can use the getpass library for a masked password prompt:

from getpass import getpass

password = getpass()

I'm not sure how you're currently implementing storing the password, since you haven't included it.

\$\endgroup\$
1
  • \$\begingroup\$ incredible explanation \$\endgroup\$
    – Alex D'ago
    Sep 8 at 10:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.