18.4. 邮箱-处理各种格式的邮箱

此模块定义了两个类MailboxMessage,用于访问和操作磁盘上的邮箱及其包含的消息。 Mailbox提供了从键到消息的类似字典的 Map。 Message以特定于格式的状态和行为扩展email.message模块的Message类。支持的邮箱格式为 Maildir,mbox,MH,Babyl 和 MMDF。

See also

  • Module email

  • 表示和处理消息。

18.4.1. 邮箱对象

Mailbox类定义一个接口,不能实例化。而是,特定于格式的子类应继承Mailbox,并且您的代码应实例化特定的子类。

Mailbox界面类似于字典,带有对应于消息的小键。密钥由将与它们一起使用的Mailbox实例发出,并且仅对该Mailbox实例有意义。即使修改了相应的消息(例如,pass将其替换为另一条消息),键也会 continue 标识消息。

可以使用类集方法add()将消息添加到Mailbox实例,并使用del语句或类集方法remove()discard()删除消息。

Mailbox接口语义在某些方面与字典语义有所不同。每次请求消息时,都会根据邮箱的当前状态生成新的表示形式(通常为Message实例)。同样,将消息添加到Mailbox实例时,将复制提供的消息表示形式的内容。在任何情况下,都不会引用Mailbox实例保留的消息表示形式。

默认的Mailbox迭代器遍历消息表示形式,而不是键,而不是默认的字典迭代器。此外,在迭代过程中修改邮箱是安全且定义明确的。创建迭代器后,添加到邮箱的邮件将不会被迭代器看到。在迭代器生成之前从邮箱中删除的消息将被静默跳过,尽管如果随后删除了相应的消息,则使用来自迭代器的键可能会导致KeyError异常。

Warning

修改可能被某些其他过程同时更改的邮箱时,请务必谨慎。用于此类任务的最安全的邮箱格式是 Maildir;try避免使用诸如 mbox 之类的单文件格式进行并发写入。如果要修改邮箱,则必须*在读取文件中的任何消息之前或pass添加或删除消息进行任何更改之前,pass调用lock()unlock()方法来锁定它。未能锁定邮箱会带来丢失消息或损坏整个邮箱的风险。

Mailbox个实例具有以下方法:

参数* message 可以是Message实例,email.message.Message实例,字符串或类似文件的对象(应在文本模式下打开)。如果 message *是适当的特定于格式的Message子类的实例(例如,如果它是mboxMessage实例,而这是mbox实例),则使用其特定于格式的信息。否则,将使用特定于格式的信息的合理默认值。

如果不存在此类消息,则将方法称为remove()delitem()引发KeyError异常,但如果将方法称为discard()则不引发异常。如果基础邮箱格式支持其他进程的并发修改,则discard()的行为可能是首选的。

add()一样,参数* message 可以是Message实例,email.message.Message实例,字符串或类似文件的对象(应在文本模式下打开)。如果 message 是适当的特定于格式的Message子类的实例(例如,如果它是mboxMessage实例而这是mbox实例),则使用其特定于格式的信息。否则,当前对应于 key *的消息的格式特定信息将保持不变。

Note

iter()的行为与字典不同,后者在键上进行迭代。

Note

与其他消息表示不同,类似文件的表示不一定独立于创建它们的Mailbox实例或基础邮箱。每个子类都提供了更具体的文档。

Note

与字典不同,不支持关键字参数。

18.4.1.1. Maildir

由于历史原因,* factory 默认为rfc822.Message,而 dirname 则这样命名,而不是 path 。对于行为类似于其他Mailbox子类的实例的Maildir实例,请将 factor **设置为None

Maildir 是为 qmail 邮件传输代理发明的基于目录的邮箱格式,现在已被其他程序广泛支持。 Maildir 邮箱中的邮件存储在公共目录结构中的单独文件中。这种设计允许多个不相关的程序访问和修改 Maildir 邮箱而不会破坏数据,因此不需要文件锁定。

Maildir 邮箱包含三个子目录,即:tmpnewcur。在tmp子目录中暂时创建消息,然后将其移到new子目录中以完成传递。邮件用户代理可以随后将消息移动到cur子目录,并将有关消息状态的信息存储在附加到其文件名的特殊“信息”部分中。

