18.1.3. "email.generator": MIME 文書を生成する
**********************************************

よくある作業のひとつは、メッセージオブジェクト構造からフラットな電子メ
ールテキストを生成することです。この作業は "smtplib" や "nntplib" モジ
ュールを使ってメッセージを送信したり、メッセージをコンソールに出力した
りするときに必要になります。あるメッセージオブジェクト構造をとってきて
、そこからフラットなテキスト文書を生成するのは "Generator" クラスの仕
事です。

繰り返しになりますが、 "email.parser" モジュールと同じく、この機能はこ
こでの定義済みジェネレータに制限されるわけではありません。これらはご自
身でゼロから作りあげることもできます。しかしながら、定義済みのジェネレ
ータはほとんどの電子メールを標準に沿ったやり方で生成する方法を知ってい
ますし、MIME メッセージも非 MIME メッセージもとても上手く扱えます。さ
らにこれはフラットなテキストから "Parser" クラスを使ってメッセージ構造
に変換し、それをまたフラットなテキストに戻しても、結果が冪等 (入力が出
力と同じになる) [1] になるよう設計されています。一方で、プログラムによ
って構成された "Message" のジェネレータを使う場合、デフォルトの挿入に
よって "Message" オブジェクトを変えてしまうかもしれません。

"email.generator" モジュールからインポートされる "Generator" クラスで
公開されているメソッドには、以下のようなものがあります:

class email.generator.Generator(outfp[, mangle_from_[, maxheaderlen]])

   "Generator" クラスのコンストラクタは *outfp* と呼ばれるストリーム形
   式 (file-like) のオブジェクトひとつを引数にとります。 *outfp* は
   "write()" メソッドをサポートし、 Python 拡張 print 文の出力ファイル
   として使えるようになっている必要があります。

   オプション引数 *mangle_from_* はフラグで、"True" のときはメッセージ
   本体の行で厳密に "From" で始まるもの、つまり行の先頭が "From" でそ
   の後に空白が続く行の前に ">" という文字を挿入します。これは、このよ
   うな行が Unix の mailbox 形式のエンペローブヘッダ区切り文字列として
   誤認識されるのを防ぐための、移植性ある唯一の方法です (詳しくは WHY
   THE CONTENT-LENGTH FORMAT IS BAD (なぜ Content-Length 形式が有害か)
   を参照してください)。デフォルトでは *mangle_from_* は "True" になっ
   ていますが、Unix の mailbox 形式ファイルに出力しないのならばこれは
   "False" に設定してもかまいません。

   オプション引数 *maxheaderlen* は連続していないヘッダの最大長を指定
   します。ひとつのヘッダ行が *maxheaderlen* (これは文字数です、tab は
   空白 8文字に展開されます) よりも長い場合、ヘッダは "Header" クラス
   で定義されているように途中で折り返され、間にはセミコロンが挿入され
   ます。もしセミコロンが見つからない場合、そのヘッダは放置されます。
   ヘッダの折り返しを禁止するにはこの値にゼロを指定してください。デフ
   ォルトは 78 文字で、 **RFC 2822** で推奨されている (ですが強制では
   ありません) 値です。

   これ以外のパブリックな "Generator" メソッドは以下のとおりです:

   flatten(msg[, unixfrom])

      *msg* を基点とするメッセージオブジェクト構造体の文字表現を出力し
      ます。出力先のファイルにはこの "Generator" インスタンスが作成さ
      れたときに指定されたものが使われます。各 subpart は深さ優先順序
      (depth-first) で出力され、得られるテキストは適切に MIME エンコー
      ドされたものになっています。

      オプション引数 *unixfrom* は、基点となるメッセージオブジェクトの
      最初の **RFC 2822** ヘッダが現れる前に、エンペローブヘッダ区切り
      文字列を出力することを強制するフラグです。そのメッセージオブジェ
      クトがエンペローブヘッダをもたない場合、標準的なエンペローブヘッ
      ダが自動的に作成されます。デフォルトではこの値は "False" に設定
      されており、エンペローブヘッダ区切り文字列は出力されません。

      注意: 各 subpart に関しては、エンペローブヘッダは出力されません
      。

      バージョン 2.2.2 で追加.

   clone(fp)

      この "Generator" インスタンスの独立したクローンを生成し返します
      。オプションはすべて同一になっています。

      バージョン 2.2.2 で追加.

   write(s)

      文字列 *s* を既定のファイルに出力します。ここでいう出力先は
      "Generator" コンストラクタに渡した *outfp* のことをさします。こ
      の関数はただ単に拡張 print 文で使われる "Generator" インスタンス
      に対してファイル操作風の API を提供するためだけのものです。

ユーザの便宜をはかるため、メソッド "Message.as_string()" と
"str(aMessage)" (つまり "Message.__str__()" のことです) をつかえばメッ
セージオブジェクトを特定の書式でフォーマットされた文字列に簡単に変換す
ることができます。詳細は "email.message" を参照してください。

"email.generator" モジュールはひとつの派生クラスも提供しています。これ
は "DecodedGenerator" と呼ばれるもので、 "Generator" 基底クラスと似て
いますが、非 *text* 型の subpart を特定の書式でフォーマットされた表現
形式で置きかえるところが違っています。

class email.generator.DecodedGenerator(outfp[, mangle_from_[, maxheaderlen[, fmt]]])

   このクラスは "Generator" から派生したもので、メッセージの subpart
   をすべて渡り歩きます。subpart の主形式が *text* だった場合、これは
   その subpart のペイロードをデコードして出力します。オプション引数
   *_mangle_from_* および *maxheaderlen* の意味は基底クラス
   "Generator" のそれと同じです。

   Subpart の主形式が *text* ではない場合、オプション引数 *fmt* がその
   メッセージペイロードのかわりのフォーマット文字列として使われます。
   *fmt* は "%(keyword)s" のような形式を展開し、以下のキーワードを認識
   します:

   * "type" -- 非 *text* 型 subpart の MIME 形式

   * "maintype" -- 非 *text* 型 subpart の MIME 主形式 (maintype)

   * "subtype" -- 非 *text* 型 subpart の MIME 副形式 (subtype)

   * "filename" -- 非 *text* 型 subpart のファイル名

   * "description" -- 非 *text* 型 subpart につけられた説明文字列

   * "encoding" -- 非 *text* 型 subpart の Content-transfer-encoding

   *fmt* のデフォルト値は "None" です。こうすると以下の形式で出力しま
   す

      [Non-text (%(type)s) part of message omitted, filename %(filename)s]

   バージョン 2.2.2 で追加.

バージョン 2.5 で変更: 以前の非推奨メソッド "__call__()" は削除されま
した。

-[ 注記 ]-

[1] ここではあなたが "unixfrom" 引数に適切な設定を使い、
    maxheaderlen=0 (入力行がどんな長さでも元のものを維持します) をセッ
    トしていることを前提にしています。これでもなお厳密には正しくありま
    せん。多くの場合ヘッダの連続する空白が単一の空白に置き換えられるか
    らです。これはいずれは修正されるべきバグです。
