email.policy:策略物件

在 3.3 版本加入。

原始碼: Lib/email/policy.py


email 包的主要關注點是處理各種電子郵件和 MIME RFCs 所描述的電子郵件訊息。然而,電子郵件訊息的通用格式(一個由多個標頭欄位組成的塊,每個欄位包含一個名稱後跟一個冒號,然後是一個值,整個塊後跟一個空行和一個任意的“正文”)這種格式在電子郵件領域之外也得到了應用。其中一些用法與主要的電子郵件 RFCs 相當接近,而另一些則不然。即使在處理電子郵件時,有時也需要打破對 RFCs 的嚴格遵守,例如生成與那些本身不遵循標準或以違反標準的方式實現您想使用的擴充套件的電子郵件伺服器互操作的電子郵件。

策略物件賦予 email 包處理所有這些不同用例的靈活性。

Policy 物件封裝了一組屬性和方法,用於在使用過程中控制 email 包中各種元件的行為。Policy 例項可以傳遞給 email 包中的各種類和方法,以改變預設行為。可設定的值及其預設值將在下面描述。

email 包中所有的類都使用一個預設策略。對於所有的 parser 類和相關的便利函式,以及 Message 類,預設策略是 Compat32 策略,透過其對應的預定義例項 compat32 來實現。這個策略提供了與 Python 3.3 之前的 email 包版本的完全向後相容性(在某些情況下,包括與 bug 的相容性)。

EmailMessagepolicy 關鍵字的預設值是 EmailPolicy 策略,透過其預定義例項 default 實現。

當建立 MessageEmailMessage 物件時,它會獲得一個策略。如果訊息是由 parser 建立的,傳遞給解析器的策略將成為它建立的訊息所使用的策略。如果訊息是由程式建立的,那麼可以在建立時指定策略。當訊息被傳遞給 generator 時,生成器預設使用訊息的策略,但你也可以傳遞一個特定的策略給生成器,它將覆蓋儲存在訊息物件上的策略。

在 Python 的未來版本中,email.parser 類和解析器便利函式的 policy 關鍵字的預設值**將會改變**。因此,在呼叫 parser 模組中描述的任何類和函式時,你應該**總是顯式指定你想要使用的策略**。

本文件的第一部分涵蓋了 Policy 的特性,它是一個抽象基類,定義了所有策略物件(包括 compat32)共有的特性。這包括一些由 email 包內部呼叫的鉤子方法,自定義策略可以重寫這些方法以獲得不同的行為。第二部分描述了具體的類 EmailPolicyCompat32,它們分別實現了提供標準行為和向後相容行為與特性的鉤子。

Policy 例項是不可變的,但可以被克隆,接受與類建構函式相同的關鍵字引數,並返回一個新的 Policy 例項,該例項是原始例項的副本,但指定的屬性值已被更改。

例如,以下程式碼可用於從磁碟檔案中讀取電子郵件訊息,並將其傳遞給 Unix 系統上的系統 sendmail 程式。

>>> from email import message_from_binary_file
>>> from email.generator import BytesGenerator
>>> from email import policy
>>> from subprocess import Popen, PIPE
>>> with open('mymsg.txt', 'rb') as f:
...     msg = message_from_binary_file(f, policy=policy.default)
...
>>> p = Popen(['sendmail', msg['To'].addresses[0]], stdin=PIPE)
>>> g = BytesGenerator(p.stdin, policy=msg.policy.clone(linesep='\r\n'))
>>> g.flatten(msg)
>>> p.stdin.close()
>>> rc = p.wait()

在這裡,我們告訴 BytesGenerator 在建立要輸入到 sendmailstdin 的二進位制字串時,使用符合 RFC 規範的行分隔符,而預設策略會使用 \n 行分隔符。

一些 email 包的方法接受一個 policy 關鍵字引數,允許為該方法覆蓋策略。例如,以下程式碼使用前一個示例中 msg 物件的 as_bytes() 方法,並使用其執行平臺的原生行分隔符將訊息寫入檔案。