还支持使用 Courier 邮件传输代理引入的样式的文件夹。如果'.'是名称中的第一个字符,则该主邮箱的任何子目录均被视为文件夹。文件夹名称由Maildir表示,不带前导'.'。每个文件夹本身就是一个 Maildir 邮箱,但不应包含其他文件夹。相反,使用'.'表示逻辑嵌套以界定级别,例如“ Archived.2005.07”。

Note

Maildir 规范要求在某些消息文件名中使用冒号(':')。但是,某些 os 不允许在文件名中使用此字符。如果希望在此类 os 上使用类似 Maildir 的格式,则应指定另一个字符来代替。感叹号('!')是一个流行的选择。例如:

import mailbox
mailbox.Maildir.colon = '!'

colon属性也可以按实例设置。

Maildir实例除以下内容外,还具有Mailbox的所有方法:

Maildir实现的某些Mailbox方法值得特别说明:

这些方法根据当前进程 ID 生成唯一的文件名。使用多个线程时,除非发生线程协调以避免使用这些方法同时操作同一邮箱的情况,否则可能会发生未检测到的名称冲突,并导致邮箱损坏。

See also

18.4.1.2. mbox

mbox 格式是在 Unix 系统上存储邮件的经典格式。 mbox 邮箱中的所有邮件都存储在一个文件中,每封邮件的开头由一行表示,其前五个字符为“发件人”。

存在 mbox 格式的几种变体,以解决原始版本中的感知缺陷。出于兼容性考虑,mbox实现了原始格式,有时称为* mboxo 。这意味着 Content-Length *Headers(如果存在)将被忽略,并且在存储消息时,在消息正文行的开头出现的所有“ From”都将转换为“> From”,尽管出现“阅读消息时,> From“不会转换为” From“。

mbox实现的某些Mailbox方法值得特别说明:

See also

18.4.1.3. MH

MH 是为邮件用户代理 MH 消息处理系统发明的基于目录的邮箱格式。 MH 邮箱中的每个邮件都驻留在其自己的文件中。 MH 邮箱除包含邮件外,还可以包含其他 MH 邮箱(称为* folders )。文件夹可能无限期嵌套。 MH 邮箱还支持 sequences *,这是命名列表,用于对消息进行逻辑分组而不将其移动到子文件夹。序列在每个文件夹中的名为.mh_sequences的文件中定义。

MH类可操纵 MH 邮箱,但不会try模拟 mh 的所有行为。特别是,它不会修改并且不受 mh 用来存储其状态和配置的context.mh_profile文件的影响。

MH实例除以下内容外,还具有Mailbox的所有方法:

Note

此操作已使已发布的密钥无效,因此以后不应使用。

MH实现的某些Mailbox方法值得特别说明:

See also

18.4.1.4. Babyl

Babyl 是 Emacs 附带的 Rmail 邮件用户代理使用的单文件邮箱格式。消息的开头由包含两个字符 Control-Underscore('\037')和 Control-L('\014')的行指示。消息的结尾由下一条消息的开头或在最后一条消息的情况下由包含 Control-Underscore('\037')字符的行指示。

Babyl 邮箱中的邮件具有两组标题,原始标题和所谓的可见标题。可见 Headers 通常是原始 Headers 的子集,这些 Headers 已被重新格式化或删节以使其更具吸引力。 Babyl 邮箱中的每封邮件还带有一个随附的* labels *列表,或记录有关该邮件的额外信息的短字符串,并且在 Babyl 选项部分中保留了邮箱中找到的所有用户定义标签的列表。

Babyl实例除以下内容外,还具有Mailbox的所有方法:

Note

检查实际消息以确定邮箱中存在哪些标签,而不是查阅 Babyl 选项部分中的标签列表,但是只要修改邮箱,Babyl 部分都会更新。

Babyl实现的某些Mailbox方法值得特别说明:

See also

18.4.1.5. MMDF

MMDF 是一种单文件邮箱格式,是为邮件传输代理多通道备忘录分发设施发明的。每条消息的格式与 mbox 消息的格式相同,但前后均用包含四个 Control-A('\001')字符的行括起来。与 mbox 格式一样,每条消息的开头均由前五个字符为“ From”的行表示,但是在存储消息时,其他出现的“ From”不会转换为“> From”,因为多余的消息分隔符行会阻止将这种情况误认为是后续消息的开始。

MMDF实现的某些Mailbox方法值得特别说明:

