email.message.Message:使用 compat32 API 表示電子郵件訊息

Message 類與 EmailMessage 類非常相似,但沒有該類新增的方法,並且某些其他方法的預設行為略有不同。我們在此處還記錄了一些方法,這些方法雖然受 EmailMessage 類支援,但除非您正在處理遺留程式碼,否則不建議使用。

否則,這兩個類的理念和結構是相同的。

本文件描述了在預設(對於 Message)策略 Compat32 下的行為。如果您要使用其他策略,則應該改用 EmailMessage 類。

電子郵件訊息由頭部有效負載組成。頭部必須是 RFC 5322 風格的名稱和值,其中欄位名稱和值由冒號分隔。冒號不屬於欄位名稱或欄位值。有效負載可以是簡單的文字訊息、二進位制物件,或者是由子訊息組成的結構化序列,每個子訊息都有自己的頭部集和自己的有效負載。後一種有效負載型別透過具有 MIME 型別(例如 multipart/*message/rfc822)的訊息來指示。

Message 物件提供的概念模型是一個有序的頭部字典,具有用於訪問頭部中的專用資訊、訪問有效負載、生成訊息的序列化版本以及遞迴遍歷物件樹的附加方法。請注意,支援重複的頭部,但必須使用特殊方法來訪問它們。

Message 偽字典透過頭部名稱進行索引,頭部名稱必須是 ASCII 值。字典的值是假定只包含 ASCII 字元的字串;對非 ASCII 輸入有一些特殊處理,但它並不總是產生正確的結果。頭部以保留大小寫形式儲存和返回,但欄位名稱匹配不區分大小寫。可能還有一個單獨的信封頭部,也稱為 Unix-From 頭部或 From_ 頭部。對於簡單訊息物件,有效負載是字串或位元組;對於 MIME 容器文件(例如 multipart/*message/rfc822),它是 Message 物件的列表。

以下是 Message 類的方法

class email.message.Message(policy=compat32)

如果指定了 policy(它必須是 policy 類的一個例項),則使用它指定的規則來更新和序列化訊息的表示。如果未設定 policy,則使用 compat32 策略,該策略維護與 Python 3.2 版 email 包的向後相容性。有關更多資訊,請參閱 policy 文件。

3.3 版更改: 添加了 policy 關鍵字引數。

as_string(unixfrom=False, maxheaderlen=0, policy=None)

將整個訊息扁平化為字串返回。當可選引數 unixfrom 為 true 時,返回的字串中包含信封頭部。unixfrom 預設為 False。出於向後相容性原因,maxheaderlen 預設為 0,因此如果需要不同的值,必須顯式覆蓋它(此方法將忽略策略中為 max_line_length 指定的值)。policy 引數可用於覆蓋從訊息例項獲取的預設策略。這可用於控制方法產生的一些格式,因為指定的 policy 將傳遞給 Generator

扁平化訊息可能會觸發對 Message 的更改,如果需要填充預設值以完成到字串的轉換(例如,可能會生成或修改 MIME 邊界)。

請注意,此方法僅為方便而提供,可能並不總是按照您希望的方式格式化訊息。例如,預設情況下,它不會對以 From 開頭的行進行混亂處理,而 Unix mbox 格式需要這種處理。為了獲得更大的靈活性,請例項化一個 Generator 例項並直接使用其 flatten() 方法。例如

from io import StringIO
from email.generator import Generator
fp = StringIO()
g = Generator(fp, mangle_from_=True, maxheaderlen=60)
g.flatten(msg)
text = fp.getvalue()

如果訊息物件包含未按照 RFC 標準編碼的二進位制資料,則不合規的資料將被 Unicode“未知字元”程式碼點替換。(另請參閱 as_bytes()BytesGenerator。)

3.4 版更改: 添加了 policy 關鍵字引數。

__str__()

等同於 as_string()。允許 str(msg) 生成包含格式化訊息的字串。

as_bytes(unixfrom=False, policy=None)

將整個訊息扁平化為位元組物件返回。當可選引數 unixfrom 為 true 時,返回的字串中包含信封頭部。unixfrom 預設為 Falsepolicy 引數可用於覆蓋從訊息例項獲取的預設策略。這可用於控制方法產生的一些格式,因為指定的 policy 將傳遞給 BytesGenerator

扁平化訊息可能會觸發對 Message 的更改,如果需要填充預設值以完成到字串的轉換(例如,可能會生成或修改 MIME 邊界)。

請注意,此方法僅為方便而提供,可能並不總是按照您希望的方式格式化訊息。例如,預設情況下,它不會對以 From 開頭的行進行混亂處理,而 Unix mbox 格式需要這種處理。為了獲得更大的靈活性,請例項化一個 BytesGenerator 例項並直接使用其 flatten() 方法。例如

from io import BytesIO
from email.generator import BytesGenerator
fp = BytesIO()
g = BytesGenerator(fp, mangle_from_=True, maxheaderlen=60)
g.flatten(msg)
text = fp.getvalue()

在 3.4 版本加入。

__bytes__()

等同於 as_bytes()。允許 bytes(msg) 生成包含格式化訊息的位元組物件。

在 3.4 版本加入。

is_multipart()

如果訊息的有效負載是子 Message 物件的列表,則返回 True,否則返回 False。當 is_multipart() 返回 False 時,有效負載應該是一個字串物件(它可能是一個 CTE 編碼的二進位制有效負載)。(請注意,is_multipart() 返回 True 並不一定意味著“msg.get_content_maintype() == 'multipart'”將返回 True。例如,當 Message 型別為 message/rfc822 時,is_multipart 將返回 True。)

set_unixfrom(unixfrom)

將訊息的信封頭部設定為 unixfrom,它應該是一個字串。

get_unixfrom()

返回訊息的信封頭部。如果信封頭部從未設定,則預設為 None

attach(payload)

將給定的 payload 新增到當前有效負載中,該有效負載在呼叫之前必須是 NoneMessage 物件的列表。呼叫後,有效負載將始終是 Message 物件的列表。如果您想將有效負載設定為標量物件(例如字串),請改用 set_payload()

這是一個遺留方法。在 EmailMessage 類上,其功能由 set_content() 以及相關的 makeadd 方法取代。

get_payload(i=None, decode=False)

返回當前有效負載,當 is_multipart()True 時,它將是 Message 物件的列表;當 is_multipart()False 時,它將是一個字串。如果有效負載是列表並且您修改了列表物件,則您將就地修改訊息的有效負載。

使用可選引數 i,如果 is_multipart()Trueget_payload() 將返回有效負載的第 i 個元素,從零開始計數。如果 i 小於 0 或大於或等於有效負載中的專案數,則會引發 IndexError。如果有效負載是字串(即 is_multipart()False)並且給出了 i,則會引發 TypeError

可選的 decode 是一個標誌,指示是否應根據 Content-Transfer-Encoding 頭部解碼有效負載。當 True 且訊息不是多部分時,如果此頭部的值為 quoted-printablebase64,則有效負載將被解碼。如果使用了其他編碼,或者缺少 Content-Transfer-Encoding 頭部,則有效負載按原樣返回(未解碼)。在所有情況下,返回值都是二進位制資料。如果訊息是多部分且 decode 標誌為 True,則返回 None。如果有效負載是 base64 且格式不完美(缺少填充、字元不在 base64 字母表中),則會向訊息的缺陷屬性新增相應的缺陷(InvalidBase64PaddingDefectInvalidBase64CharactersDefect)。

decodeFalse(預設值)時,主體作為字串返回,不解碼 Content-Transfer-Encoding。但是,對於 8bit 的 Content-Transfer-Encoding,嘗試使用 Content-Type 頭部指定的 charset 解碼原始位元組,並使用 replace 錯誤處理程式。如果未指定 charset,或者給定的 charset 不被 email 包識別,則使用預設的 ASCII 字元集解碼主體。

這是一個遺留方法。在 EmailMessage 類上,其功能由 get_content()iter_parts() 取代。

set_payload(payload, charset=None)

將整個訊息物件的有效負載設定為 payload。客戶端有責任確保有效負載的不變性。可選引數 charset 設定訊息的預設字元集;有關詳細資訊,請參閱 set_charset()

這是一個遺留方法。在 EmailMessage 類上,其功能由 set_content() 取代。

set_charset(charset)

將有效負載的字元集設定為 charset,它可以是 Charset 例項(參見 email.charset)、命名字元集的字串或 None。如果它是字串,它將被轉換為 Charset 例項。如果 charsetNone,則 charset 引數將從 Content-Type 頭部中刪除(訊息不會以其他方式修改)。任何其他值將生成 TypeError

如果沒有現有的 MIME-Version 頭部,則會新增一個。如果沒有現有的 Content-Type 頭部,則會新增一個值為 text/plain 的頭部。無論 Content-Type 頭部是否已存在,其 charset 引數都將設定為 charset.output_charset。如果 charset.input_charsetcharset.output_charset 不同,則有效負載將重新編碼為 output_charset。如果沒有現有的 Content-Transfer-Encoding 頭部,則在需要時,將使用指定的 Charset 對有效負載進行傳輸編碼,並新增具有適當值的頭部。如果 Content-Transfer-Encoding 頭部已存在,則假定有效負載已使用該 Content-Transfer-Encoding 正確編碼,並且不會修改。

這是一個遺留方法。在 EmailMessage 類上,其功能由 email.message.EmailMessage.set_content() 方法的 charset 引數取代。

get_charset()

返回與訊息有效負載關聯的 Charset 例項。

這是一個遺留方法。在 EmailMessage 類上,它總是返回 None

以下方法實現了一個類似對映的介面,用於訪問訊息的 RFC 2822 頭部。請注意,這些方法與普通對映(即字典)介面之間存在一些語義差異。例如,在字典中沒有重複的鍵,但這裡可能存在重複的訊息頭部。此外,在字典中,keys() 返回的鍵沒有保證的順序,但在 Message 物件中,頭部始終按它們在原始訊息中出現的順序或後來新增到訊息中的順序返回。任何已刪除然後重新新增的頭部都始終附加到頭部列表的末尾。

這些語義差異是故意的,並且傾向於最大的便利性。

請注意,在所有情況下,訊息中存在的任何信封頭部都不包含在對映介面中。

在從位元組生成的模型中,任何(違反 RFC 的)包含非 ASCII 位元組的頭部值,在透過此介面檢索時,將表示為 Header 物件,其字元集為 unknown-8bit

__len__()

返回頭部的總數,包括重複項。

__contains__(name)

如果訊息物件有名為 name 的欄位,則返回 True。匹配不區分大小寫,name 不應包含尾隨冒號。用於 in 運算子,例如

if 'message-id' in myMessage:
   print('Message-ID:', myMessage['message-id'])
__getitem__(name)

返回指定頭部欄位的值。name 不應包含冒號欄位分隔符。如果頭部缺失,則返回 None;絕不會引發 KeyError

請注意,如果命名欄位在訊息頭部中出現多次,則返回哪個欄位值是未定義的。使用 get_all() 方法獲取所有現有命名頭部的值。

__setitem__(name, val)

向訊息新增一個頭部,欄位名為 name,值為 val。該欄位將附加到訊息現有欄位的末尾。

請注意,這不會覆蓋或刪除任何具有相同名稱的現有頭部。如果您想確保新頭部是訊息中唯一具有欄位名 name 的頭部,請首先刪除該欄位,例如

del msg['subject']
msg['subject'] = 'Python roolz!'
__delitem__(name)

從訊息頭部中刪除所有名為 name 的欄位。如果指定的欄位不存在於頭部中,則不會引發異常。

keys()

返回訊息所有頭部欄位名稱的列表。

values()

返回訊息所有欄位值的列表。

items()

返回一個包含訊息所有欄位頭部和值的 2 元組列表。

get(name, failobj=None)

返回指定頭部欄位的值。這與 __getitem__() 相同,不同之處在於如果指定的頭部缺失,則返回可選引數 failobj(預設為 None)。

這裡有一些其他有用的方法

get_all(name, failobj=None)

返回欄位 name 的所有值的列表。如果訊息中沒有此類命名的頭部,則返回 failobj(預設為 None)。

add_header(_name, _value, **_params)

擴充套件頭部設定。此方法類似於 __setitem__(),不同之處在於可以透過關鍵字引數提供額外的頭部引數。_name 是要新增的頭部欄位,_value 是頭部的主值。

對於關鍵字引數字典 _params 中的每個項,鍵被視為引數名,下劃線轉換為破折號(因為破折號在 Python 識別符號中是非法的)。通常,引數將新增為 key="value",除非值為 None,在這種情況下將只新增鍵。如果值包含非 ASCII 字元,則可以將其指定為格式為 (CHARSET, LANGUAGE, VALUE) 的三元組,其中 CHARSET 是命名用於編碼值的字元集的字串,LANGUAGE 通常可以設定為 None 或空字串(有關其他可能性,請參閱 RFC 2231),而 VALUE 是包含非 ASCII 程式碼點的字串值。如果未傳遞三元組且值包含非 ASCII 字元,則會自動使用 utf-8CHARSETNoneLANGUAGERFC 2231 格式進行編碼。

這是一個例子

msg.add_header('Content-Disposition', 'attachment', filename='bud.gif')

這將新增一個看起來像這樣的頭部

Content-Disposition: attachment; filename="bud.gif"

帶有非 ASCII 字元的示例

msg.add_header('Content-Disposition', 'attachment',
               filename=('iso-8859-1', '', 'Fußballer.ppt'))

結果是

Content-Disposition: attachment; filename*="iso-8859-1''Fu%DFballer.ppt"
replace_header(_name, _value)

替換頭部。替換訊息中與 _name 匹配的第一個頭部,保留頭部順序和欄位名稱大小寫。如果未找到匹配的頭部,則會引發 KeyError

get_content_type()

返回訊息的內容型別。返回的字串將轉換為小寫形式 maintype/subtype。如果訊息中沒有 Content-Type 頭部,則將返回 get_default_type() 給出的預設型別。由於根據 RFC 2045,訊息總是具有預設型別,get_content_type() 將始終返回值。

RFC 2045 將訊息的預設型別定義為 text/plain,除非它出現在 multipart/digest 容器中,在這種情況下它將是 message/rfc822。如果 Content-Type 頭部具有無效的型別規範,RFC 2045 規定預設型別為 text/plain

get_content_maintype()

返回訊息的主內容型別。這是 get_content_type() 返回的字串的 maintype 部分。

get_content_subtype()

返回訊息的子內容型別。這是 get_content_type() 返回的字串的 subtype 部分。

get_default_type()

返回預設內容型別。大多數訊息的預設內容型別是 text/plain,但 multipart/digest 容器的子部分除外。此類子部分的預設內容型別是 message/rfc822

set_default_type(ctype)

設定預設內容型別。ctype 應該是 text/plainmessage/rfc822,儘管這並非強制執行。預設內容型別不儲存在 Content-Type 頭部中。

get_params(failobj=None, header='content-type', unquote=True)

返回訊息的 Content-Type 引數,作為一個列表。返回列表的元素是鍵/值對的 2 元組,按 '=' 符號分割。 '=' 左側是鍵,右側是值。如果引數中沒有 '=' 符號,則值為字串,否則值為 get_param() 中所述,如果可選引數 unquoteTrue(預設值),則取消引用。

可選引數 failobj 是在沒有 Content-Type 頭部時返回的物件。可選引數 header 是要搜尋的頭部,而不是 Content-Type

這是一個遺留方法。在 EmailMessage 類上,其功能由頭部訪問方法返回的各個頭部物件的 params 屬性取代。

get_param(param, failobj=None, header='content-type', unquote=True)

以字串形式返回 Content-Type 頭部引數 param 的值。如果訊息沒有 Content-Type 頭部或者沒有這樣的引數,則返回 failobj(預設為 None)。

可選引數 header(如果給出)指定要使用的訊息頭部,而不是 Content-Type

引數鍵始終不區分大小寫進行比較。返回值可以是字串,也可以是 3 元組,如果引數是 RFC 2231 編碼的。當它是 3 元組時,值的元素形式為 (CHARSET, LANGUAGE, VALUE)。請注意,CHARSETLANGUAGE 都可以是 None,在這種情況下,您應該認為 VALUEus-ascii 字元集編碼。您通常可以忽略 LANGUAGE

如果您的應用程式不關心引數是否以 RFC 2231 中所述進行編碼,您可以透過呼叫 email.utils.collapse_rfc2231_value() 來摺疊引數值,將 get_param() 的返回值傳遞給它。如果值為元組,這將返回一個適當解碼的 Unicode 字串;如果不是,則返回原始字串的未引用版本。例如

rawparam = msg.get_param('foo')
param = email.utils.collapse_rfc2231_value(rawparam)

在任何情況下,引數值(無論是返回的字串,還是 3 元組中的 VALUE 項)始終是未引用的,除非 unquote 設定為 False

這是一個遺留方法。在 EmailMessage 類上,其功能由頭部訪問方法返回的各個頭部物件的 params 屬性取代。

set_param(param, value, header='Content-Type', requote=True, charset=None, language='', replace=False)

Content-Type 頭部中設定引數。如果引數已存在於頭部中,其值將被 value 替換。如果此訊息尚未定義 Content-Type 頭部,則它將設定為 text/plain,新引數值將按照 RFC 2045 附加。

可選引數 header 指定 Content-Type 的替代頭部,並且所有引數都將根據需要引用,除非可選引數 requoteFalse(預設為 True)。

如果指定了可選引數 charset,則引數將根據 RFC 2231 進行編碼。可選引數 language 指定 RFC 2231 語言,預設為空字串。charsetlanguage 都應該是字串。

如果 replaceFalse(預設值),則頭部將移至頭部列表的末尾。如果 replaceTrue,則頭部將就地更新。

3.4 版更改: 添加了 replace 關鍵字。

del_param(param, header='content-type', requote=True)

Content-Type 頭部中完全刪除給定引數。頭部將在原地重寫,不帶引數或其值。除非 requoteFalse(預設為 True),否則所有值都將根據需要引用。可選引數 header 指定 Content-Type 的替代頭部。

set_type(type, header='Content-Type', requote=True)

設定 Content-Type 頭部的主型別和子型別。type 必須是 maintype/subtype 形式的字串,否則會引發 ValueError

此方法替換 Content-Type 頭部,保留所有引數。如果 requoteFalse,則保持現有頭部的引用不變,否則引數將被引用(預設)。

可以在 header 引數中指定替代頭部。設定 Content-Type 頭部時,還會新增一個 MIME-Version 頭部。

這是一個遺留方法。在 EmailMessage 類上,其功能由 make_add_ 方法取代。

get_filename(failobj=None)

返回訊息的 Content-Disposition 頭部中 filename 引數的值。如果頭部沒有 filename 引數,此方法將退回到在 Content-Type 頭部中查詢 name 引數。如果兩者都未找到,或者頭部缺失,則返回 failobj。返回的字串將始終按照 email.utils.unquote() 的規定取消引用。

get_boundary(failobj=None)

返回訊息的 Content-Type 頭部中 boundary 引數的值,如果頭部缺失或沒有 boundary 引數,則返回 failobj。返回的字串將始終按照 email.utils.unquote() 的規定取消引用。

set_boundary(boundary)

Content-Type 頭部中的 boundary 引數設定為 boundaryset_boundary() 將始終在必要時引用 boundary。如果訊息物件沒有 Content-Type 頭部,則會引發 HeaderParseError

請注意,使用此方法與刪除舊的 Content-Type 頭部並透過 add_header() 新增帶有新邊界的新頭部略有不同,因為 set_boundary() 保留了 Content-Type 頭部在頭部列表中的順序。但是,它不保留原始 Content-Type 頭部中可能存在的任何續行。

get_content_charset(failobj=None)

返回 Content-Type 頭部中 charset 引數的值,並強制轉換為小寫。如果不存在 Content-Type 頭部,或者該頭部沒有 charset 引數,則返回 failobj

請注意,此方法與 get_charset() 不同,後者返回訊息正文預設編碼的 Charset 例項。

get_charsets(failobj=None)

返回一個列表,其中包含訊息中的字元集名稱。如果訊息是 multipart,則該列表將包含有效負載中每個子部分的一個元素,否則,它將是一個長度為 1 的列表。

列表中的每個項都將是一個字串,它是所表示子部分的 Content-Type 頭部中 charset 引數的值。但是,如果子部分沒有 Content-Type 頭部,沒有 charset 引數,或者不是 text 主要 MIME 型別,則返回列表中的該項將是 failobj

get_content_disposition()

返回訊息的 Content-Disposition 頭部的小寫值(不帶引數),如果有的話,否則返回 None。如果訊息遵循 RFC 2183,此方法的可能值是 inlineattachmentNone

在 3.5 版本加入。

walk()

walk() 方法是一個通用生成器,可用於以深度優先遍歷順序迭代訊息物件樹的所有部分和子部分。您通常將 walk() 用作 for 迴圈中的迭代器;每次迭代返回下一個子部分。

這是一個列印多部分訊息結構的每個部分的 MIME 型別的示例

>>> for part in msg.walk():
...     print(part.get_content_type())
multipart/report
text/plain
message/delivery-status
text/plain
text/plain
message/rfc822
text/plain

walk 迭代任何部分的子部分,其中 is_multipart() 返回 True,即使 msg.get_content_maintype() == 'multipart' 可能返回 False。我們可以透過使用 _structure 除錯輔助函式在我們的示例中看到這一點

>>> for part in msg.walk():
...     print(part.get_content_maintype() == 'multipart',
...           part.is_multipart())
True True
False False
False True
False False
False False
False True
False False
>>> _structure(msg)
multipart/report
    text/plain
    message/delivery-status
        text/plain
        text/plain
    message/rfc822
        text/plain

這裡 message 部分不是 multiparts,但它們確實包含子部分。is_multipart() 返回 True,並且 walk 進入子部分。

Message 物件還可以選擇包含兩個例項屬性,這些屬性在生成 MIME 訊息的純文字時可以使用。

preamble

MIME 文件的格式允許在頭部後面的空行與第一個多部分邊界字串之間存在一些文字。通常,此文字在支援 MIME 的郵件閱讀器中永遠不可見,因為它超出標準 MIME 封裝。但是,在檢視訊息的原始文字或在不支援 MIME 的閱讀器中檢視訊息時,此文字可能會變得可見。

preamble 屬性包含 MIME 文件的此開頭額外封裝文字。當 Parser 在頭部之後但在第一個邊界字串之前發現一些文字時,它會將此文字分配給訊息的 preamble 屬性。當 Generator 寫入 MIME 訊息的純文字表示時,如果它發現訊息具有 preamble 屬性,它將在頭部和第一個邊界之間的區域中寫入此文字。有關詳細資訊,請參閱 email.parseremail.generator

請注意,如果訊息物件沒有前導碼,則 preamble 屬性將為 None

epilogue

epilogue 屬性的作用與 preamble 屬性相同,只是它包含出現在最後一個邊界和訊息末尾之間的文字。

您無需將 epilogue 設定為空字串,以便 Generator 在檔案末尾列印換行符。

defects

defects 屬性包含解析此訊息時發現的所有問題的列表。有關可能解析缺陷的詳細描述,請參閱 email.errors