使用imap_tools实现邮箱登录后,如何保持IMAP连接避免LOGOUT错误?
问题描述
我编写了一个用于登录邮箱的辅助函数:
import imap_tools def mailbox_login(): try: with imap_tools.MailBoxUnencrypted(ENV["IMAP4_FQDN"]).login( ENV["RECEIVING_EMAIL_USER"], ENV["RECEIVING_EMAIL_PASSWORD"] ) as mailbox: print("Successfully logged into the mailbox.") return mailbox except imap_tools.MailboxLoginError as error: print(f"CRITICAL: Failed to login to the mailbox: {error}")
另有一个需要邮箱连接的函数:
def email_count(): """ Get all emails from the mail server via IMAP """ msgs = [] mailbox = mailbox_login() for msg in mailbox.fetch(): msgs.append(msg) return msgs
运行email_count()时出现如下错误:
imaplib.IMAP4.error: command SEARCH illegal in state LOGOUT, only allowed in states SELECTED
离开with语句范围后邮箱就会自动登出,请问如何在离开mailbox_login()后保持连接?
解决方案
问题出在with语句的特性上——当with块执行完毕,会自动触发对象的__exit__方法,而imap_tools的MailBox对象在这个方法里会自动执行登出操作,所以你从mailbox_login()返回的mailbox已经是登出状态,自然无法执行后续的fetch操作。以下两种方法可以解决这个问题:
方法一:移除with语句,手动管理连接生命周期
直接创建并登录邮箱实例,不使用with,这样就不会自动登出。但要记得在使用完邮箱后手动调用logout(),避免连接泄漏:
import imap_tools def mailbox_login(): try: mailbox = imap_tools.MailBoxUnencrypted(ENV["IMAP4_FQDN"]).login( ENV["RECEIVING_EMAIL_USER"], ENV["RECEIVING_EMAIL_PASSWORD"] ) print("Successfully logged into the mailbox.") return mailbox except imap_tools.MailboxLoginError as error: print(f"CRITICAL: Failed to login to the mailbox: {error}") return None # 使用时手动登出连接 def email_count(): msgs = [] mailbox = mailbox_login() if not mailbox: return msgs try: for msg in mailbox.fetch(): msgs.append(msg) finally: mailbox.logout() # 手动释放连接,避免资源浪费 return msgs
方法二:将mailbox_login改造为上下文管理器,在外层控制连接生命周期
用contextmanager装饰器把mailbox_login改成可以用with语句调用的上下文管理器,让连接的生命周期由外层的with块控制:
import imap_tools from contextlib import contextmanager @contextmanager def mailbox_login(): mailbox = None try: mailbox = imap_tools.MailBoxUnencrypted(ENV["IMAP4_FQDN"]).login( ENV["RECEIVING_EMAIL_USER"], ENV["RECEIVING_EMAIL_PASSWORD"] ) print("Successfully logged into the mailbox.") yield mailbox except imap_tools.MailboxLoginError as error: print(f"CRITICAL: Failed to login to the mailbox: {error}") yield None finally: if mailbox: mailbox.logout() # 自动在with块结束后登出 # 在外层使用with管理连接 def email_count(): msgs = [] with mailbox_login() as mailbox: if not mailbox: return msgs for msg in mailbox.fetch(): msgs.append(msg) return msgs
内容的提问来源于stack exchange,提问作者Trouble Bucket
相关产品推荐
相关产品推荐