See also

  • 锡的 mmdf 手册页

  • 新闻阅读器 tin 的文档中的 MMDF 格式规范。

  • MMDF

  • Wikipedia 上描述多 Channel 备忘录分发工具的文章。

18.4.2. 讯息对象

如果Ellipsis* message ,则新实例以默认的空状态创建。如果 message email.message.Message实例,则复制其内容;此外,如果 message Message实例,则将尽可能转换任何特定于格式的信息。如果 message *是字符串或文件,则它应包含 RFC 2822兼容的消息,该消息将被读取和解析。

子类提供的特定于格式的状态和行为各不相同,但通常仅支持不特定于特定邮箱的属性(尽管这些属性特定于特定邮箱格式)。例如,单文件邮箱格式的文件偏移和基于目录的邮箱格式的文件名不会保留,因为它们仅适用于原始邮箱。但是保留诸如消息已被用户阅读还是标记为重要之类的状态,因为它适用于消息本身。

无需使用Message实例来表示使用Mailbox实例检索的消息。在某些情况下,生成Message表示形式所需的时间和内存可能不可接受。在这种情况下,Mailbox实例还提供字符串和类似文件的表示形式,并且在初始化Mailbox实例时可以指定自定义消息工厂。

18.4.2.1. MaildirMessage

通常,邮件用户代理应用程序在用户第一次打开和关闭邮箱之后,将new子目录中的所有消息移至cur子目录,记录该消息是否旧,无论是否已被实际读取。 cur中的每个消息都有一个“ info”部分添加到其文件名中,以存储有关其状态的信息。 (某些邮件阅读器可能还会在new中的消息中添加“信息”部分.)“信息”部分可能采用以下两种形式之一:它可能包含“ 2”,后跟标准化标志列表(例如,“ 2” FR”),也可以包含“ 1”,后跟所谓的实验信息。 Maildir 消息的标准标志如下:

Flag Meaning Explanation
D Draft Under composition
F Flagged 标记为重要
P Passed 转发,重新发送或退回
R Replied Replied to
S Seen Read
T Trashed 标记为以后删除

MaildirMessage个实例提供以下方法:

Note

无论是否已读取邮件,访问邮件后,通常会将邮件从new移到cur。如果"S" in msg.get_flags()True,则已读取消息msg

当基于mboxMessageMMDFMessage实例创建MaildirMessage实例时,将Ellipsis* Status X-Status *Headers,并进行以下转换:

Resulting state mboxMessageMMDFMessage状态
"cur" subdirectory O flag
F flag F flag
R flag A flag
S flag R flag
T flag D flag

当基于MHMessage实例创建MaildirMessage实例时,将发生以下转换:

Resulting state MHMessage state
"cur" subdirectory "unseen" sequence
“ cur”子目录和 S 标志 没有“看不见”的序列
F flag "flagged" sequence
R flag "replied" sequence

当基于BabylMessage实例创建MaildirMessage实例时,将发生以下转换:

Resulting state BabylMessage state
"cur" subdirectory "unseen" label
“ cur”子目录和 S 标志 没有“看不见”的标签
P flag “转发”或“重新发送”标签
R flag "answered" label
T flag "deleted" label

18.4.2.2. mboxMessage

mbox 邮箱中的邮件一起存储在一个文件中。发件人的信封地址和传递时间通常存储在以“发件人”开头的行中,该行用于指示消息的开始,尽管在 mbox 实现中此数据的确切格式存在很大差异。指示消息状态的标志,例如是否已读取或标记为重要,通常存储在* Status X-Status *Headers 中。

mbox 消息的常规标志如下:

Flag Meaning Explanation
R Read Read
O Old 以前由 MUA 检测到
D Deleted 标记为以后删除
F Flagged 标记为重要
A Answered Replied to

“ R”和“ O”标志存储在* Status Headers 中,而“ D”,“ F”和“ A”标志存储在 X-Status *Headers 中。标志和 Headers 通常按提到的 Sequences 出现。

mboxMessage个实例提供以下方法:

当基于MaildirMessage实例创建mboxMessage实例时,将根据MaildirMessage实例的交付日期生成“发件人”行,并进行以下转换:

Resulting state MaildirMessage state
R flag S flag
O flag "cur" subdirectory
D flag T flag
F flag F flag
A flag R flag

当基于MHMessage实例创建mboxMessage实例时,将发生以下转换:

Resulting state MHMessage state
R 标志和 O 标志 没有“看不见”的序列
O flag "unseen" sequence
F flag "flagged" sequence
A flag "replied" sequence

