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 包的完全向下兼容性(在某些情況下,也包括對缺陷的兼容性)。
傳給 EmailMessage 的 policy 關鍵字的默認值是 EmailPolicy 策略,表示為其預定義的實例 default。
在創建 Message 或 EmailMessage 對象時,它需要一個策略。 如果消息是由 parser 創建的,則傳給該解析器的策略將是它所創建的消息所使用的策略。 如果消息是由程序創建的,則該策略可以在創建它的時候指定。 當消息被傳遞給 generator 時,生成器默認會使用來自該消息的策略,但你也可以將指定的策略傳遞給生成器,這將覆蓋存儲在消息對象上的策略。
email.parser 類和解析器便捷函數的 policy 關鍵字的默認值在未來的 Python 版本中 將會改變。 因此在調用任何 parser 模塊所描述的類和函數時你應當 總是顯式地指定你想要使用的策略。
本文檔的第一部分介紹了 Policy 的特性,它是一個 abstract base class,定義了所有策略對象包括 compat32 的共有特性。 這些特性包括一些由 email 包內部調用的特定鉤子方法,自定義策略可以重載這些方法以獲得不同行為。 第二部分描述了實體類 EmailPolicy 和 Compat32,它們分別實現了提供標準行為和向下兼容行為與特性的鉤子。
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()方法以及構造器語義的實現。可以向策略類的構造器傳入各種關鍵字參數。 可以指定的參數是該類的任何非方法特征屬性,以及實體類的任何額外非方法特征屬性。 在構造器中指定的值將覆蓋相應屬性的默認值。
這個類定義了下列特征屬性,因此下列值可以被傳給任何策略類的構造器:
-
linesep? 用來在序列化輸出中確定行的字符串。 默認值為
\n因為這是 Python 所使用的內部行結束符規范,但 RFC 的要求是\r\n。
-
cte_type? 控制可能要求使用的內容傳輸編碼格式類型。 可能的值包括:
7bit所有數據必須為 "純 7 比特位" (僅 ASCII)。 這意味著在必要情況下數據將使用可打印引用形式或 base64 編碼格式進行編碼。
8bit數據不會被限制為純 7 比特位。 標頭中的數據仍要求僅 ASCII 因此將被編碼(參閱下文的
fold_binary()和utf8了解例外情況),但消息體部分可能使用8bitCTE。cte_type值為8bit僅適用于BytesGenerator而非Generator,因為字符串不能包含二進制數據。 如果Generator運行于指定了cte_type=8bit的策略,它的行為將與cte_type為7bit相同。
-
raise_on_defect? 如為
True,則遇到的任何缺陷都將引發錯誤。 如為False(默認值),則缺陷將被傳遞給register_defect()方法。
-
mangle_from_? 如為
True,則消息體中以 "From " 開頭的行會通過在其前面放一個>來進行轉義。 當消息被生成器執行序列化時會使用此形參。 默認值t:False。3.5 新版功能: mangle_from_ 形參。
下列
Policy方法是由使用 email 庫的代碼來調用以創建具有自室外設置的策略實例:其余的
Policy方法是由 email 包代碼來調用的,而不應當被使用 email 包的應用程序所調用。 自定義的策略必須實現所有這些方法。-
handle_defect(obj, defect)? 處理在 obj 上發現的 defect。 當 email 包調用此方法時,defect 將總是
Defect的一個子類。默認實現會檢查
raise_on_defect旗標。 如果其為True,則 defect 會被作為異常來引發。 如果其為False(默認值),則 obj 和 defect 會被傳遞給register_defect()。
-
register_defect(obj, defect)? 在 obj 上注冊一個 defect。 在 email 包中,defect 將總是
Defect的一個子類。默認實現會調用 obj 的
defects屬性的append方法。 當 email 包調用handle_defect時,obj 通常將具有一個帶append方法的defects屬性。 配合 email 包使用的自定義對象類型(例如自定義的Message對象)也應當提供這樣的屬性,否則在被解析消息中的缺陷將引發非預期的錯誤。
-
header_max_count(name)? 返回名為 name 的標頭的最大允許數量。
當添加一個標頭到
EmailMessage或Message對象時被調用。 如果返回值不為0或None,并且已有的名稱為 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 包策略的兼容性,則 name 和 value 應當為字符串或字符串的子類,它們不會修改在參數中傳入的內容。
此方法沒有默認實現
-
header_fetch_parse(name, value)? 當標頭被應用程序所請求時,email 包會調用此方法并附帶當前保存在
Message中的 name 和 value,并且無論此方法返回什么它都會被回傳給應用程序作為被提取標頭的值。 請注意可能會有多個相同名稱的標頭被保存在Message中;此方法會將指定標頭的名稱和值返回給應用程序。value 可能包含經替代轉義的二進制數據。 此方法所返回的值應當沒有經替代轉義的二進制數據。
此方法沒有默認實現
-
-
class
email.policy.EmailPolicy(**kw)? 這個實體
Policy提供了完全遵循當前電子郵件 RFC 的行為。 這包括 (但不限于) RFC 5322, RFC 2047 以及當前的各種 MIME RFC。此策略添加了新的標頭解析和折疊算法。 標頭不是簡單的字符串,而是帶有依賴于字段類型的屬性的
str的子類。 這個解析和折疊算法完整實現了 RFC 2047 和 RFC 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? 該可調用對象接受兩個參數,
name和value,其中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_source_parse(sourcelines)? 此名稱會被作為到 '
:' 止的所有內容來解析。 該值是通過從第一行的剩余部分去除前導空格,再將所有后續行連接到一起,并去除所有末尾回車符或換行符來確定的。
-
header_store_parse(name, value)? name 將會被原樣返回。 如果輸入值具有
name屬性并可在忽略大小寫的情況下匹配 name,則 value 也會被原樣返回。 在其他情況下 name 和 value 會被傳遞給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 編碼。
-
以下 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類似但是utf8為True。 適用于在不使用標頭內已編碼字的情況下對消息進行序列化。 如果發送方或接收方地址具有非 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 發生了以下幾方面變化:
從應用程序的視角來看,這意味著任何通過 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對象。 在其他情況下它會被原樣返回。
-
備注