>>> import os
>>> with open('converted.txt', 'wb') as f:
...     f.write(msg.as_bytes(policy=msg.policy.clone(linesep=os.linesep)))
17

策略物件也可以使用加法運算子進行組合,生成一個策略物件,其設定是相加物件非預設值的組合。

>>> compat_SMTP = policy.compat32.clone(linesep='\r\n')
>>> compat_strict = policy.compat32.clone(raise_on_defect=True)
>>> compat_strict_SMTP = compat_SMTP + compat_strict

這個操作不滿足交換律;也就是說,物件相加的順序很重要。舉例說明:

>>> policy100 = policy.compat32.clone(max_line_length=100)
>>> policy80 = policy.compat32.clone(max_line_length=80)
>>> apolicy = policy100 + policy80
>>> apolicy.max_line_length
80
>>> apolicy = policy80 + policy100
>>> apolicy.max_line_length
100
class email.policy.Policy(**kw)

這是所有策略類的抽象基類。它為一些簡單的方法提供了預設實現,以及不可變性屬性的實現、clone() 方法和建構函式的語義。

策略類的建構函式可以接受各種關鍵字引數。可以指定的引數是這個類上的任何非方法屬性,以及具體類上的任何額外的非方法屬性。在建構函式中指定的值將覆蓋相應屬性的預設值。

該類定義了以下屬性,因此以下屬性的值可以在任何策略類的建構函式中傳遞:

max_line_length

序列化輸出中任何行的最大長度,不包括行尾字元。預設為 78,依據 RFC 5322。值為 0None 表示完全不進行換行。

linesep

用於在序列化輸出中終止行的字串。預設是 \n,因為這是 Python 內部使用的行尾規則,儘管 RFCs 要求使用 \r\n

cte_type

控制可能或必須使用的內容傳輸編碼(Content Transfer Encodings)的型別。可能的值是:

7bit

所有資料必須是“7 位安全的”(僅 ASCII)。這意味著在必要時,資料將使用 quoted-printable 或 base64 編碼進行編碼。

8bit

資料不限於 7 位安全。標頭中的資料仍然需要是僅 ASCII 的,因此會被編碼(有關例外情況,請參見下面的 fold_binary()utf8),但正文部分可以使用 8bit CTE。

cte_type 值為 8bit 僅適用於 BytesGenerator,不適用於 Generator,因為字串不能包含二進位制資料。如果一個 Generator 在指定 cte_type=8bit 的策略下執行,它的行為將如同 cte_type7bit 一樣。

raise_on_defect

如果為 True,任何遇到的缺陷都會被作為錯誤引發。如果為 False(預設值),缺陷將被傳遞給 register_defect() 方法。

mangle_from_

如果為 True,正文中以 *“From ”* 開頭的行會透過在它們前面加上一個 > 來進行轉義。此引數在訊息由生成器序列化時使用。預設值:False

在 3.5 版本加入。

message_factory

用於構造新空訊息物件的工廠函式。解析器在構建訊息時使用。預設為 None,在這種情況下使用 Message

在 3.6 版本加入。

verify_generated_headers

如果為 True(預設值),生成器將引發 HeaderWriteError,而不是寫入一個摺疊或分隔不當的標頭,這樣的標頭可能會被解析為多個標頭或與相鄰資料連線。這類標頭可能由自定義標頭類或 email 模組中的錯誤生成。

由於它是一項安全特性,即使在 Compat32 策略中,它也預設為 True。為了實現向後相容但行為不安全的模式,必須將其顯式設定為 False

在 3.13 版本加入。

以下 Policy 方法旨在由使用 email 庫的程式碼呼叫,以建立具有自定義設定的策略例項:

clone(**kw)

返回一個新的 Policy 例項,其屬性值與當前例項相同,除非這些屬性透過關鍵字引數被賦予了新值。

其餘的 Policy 方法由 email 包的程式碼呼叫,不應由使用 email 包的應用程式呼叫。自定義策略必須實現所有這些方法。

handle_defect(obj, defect)

