with
内の例外は__exit__
が正しく呼び出されないことを読んだと思います。このメモに誤りがある場合は、私の無知をご容赦ください。
したがって、ここにいくつかの疑似コードがあります。私の目標は、__enter__
が開始日時を記録してロックIDを返し、__exit__
が終了日時を記録してロックを解放するロックコンテキストを使用することです。
def main():
raise Exception
with cron.lock() as lockid:
print('Got lock: %i' % lockid)
main()
安全に既存のコンテキストに加えて、エラーを発生させるにはどうすればよいですか?
注:予期される例外だけでなく、例外が発生しても安全に終了したいので、この疑似コードで意図的にベース例外を発生させます。
注:代替/標準の同時実行防止方法は関係ありません。この知識を一般的なコンテキスト管理に適用したいと思います。異なるコンテキストに異なる癖があるかどうかわかりません。
PS。 finally
ブロックは関連していますか?
___exit__
_メソッド通常どおり呼び出されますコンテキストマネージャが例外によって壊れた場合。実際、___exit__
_に渡されるパラメーターはすべて、このケースの処理に関係しています。 the docs から:
object.__exit__(self, exc_type, exc_value, traceback)
このオブジェクトに関連するランタイムコンテキストを終了します。パラメータは、コンテキストが終了する原因となった例外を示します。コンテキストが例外なく終了した場合、3つの引数はすべてNoneになります。
例外が提供され、メソッドが例外を抑制したい場合(つまり、例外が伝播されないようにする場合)、trueを返す必要があります。それ以外の場合、このメソッドの終了時に例外は通常どおり処理されます。
__exit__()
メソッドは、渡された例外を再発生させないように注意してください。これは呼び出し側の責任です。
したがって、___exit__
_メソッドが実行され、デフォルトで、例外がコンテキストマネージャーを終了した後afterに再発生することがわかります。簡単なコンテキストマネージャを作成し、例外を付けてこれを解除することで、これを自分でテストできます。
_DummyContextManager(object):
def __enter__(self):
print('Entering...')
def __exit__(self, exc_type, exc_value, traceback):
print('Exiting...')
# If we returned True here, any exception would be suppressed!
with DummyContextManager() as foo:
raise Exception()
_
このコードを実行すると、必要なものがすべて表示されます(print
はトレースバックの途中で終了する傾向があるため、順序が狂っている可能性があります)。
_Entering...
Exiting...
Traceback (most recent call last):
File "C:\foo.py", line 8, in <module>
raise Exception()
Exception
_
@contextlib.contextmanager
を使用する際のベストプラクティスは、上記の回答からははっきりしませんでした。 @ BenUsman からのコメントで link をフォローしました。
コンテキストマネージャを作成している場合は、yield
をtry-finally
ブロックでラップする必要があります。
from contextlib import contextmanager
@contextmanager
def managed_resource(*args, **kwds):
# Code to acquire resource, e.g.:
resource = acquire_resource(*args, **kwds)
try:
yield resource
finally:
# Code to release resource, e.g.:
release_resource(resource)
>>> with managed_resource(timeout=3600) as resource:
... # Resource is released at the end of this block,
... # even if code in the block raises an exception