2013-08-23 21 views
5

での例外の再調達:__exit__()メソッドは、渡された例外を再呼出しべきではないとは、コンテキストマネージャ上<a href="http://docs.python.org/2/reference/datamodel.html#object.__exit__" rel="nofollow">datamodel docs</a>からコンテキストハンドラ

注意を。これは発信者の責任です。


私は、そのファイル記述子私はcloseではなく、ディスクには何も記述せずに解放したいのですが、一時ファイルを、持っています。私の直感的な解決策は、例外を渡すことでしたが、それはdiscouraged in the docsです - と確かに良い理由があります。また

class Processor(object): 
    ... 
    def write(self, *args, **kwargs): 
     if something_bad_happens: 
      raise RuntimeError('This format expects %s columns: %s, got %s.' % (
           (len(self.cols), self.cols, len(args)))) 
     self.writer.writerow(args) 

    def __enter__(self): 
     return self 

    def __exit__(self, type, value, traceback): 
     # the RuntimeError from write will be set as type, value and so on .. 
     # I'd like to close the stream here (release the file descriptor), 
     # but I do not leave a trace of the associated file - 
     # (one can always 'manually' delete with `os.remove` but maybe there's a 
     # better way ..?) 
     self.output_pipe.close() 

私は2つの理由から、呼び出し側では、この特定の場合のエラー処理にはしたくない。(下記参照)、最小、呼び出し元のコードを維持するために

  • 呼び出し側が満足しています例外を除いて

コンテキストマネージャは、このようなものを使用されている(速いが、私たちはここに何をしたいです失敗):

class Worker(object): 
    ... 
    def run(self): 
     # output setup so it will emit a three column CSV 
     with self.output().open('w') as output: 
      output.write('John', 'CA', 92101) 
      output.write('Jane', 'NY', 10304) 
      # should yield an error, since only three 'columns' are allowed 
      output.write('Hello', 'world') 

アップデート:私の問題は、本当にこれに煮詰めとして私のquesionは少し病気で処方されました:ネストされたコンテキストマネージャでは、どのように私は、最も外側のCMに上の例外を渡すことができますか?

+1

あなたが本当に__exit__' 'で二回出力を閉じるようにもしかして:あなたは__enter__にを変更する必要がありますか? – user2357112

+0

@ user2357112、ここには含まれていないコードがたくさんあるので、コードは簡潔に見え、文脈に合わせるのが難しいかもしれません。申し訳ありません。私の質問が更新されました。私は 'output_pipe'を2回閉じません。 – miku

答えて

6
  • __exit__がTrueを返すと、それに渡された例外はすべて飲み込まれます。
  • __exit__がFalseを返すと、例外が発生します。

def __exit__(self, type, value, traceback): 
    self.output_pipe.close() # always close the file 
    if type is not None: # an exception has occurred 
     os.unlink(...) # remove the file 
     return False  # reraise the exception 

Pythonはデフォルトで(Falsishである)Noneを返しますので、もちろんreturn Falseを省略することができます。ところで


は、Processorself.output()インスタンスのですか?

with self.output().open('w') as output: 

は、いずれの場合も

with self.output() as output: 

をする必要があり、その場合は後者が正しい構文であるためにあなたがアレンジすることができれば、それはよりよいだろう。

def __enter__(self): 
    return self.output_pipe.open('w') 
+0

ありがとう、私は 'return False'が私が検索したいと思っていると思います。 – miku

+1

@miku:*任意の*偽の値があります。 「なし」は含まれています。明示的に返されない場合は、「None」が返され、例外が発生したままになります。 –

+1

@MartijnPieters、そうです。私は明示的な「偽」が現時点ではもう少し手直し可能であることがわかります。私はこれに関する規則の標準ライブラリをチェックしなければなりません... – miku

2

はありません。は、raiseの例外です。例外はがすでにになりました。あなたのコンテキストマネージャーに通知されているだけです。

テストの代わりに例外がなかった場合:あなたのコンテキストマネージャは、その時点でTrueを返却した場合

if type is None: 
    # if no exception is raised, proceed as usual: 
    self.output_pipe.close() 

が、あなたは例外を抑制するだろう。代わりに関数を終了するとNoneが返され、例外は '発生した'ままです。

tempfile moduleには、プラットフォームとは独立して、がすでにという自己削除型のコンテキストマネージャとして機能する2種類の一時ファイルオブジェクトが含まれています。 POSIXシステムでは、作成直後にファイルのリンクを解除することができます。ファイルを閉じるまで、ファイル記述子は生きています。 Windowsでは「閉じるときに削除する」オプションも用意されています。 tempfile.NamedTemporaryFile()クラスは、使用するプラットフォームに適切なオプションを使用します。

+0

これはうまく見えますが、私が望むことをするでしょうが、その時点で 'w'モードで開いているファイルである' output_pipe'のリソース(ファイル記述子)を解放しますか? – miku

+0

どうしてテストするのですか? – user2357112

+0

@miku:あなたのコードは 'self.output_pipe.close()'を呼び出します** ** 2回**;例外に遭遇しなかったときにのみ、2回目です。私は単に例外を処理する方法を示しました。 –

関連する問題