處理在 obj 上發現的 defect。當 email 包呼叫此方法時,defect 將始終是 MessageDefect 的子類。

預設實現會檢查 raise_on_defect 標誌。如果為 Truedefect 將作為異常被引發。如果為 False(預設值),objdefect 將被傳遞給 register_defect()

register_defect(obj, defect)

obj 上註冊一個 defect。在 email 包中,defect 將始終是 MessageDefect 的子類。

預設實現會呼叫 objdefects 屬性的 append 方法。當 email 包呼叫 handle_defect 時,obj 通常會有一個具有 append 方法的 defects 屬性。與 email 包一起使用的自定義物件型別(例如,自定義 Message 物件)也應提供這樣的屬性,否則解析訊息中的缺陷將引發意外錯誤。

header_max_count(name)

返回名為 name 的標頭所允許的最大數量。

在向 EmailMessageMessage 物件新增標頭時呼叫。如果返回的值不是 0None,並且已經存在名為 name 的標頭數量大於或等於返回的值,則會引發 ValueError

由於 Message.__setitem__ 的預設行為是將值附加到標頭列表中,因此很容易在不知不覺中建立重複的標頭。此方法允許限制某些標頭可以以程式設計方式新增到 Message 物件中的例項數量。(解析器不會遵守此限制,它將忠實地生成訊息中存在的所有標頭。)

預設實現對所有標頭名稱返回 None

header_source_parse(sourcelines)

email 包使用一個字串列表呼叫此方法,每個字串都以在被解析的源中找到的行分隔符結尾。第一行包括欄位標頭名稱和分隔符。源中的所有空白都將被保留。該方法應返回 (name, value) 元組,該元組將儲存在 Message 中以表示解析後的標頭。

如果實現希望保持與現有 email 包策略的相容性,name 應該是保留大小寫的名稱(直到“:”分隔符之前的所有字元),而 value 應該是展開後的值(所有行分隔符被移除,但空白保持不變),並去除前導空白。

sourcelines 可能包含 surrogateescaped 編碼的二進位制資料。

沒有預設實現。

header_store_parse(name, value)

當應用程式以程式設計方式修改 Message(而不是由解析器建立的 Message)時,email 包會使用應用程式提供的名稱和值呼叫此方法。該方法應返回 (name, value) 元組,該元組將儲存在 Message 中以表示該標頭。

如果實現希望保持與現有 email 包策略的相容性,namevalue 應該是不會改變傳入引數內容的字串或字串子類。

沒有預設實現。

header_fetch_parse(name, value)

當應用程式請求某個標頭時,email 包會使用當前儲存在 Message 中的 namevalue 呼叫此方法,而該方法返回的任何內容都將作為被檢索標頭的值返回給應用程式。請注意,Message 中可能儲存了多個同名的標頭;該方法被傳遞的是將要返回給應用程式的特定標頭的名稱和值。

value 可能包含 surrogateescaped 編碼的二進位制資料。方法返回的值中不應包含 surrogateescaped 編碼的二進位制資料。

沒有預設實現。

fold(name, value)

email 包使用當前儲存在 Message 中給定標頭的 namevalue 呼叫此方法。該方法應返回一個字串,表示該標頭透過組合 namevalue 並在適當位置插入 linesep 字元而(根據策略設定)正確“摺疊”的結果。有關摺疊電子郵件標頭的規則的討論,請參閱 RFC 5322

value 可能包含 surrogateescaped 編碼的二進位制資料。方法返回的字串中不應包含 surrogateescaped 編碼的二進位制資料。

fold_binary(name, value)

fold() 相同,只是返回的值應為位元組物件而不是字串。

value 可能包含 surrogateescaped 編碼的二進位制資料。這些資料可以在返回的位元組物件中轉換回二進位制資料。

class email.policy.EmailPolicy(**kw)

這個具體的 Policy 提供了旨在完全符合當前電子郵件 RFC 的行為。這些 RFC 包括(但不限於)RFC 5322RFC 2047 以及當前的 MIME RFC。