当基于BabylMessage实例创建mboxMessage实例时,将发生以下转换:

Resulting state BabylMessage state
R 标志和 O 标志 没有“看不见”的标签
O flag "unseen" label
D flag "deleted" label
A flag "answered" label

当基于MMDFMessage实例创建Message实例时,将复制“ From”行,并且所有标志都直接对应:

Resulting state MMDFMessage state
R flag R flag
O flag O flag
D flag D flag
F flag F flag
A flag A flag

18.4.2.3. MHMessage

MH 消息不支持传统意义上的标记或标志,但支持序列,这是任意消息的逻辑分组。一些邮件阅读程序(尽管不是标准的 mhnmh )使用序列的方式与标记与其他格式的使用方式大致相同,如下所示:

Sequence Explanation
unseen 未读取,但先前被 MUA 检测到
replied Replied to
flagged 标记为重要

MHMessage个实例提供以下方法:

当基于MaildirMessage实例创建MHMessage实例时,将发生以下转换:

Resulting state MaildirMessage state
"unseen" sequence 没有 S 标志
"replied" sequence R flag
"flagged" sequence F flag

当基于mboxMessageMMDFMessage实例创建MHMessage实例时,将Ellipsis* Status X-Status *Headers,并进行以下转换:

Resulting state mboxMessageMMDFMessage状态
"unseen" sequence 没有 R 标志
"replied" sequence A flag
"flagged" sequence F flag

当基于BabylMessage实例创建MHMessage实例时,将发生以下转换:

Resulting state BabylMessage state
"unseen" sequence "unseen" label
"replied" sequence "answered" label

18.4.2.4. BabylMessage

某些称为* attributes *的消息标签由约定定义为具有特殊含义。属性如下:

Label Explanation
unseen 未读取,但先前被 MUA 检测到
deleted 标记为以后删除
filed 复制到另一个文件或邮箱
answered Replied to
forwarded Forwarded
edited 由用户修改
resent Resent

默认情况下,Rmail 仅显示可见的标题。但是,BabylMessage类使用原始 Headers,因为它们更完整。如果需要,可以显式访问可见的 Headers。

BabylMessage个实例提供以下方法:

当基于MaildirMessage实例创建BabylMessage实例时,将发生以下转换:

Resulting state MaildirMessage state
"unseen" label 没有 S 标志
"deleted" label T flag
"answered" label R flag
"forwarded" label P flag

当基于mboxMessageMMDFMessage实例创建BabylMessage实例时,将Ellipsis* Status X-Status *Headers,并进行以下转换:

Resulting state mboxMessageMMDFMessage状态
"unseen" label 没有 R 标志
"deleted" label D flag
"answered" label A flag

当基于MHMessage实例创建BabylMessage实例时,将发生以下转换:

Resulting state MHMessage state
"unseen" label "unseen" sequence
"answered" label "replied" sequence

18.4.2.5. MMDFMessage

与 mbox 邮箱中的邮件一样,MMDF 邮件与发件人的地址和传递日期一起存储在以“ From”开头的首行中。同样,指示消息状态的标志通常存储在* Status X-Status *Headers 中。

MMDF 消息的常规标志与 mbox 消息的常规标志相同,如下所示:

Flag Meaning Explanation
R Read Read
O Old 以前由 MUA 检测到
D Deleted 标记为以后删除
F Flagged 标记为重要
A Answered Replied to

“ R”和“ O”标志存储在* Status Headers 中,而“ D”,“ F”和“ A”标志存储在 X-Status *Headers 中。标志和 Headers 通常按提到的 Sequences 出现。

MMDFMessage实例提供以下方法,与mboxMessage提供的方法相同:

当基于MaildirMessage实例创建MMDFMessage实例时,将根据MaildirMessage实例的交付日期生成“发件人”行,并进行以下转换:

Resulting state MaildirMessage state
R flag S flag
O flag "cur" subdirectory
D flag T flag
F flag F flag
A flag R flag

当基于MHMessage实例创建MMDFMessage实例时,将发生以下转换:

Resulting state MHMessage state
R 标志和 O 标志 没有“看不见”的序列
O flag "unseen" sequence
F flag "flagged" sequence
A flag "replied" sequence

当基于BabylMessage实例创建MMDFMessage实例时,将发生以下转换:

