AttributeError: 'str' object has no attribute 'copy' when parsing Multipart email message

AttributeError: 'str' object has no attribute 'copy' when parsing Multipart email message

Python 3.6 电子邮件模块因以下错误而崩溃:

Traceback (most recent call last):
  File "empty-eml.py", line 9, in <module>
    for part in msg.iter_attachments():
  File "/usr/lib/python3.6/email/message.py", line 1055, in iter_attachments
    parts = self.get_payload().copy()
AttributeError: 'str' object has no attribute 'copy'

可以使用此 EML 文件重现崩溃,

From: "xxx@xxx.xx" <xxx@xxx.xx>
To: <xx@xxx.xx>
Subject: COURRIER EMIS PAR PACIFICA 
MIME-Version: 1.0
Content-Type: multipart/mixed;
    boundary="----=_Part_3181_1274694650.1556805728023"
Date: Thu, 2 May 2019 16:02:08 +0200

和这段最少的代码:

from email import policy
from email.parser import Parser
from sys import argv


with open(argv[1]) as eml_file:
    msg = Parser(policy=policy.default).parse(eml_file)

for part in msg.iter_attachments():
    pass

我认为它必须与 Content-Type 为 multipart/mixed 以及电子邮件内容为空有关,这导致 get_payload 到 return str。但是,我不确定,如果标准禁止这样的 EML(但我有很多这样的示例),那是电子邮件模块中的错误,或者我使用的代码错误。

如果将策略更改为 strict

Parser(policy=policy.strict).parse(eml_file)

解析器提出 email.errors.StartBoundaryNotFoundDefect,在 docs 中描述为:

StartBoundaryNotFoundDefect – The start boundary claimed in the Content-Type header was never found.

如果您使用 policy.default 解析消息并随后检查它的 defects 它包含两个缺陷:

[StartBoundaryNotFoundDefect(), MultipartInvariantViolationDefect()]

MultipartInvariantViolationDefect – A message claimed to be a multipart, but no subparts were found. Note that when a message has this defect, its is_multipart() method may return false even though its content type claims to be multipart.

StartBoundaryNotFoundDefect 的结果是解析器终止解析并将消息有效负载设置为到目前为止已捕获的正文 - 在这种情况下,什么都没有,所以有效负载是一个空字符串,导致当您 运行 您的代码时看到的异常。

可以说 Python 在调用 copy() 之前不检查负载是否为 list 是一个错误。

在实践中,您必须通过将附件迭代包装在 try/except 中、对 msg.defects 的内容进行条件迭代或使用 policy.strict 进行解析来处理这些消息丢弃所有报告缺陷的消息。