email.policy: 策略對象?

3.3 新版功能.

源代碼: Lib/email/policy.py


email 的主要焦點是按照各種電子郵件和 MIME RFC 的描述來處理電子郵件消息。 但是電子郵件消息的基本格式(一個由名稱加冒號加值的標頭字段構成的區塊,后面再加一個空白行和任意的‘消息體’)是在電子郵件領域以外也獲得應用的格式。 這些應用的規則有些與主要電子郵件 RFC 十分接近,有些則很不相同。 即使是操作電子郵件,有時也可能需要打破嚴格的 RFC 規則,例如生成可與某些并不遵循標準的電子郵件服務器互聯的電子郵件,或者是實現希望應用某些破壞標準的操作方式的擴展。

策略對象給予 email 包處理這些不同用例的靈活性。

Policy 對象封裝了一組屬性和方法用來在使用期間控制 email 包中各個組件的行為。 Policy 實例可以被傳給 email 包中的多個類和方法以更改它們的默認行為。 可設置的值及其默認值如下所述。

在 email 包中的所有類會使用一個默認的策略。 對于所有 parser 類及相關的便捷函數,還有對于 Message 類來說,它是 Compat32 策略,通過其對應的預定義實例 compat32 來使用。 這個策略提供了與 Python3.3 版之前的 email 包的完全向下兼容性(在某些情況下,也包括對缺陷的兼容性)。

傳給 EmailMessagepolicy 關鍵字的默認值是 EmailPolicy 策略,表示為其預定義的實例 default

在創建 MessageEmailMessage 對象時,它需要一個策略。 如果消息是由 parser 創建的,則傳給該解析器的策略將是它所創建的消息所使用的策略。 如果消息是由程序創建的,則該策略可以在創建它的時候指定。 當消息被傳遞給 generator 時,生成器默認會使用來自該消息的策略,但你也可以將指定的策略傳遞給生成器,這將覆蓋存儲在消息對象上的策略。

email.parser 類和解析器便捷函數的 policy 關鍵字的默認值在未來的 Python 版本中 將會改變。 因此在調用任何 parser 模塊所描述的類和函數時你應當 總是顯式地指定你想要使用的策略

本文檔的第一部分介紹了 Policy 的特性,它是一個 abstract base class,定義了所有策略對象包括 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 在創建要送入 sendmail's stdin 的二進制字串時使用符合 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

Policy 對象也可使用附加運算符進行組合來產生一個新策略對象,其設置是被加總對象的非默認值的組合:

>>> 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)?

這是所有策略類的 abstract base class。 它提供了一些簡單方法的默認實現,以及不可變特征屬性,clone() 方法以及構造器語義的實現。

可以向策略類的構造器傳入各種關鍵字參數。 可以指定的參數是該類的任何非方法特征屬性,以及實體類的任何額外非方法特征屬性。 在構造器中指定的值將覆蓋相應屬性的默認值。

這個類定義了下列特征屬性,因此下列值可以被傳給任何策略類的構造器:

max_line_length?

序列化輸出中任何行的最大長度,不計入行字符的末尾。 默認值為 78,基于 RFC 5322。 值為 0None 表示完全沒有行包裝。

linesep?

用來在序列化輸出中確定行的字符串。 默認值為 \n 因為這是 Python 所使用的內部行結束符規范,但 RFC 的要求是 \r\n

cte_type?

控制可能要求使用的內容傳輸編碼格式類型。 可能的值包括:

7bit

所有數據必須為 "純 7 比特位" (僅 ASCII)。 這意味著在必要情況下數據將使用可打印引用形式或 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 " 開頭的行會通過在其前面放一個 > 來進行轉義。 當消息被生成器執行序列化時會使用此形參。 默認值t: False

3.5 新版功能: mangle_from_ 形參。

message_factory?

用來構造新的空消息對象的工廠函數。 在構建消息時由解析器使用。 默認為 None,在此情況下會使用 Message

3.6 新版功能.

下列 Policy 方法是由使用 email 庫的代碼來調用以創建具有自室外設置的策略實例:

clone(**kw)?

返回一個新的 Policy 實例,其屬性與當前實例具有相同的值,除非是那些由關鍵字參數給出了新值的屬性。

其余的 Policy 方法是由 email 包代碼來調用的,而不應當被使用 email 包的應用程序所調用。 自定義的策略必須實現所有這些方法。

handle_defect(obj, defect)?