Resulting state BabylMessage state
R 标志和 O 标志 没有“看不见”的标签
O flag "unseen" label
D flag "deleted" label
A flag "answered" label

当基于mboxMessage实例创建MMDFMessage实例时,将复制“ From”行,并且所有标志都直接对应:

Resulting state mboxMessage state
R flag R flag
O flag O flag
D flag D flag
F flag F flag
A flag A flag

18.4.3. Exceptions

mailbox模块中定义了以下异常类:

18.4.4. 不推荐使用的类和方法

从 2.6 版开始不推荐使用。

mailbox模块的旧版本不支持邮箱的修改,例如添加或删除邮件,并且不提供表示特定于格式的邮件属性的类。为了向后兼容,较旧的邮箱类仍然可用,但应优先使用较新的类。旧类已在 Python 3 中删除。

较旧的邮箱对象仅支持迭代并提供单个公共方法:

Maildir之外,大多数较旧的邮箱类的名称都与当前邮箱类的名称不同。因此,新的Maildir类定义了next()方法,其构造函数与其他新的邮箱类的构造函数略有不同。

名称与较新邮箱名称不同的较旧邮箱类如下:

Note

出于本模块内部实现的原因,您可能希望以二进制模式打开* fp *对象。这在 Windows 上尤其重要。

为了获得最大的可移植性,Unix 风格的邮箱中的消息由任何以字符串'From '开头(请注意尾随空格)的行分隔,如果以两个换行符开头。由于实践中的变化范围很大,因此不应考虑From_行上的其他内容。但是,当前的实现不检查前两个换行符。通常,对于大多数应用程序来说,这很好。

UnixMailbox类使用通常正确匹配From_分隔符的正则表达式实现From_行检查的更严格版本。它认为定界线由From name time行分隔。为了获得最大的可移植性,请改用PortableUnixMailbox类。此类与UnixMailbox相同,除了各个消息仅由From行分隔。

如果希望将较旧的邮箱类与email模块而不是已弃用的rfc822模块一起使用,则可以按以下方式进行操作:

import email
import email.Errors
import mailbox

def msgfactory(fp):
    try:
        return email.message_from_file(fp)
    except email.Errors.MessageParseError:
        # Don't return None since that will
        # stop the mailbox iterator
        return ''

mbox = mailbox.UnixMailbox(fp, msgfactory)

另外,如果您知道您的邮箱仅包含格式正确的 MIME 消息,则可以将其简化为:

import email
import mailbox

mbox = mailbox.UnixMailbox(fp, email.message_from_file)

18.4.5. Examples

一个简单的示例,显示邮箱中所有邮件的主题,这些主题看起来很有趣:

import mailbox
for message in mailbox.mbox('~/mbox'):
    subject = message['subject']       # Could possibly be None.
    if subject and 'python' in subject.lower():
        print subject

要将所有邮件从 Babyl 邮箱复制到 MH 邮箱,请转换所有可以转换的特定于格式的信息:

import mailbox
destination = mailbox.MH('~/Mail')
destination.lock()
for message in mailbox.Babyl('~/RMAIL'):
    destination.add(mailbox.MHMessage(message))
destination.flush()
destination.unlock()

本示例将邮件从多个邮件列表中分类到不同的邮箱中,请注意避免由于其他程序的并发修改而导致的邮件损坏,由于程序break而导致的邮件丢失或由于邮箱中的邮件格式错误而导致的过早终止:

import mailbox
import email.errors

list_names = ('python-list', 'python-dev', 'python-bugs')

boxes = dict((name, mailbox.mbox('~/email/%s' % name)) for name in list_names)
inbox = mailbox.Maildir('~/Maildir', factory=None)

for key in inbox.iterkeys():
    try:
        message = inbox[key]
    except email.errors.MessageParseError:
        continue                # The message is malformed. Just leave it.

    for name in list_names:
        list_id = message['list-id']
        if list_id and name in list_id:
            # Get mailbox to use
            box = boxes[name]

            # Write copy to disk before removing original.
            # If there's a crash, you might duplicate a message, but
            # that's better than losing a message completely.
            box.lock()
            box.add(message)
            box.flush()
            box.unlock()

            # Remove original message
            inbox.lock()
            inbox.discard(key)
            inbox.flush()
            inbox.unlock()
            break               # Found destination, so stop looking.

for box in boxes.itervalues():
    box.close()
首页