此策略添加了新的標頭解析和摺疊演算法。標頭不再是簡單的字串,而是 str 的子類,其屬性取決於欄位型別。解析和摺疊演算法完全實現了 RFC 2047RFC 5322

message_factory 屬性的預設值是 EmailMessage

除了上面列出的適用於所有策略的可設定屬性外,此策略還添加了以下附加屬性:

在 3.6 版本加入: [1]

utf8

如果為 False,則遵循 RFC 5322,透過將非 ASCII 字元編碼為“編碼詞”來支援標頭中的這些字元。如果為 True,則遵循 RFC 6532 並對標頭使用 utf-8 編碼。以此方式格式化的訊息可以傳遞給支援 SMTPUTF8 擴充套件(RFC 6531)的 SMTP 伺服器。

refold_source

如果 Message 物件中標頭的值源自 parser(而不是由程式設定),此屬性指示生成器在將訊息轉換回序列化形式時是否應重新摺疊該值。可能的值為:

none

所有源值都使用原始的摺疊方式

long

任何行長於 max_line_length 的源值都將被重新摺疊

all

所有值都會被重新摺疊。

預設值為 long

header_factory

一個可呼叫物件,接受兩個引數 namevalue,其中 name 是標頭欄位名,value 是未摺疊的標頭欄位值,並返回一個表示該標頭的字串子類。提供了一個預設的 header_factory(見 headerregistry),它支援對各種地址和日期 RFC 5322 標頭欄位型別以及主要的 MIME 標頭欄位型別進行自定義解析。將來會增加對其他自定義解析的支援。

content_manager

一個至少有兩個方法的物件:get_content 和 set_content。當呼叫 EmailMessage 物件的 get_content()set_content() 方法時,它會呼叫此物件的相應方法,將訊息物件作為其第一個引數,並將傳遞給它的任何引數或關鍵字作為附加引數傳遞。預設情況下,content_manager 被設定為 raw_data_manager

在 3.4 版本加入。

該類提供了 Policy 抽象方法的以下具體實現:

header_max_count(name)

返回用於表示給定名稱標頭的專用類的 max_count 屬性的值。

header_source_parse(sourcelines)

名稱被解析為直到“:”之前的所有內容並原樣返回。值是透過去除第一行剩餘部分的前導空格,將所有後續行連線在一起,並去除任何尾隨的回車或換行符來確定的。

header_store_parse(name, value)

名稱原樣返回。如果輸入值具有一個 name 屬性且其與 name 忽略大小寫匹配,則該值原樣返回。否則,namevalue 會被傳遞給 header_factory,返回的結果標頭物件作為值。在這種情況下,如果輸入值包含 CR 或 LF 字元,則會引發 ValueError

header_fetch_parse(name, value)

如果值有一個 name 屬性,則它會被原樣返回。否則,name 和移除了任何 CR 或 LF 字元的 value 將被傳遞給 header_factory,並返回生成的標頭物件。任何代理轉義的位元組都會變成 unicode 的未知字元符號。

fold(name, value)

標頭摺疊由 refold_source 策略設定控制。一個值當且僅當它沒有 name 屬性時,才被認為是“源值”(擁有 name 屬性意味著它是某種標頭物件)。如果根據策略需要重新摺疊一個源值,它會被轉換成一個標頭物件,方法是將 name 和去除了任何 CR 和 LF 字元的 value 傳遞給 header_factory。標頭物件的摺疊是透過呼叫其 fold 方法並傳入當前策略來完成的。

源值使用 splitlines() 分割成行。如果該值不需重新摺疊,則這些行將使用策略中的 linesep 重新連線並返回。例外情況是包含非 ASCII 二進位制資料的行。在這種情況下,無論 refold_source 設定如何,該值都會被重新摺疊,這會導致二進位制資料使用 unknown-8bit 字元集進行 CTE 編碼。

fold_binary(name, value)

如果 cte_type7bit,則與 fold() 相同,只是返回的值是位元組。