處理在 obj 上發現的 defect。 當 email 包調用此方法時,defect 將總是 Defect 的一個子類。

默認實現會檢查 raise_on_defect 旗標。 如果其為 True,則 defect 會被作為異常來引發。 如果其為 False (默認值),則 objdefect 會被傳遞給 register_defect()

register_defect(obj, defect)?

obj 上注冊一個 defect。 在 email 包中,defect 將總是 Defect 的一個子類。

默認實現會調用 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 可以包含經替代轉義的二進制數據。

此方法沒有默認實現

header_store_parse(name, value)?

當一個應用通過程序代碼修改 Message (而不是由解析器創建 Message) 時,email 包會調用此方法時并附帶應用程序所提供的名稱和值。 此方法應當返回 (name, value) 元組以保存至 Message 中用來表示標頭。

如果一個實現希望保持與現有 email 包策略的兼容性,則 namevalue 應當為字符串或字符串的子類,它們不會修改在參數中傳入的內容。

此方法沒有默認實現

header_fetch_parse(name, value)?

當標頭被應用程序所請求時,email 包會調用此方法并附帶當前保存在 Message 中的 namevalue,并且無論此方法返回什么它都會被回傳給應用程序作為被提取標頭的值。 請注意可能會有多個相同名稱的標頭被保存在 Message 中;此方法會將指定標頭的名稱和值返回給應用程序。

value 可能包含經替代轉義的二進制數據。 此方法所返回的值應當沒有經替代轉義的二進制數據。

此方法沒有默認實現

fold(name, value)?

email 包調用此方法時會附帶當前保存在 Message 中的給定標頭的 namevalue。 此方法應當返回一個代表該標頭的(根據策略設置)通過處理 namevalue 并在適當位置插入 linesep 字符來正確地“折疊”的字符串。 請參閱 RFC 5322 了解有關折疊電子郵件標頭的規則的討論。

value 可能包含經替代轉義的二進制數據。 此方法所返回的字符串應當沒有經替代轉義的二進制數據。

fold_binary(name, value)?

fold() 類似,不同之處在于返回的值應當為字節串對象而非字符串。

value 可能包含經替代轉義的二進制數據。 這些數據可以在被返回的字節串對象中被轉換回二進制數據。

class email.policy.EmailPolicy(**kw)?

這個實體 Policy 提供了完全遵循當前電子郵件 RFC 的行為。 這包括 (但不限于) RFC 5322, RFC 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 屬性并可在忽略大小寫的情況下匹配 name,則 value 也會被原樣返回。 在其他情況下 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?

適用于按照符合電子郵件 RFC 的方式來序列化消息。 與 default 類似,但 linesep 被設為遵循 RFC 的 \r\n

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 為向下兼容策略。 它復制了 Python 3.2 中 email 包的行為。 policy 模塊還定義了該類的一個實例 compat32,用來作為默認策略。 因此 email 包的默認行為會保持與 Python 3.2 的兼容性。

下列屬性具有與 Policy 默認值不同的值:

mangle_from_?

默認值為 True

這個類提供了下列對 Policy 的抽象方法的具體實現:

header_source_parse(sourcelines)?

此名稱會被作為到 ':' 止的所有內容來解析。 該值是通過從第一行的剩余部分去除前導空格,再將所有后續行連接到一起,并去除所有末尾回車符或換行符來確定的。

header_store_parse(name, value)?

name 和 value 會被原樣返回。

header_fetch_parse(name, value)?

如果 value 包含二進制數據,則會使用 unknown-8bit 字符集來將其轉換為 Header 對象。 在其他情況下它會被原樣返回。

fold(name, value)?

標頭會使用 Header 折疊算法進行折疊,該算法保留 value 中現有的換行,并將結果行長度折疊至 max_line_length。 非 ASCII 二進制數據會使用 unknown-8bit 字符串進行 CTE 編碼。

fold_binary(name, value)?

標頭會使用 Header 折疊算法進行折疊,該算法保留 value 中現有的換行,并將每個結果行的長度折疊至 max_line_length。 如果 cte_type7bit,則非 ascii 二進制數據會使用 unknown-8bit 字符集進行 CTE 編碼。 在其他情況下則會使用原始的源標頭,這將保留其現有的換行和所包含的任何(不符合 RFC 的)二進制數據。

email.policy.compat32?

Compat32 的實例,提供與 Python 3.2 中的 email 包行為的向下兼容性。

備注

1

最初在 3.3 中作為 暫定特性 添加。