如果 cte_type8bit,非 ASCII 二進位制資料將被轉換回位元組。含有二進位制資料的標頭不會被重新摺疊,無論 refold_header 設定如何,因為無法知道二進位制資料是由單位元組字元還是多位元組字元組成。

以下 EmailPolicy 例項為特定應用領域提供了合適的預設值。請注意,將來這些例項的行為(特別是 HTTP 例項)可能會被調整,以更緊密地符合其相關領域的 RFC。

email.policy.default

一個 EmailPolicy 的例項,所有預設值均未改變。此策略使用標準的 Python \n 換行符,而不是 RFC 規範的 \r\n

email.policy.SMTP

適用於按照電子郵件 RFCs 規範序列化訊息。與 default 類似,但 linesep 設定為 \r\n,這符合 RFC 規範。

email.policy.SMTPUTF8

SMTP 相同,但 utf8True。適用於將訊息序列化到訊息儲存中,而不在標頭中使用編碼詞。僅當發件人或收件人地址含有非 ASCII 字元時才應用於 SMTP 傳輸(smtplib.SMTP.send_message() 方法會自動處理這種情況)。

email.policy.HTTP

適用於序列化用於 HTTP 流量的標頭。與 SMTP 類似,但 max_line_length 設定為 None(無限制)。

email.policy.strict

便利例項。與 default 相同,但 raise_on_defect 設定為 True。這允許透過編寫以下程式碼使任何策略變得嚴格:

somepolicy + policy.strict

對於所有這些 EmailPolicies,email 包的有效 API 與 Python 3.2 API 相比有以下變化:

  • Message 上設定一個標頭會導致該標頭被解析並建立一個標頭物件。

  • Message 獲取一個標頭值會導致該標頭被解析,並建立並返回一個標頭物件。

  • 任何標頭物件,或任何由於策略設定而被重新摺疊的標頭,都將使用一個完全實現了 RFC 摺疊演算法的演算法進行摺疊,包括知道在何處需要和允許使用編碼詞。

從應用程式的角度來看,這意味著透過 EmailMessage 獲取的任何標頭都是一個帶有額外屬性的標頭物件,其字串值是標頭的完全解碼的 unicode 值。同樣,可以使用 unicode 字串為標頭賦新值或建立新標頭,策略將負責將 unicode 字串轉換為正確的 RFC 編碼形式。

標頭物件及其屬性在 headerregistry 中有描述。

class email.policy.Compat32(**kw)

這個具體的 Policy 是向後相容策略。它複製了 email 包在 Python 3.2 中的行為。policy 模組也定義了這個類的一個例項 compat32,它被用作預設策略。因此,email 包的預設行為是保持與 Python 3.2 的相容性。

以下屬性的值與 Policy 的預設值不同:

mangle_from_

預設值為 True

該類提供了 Policy 抽象方法的以下具體實現:

header_source_parse(sourcelines)

名稱被解析為直到“:”之前的所有內容並原樣返回。值是透過去除第一行剩餘部分的前導空格,將所有後續行連線在一起,並去除任何尾隨的回車或換行符來確定的。

header_store_parse(name, value)

名稱和值將不加修改地返回。

header_fetch_parse(name, value)

如果值包含二進位制資料,它將被使用 unknown-8bit 字元集轉換為 Header 物件。否則,它將不加修改地返回。

fold(name, value)

標頭使用 Header 摺疊演算法進行摺疊,該演算法保留值中現有的換行符,並將每個結果行包裝到 max_line_length。非 ASCII 二進位制資料使用 unknown-8bit 字元集進行 CTE 編碼。

fold_binary(name, value)

標頭使用 Header 摺疊演算法進行摺疊,該演算法保留值中現有的換行符,並將每個結果行包裝到 max_line_length。如果 cte_type7bit,非 ASCII 二進位制資料將使用 unknown-8bit 字元集進行 CTE 編碼。否則,將使用原始的源標頭,包括其現有的換行符和它可能包含的任何(RFC 無效的)二進位制資料。

email.policy.compat32

Compat32 的一個例項,提供了與 Python 3.2 中 email 包行為的向後相容性。

腳註