__exit__()
に例外があったとしても、__enter__()
メソッドが確実に呼び出されるようにすることは可能ですか?
>>> class TstContx(object):
... def __enter__(self):
... raise Exception('Oops in __enter__')
...
... def __exit__(self, e_typ, e_val, trcbak):
... print "This isn't running"
...
>>> with TstContx():
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __enter__
Exception: Oops in __enter__
>>>
編集
これは私が得ることができる限り近い...
class TstContx(object):
def __enter__(self):
try:
# __enter__ code
except Exception as e
self.init_exc = e
return self
def __exit__(self, e_typ, e_val, trcbak):
if all((e_typ, e_val, trcbak)):
raise e_typ, e_val, trcbak
# __exit__ code
with TstContx() as tc:
if hasattr(tc, 'init_exc'): raise tc.init_exc
# code in context
後から考えると、コンテキストマネージャーは最良の設計上の決定ではなかった可能性があります
このような:
import sys
class Context(object):
def __enter__(self):
try:
raise Exception("Oops in __enter__")
except:
# Swallow exception if __exit__ returns a True value
if self.__exit__(*sys.exc_info()):
pass
else:
raise
def __exit__(self, e_typ, e_val, trcbak):
print "Now it's running"
with Context():
pass
コンテキストブロックを実行せずにプログラムを陽気に続行するには、コンテキストブロック内のコンテキストオブジェクトを検査し、__enter__
成功しました。
class Context(object):
def __init__(self):
self.enter_ok = True
def __enter__(self):
try:
raise Exception("Oops in __enter__")
except:
if self.__exit__(*sys.exc_info()):
self.enter_ok = False
else:
raise
return self
def __exit__(self, e_typ, e_val, trcbak):
print "Now this runs twice"
return True
with Context() as c:
if c.enter_ok:
print "Only runs if enter succeeded"
print "Execution continues"
私が判断できる限り、with-blockを完全にスキップすることはできません。そして、このコンテキストは、その中のall例外を飲み込むことに注意してください。例外を飲み込みたくない場合は__enter__
成功、チェックself.enter_ok
__exit__
およびreturn False
True
の場合。
いいえ。例外が__enter__()
で発生する可能性がある場合は、自分でキャッチして、クリーンアップコードを含むヘルパー関数を呼び出す必要があります。
contextlib.ExitStack
を使用できます(テストされていません):
with ExitStack() as stack:
cm = TstContx()
stack.Push(cm) # ensure __exit__ is called
with ctx:
stack.pop_all() # __enter__ succeeded, don't call __exit__ callback
または the docs の例:
stack = ExitStack()
try:
x = stack.enter_context(cm)
except Exception:
# handle __enter__ exception
else:
with stack:
# Handle normal case
contextlib2 on Python <3. を参照してください。
継承または複雑なサブルーチンが必要ない場合は、より短い方法を使用できます。
from contextlib import contextmanager
@contextmanager
def test_cm():
try:
# dangerous code
yield
except Exception, err
pass # do something
RAII(リソースの取得は初期化)に従い、コンテキストのコンストラクターを使用して、失敗する可能性のある割り当てを行うことをお勧めします。次に、あなたの__enter__
は、例外を発生させることのない自己を返すことができます。コンストラクタが失敗した場合、withコンテキストに入る前に例外がスローされる可能性があります。
class Foo:
def __init__(self):
print("init")
raise Exception("booh")
def __enter__(self):
print("enter")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("exit")
return False
with Foo() as f:
print("within with")
出力:
init
Traceback (most recent call last):
File "<input>", line 1, in <module>
...
raise Exception("booh")
Exception: booh
class MyContext:
def __enter__(self):
try:
pass
# exception-raising code
except Exception as e:
self.__exit__(e)
def __exit__(self, *args):
# clean up code ...
if args[0]:
raise
私はこのようにそれをしました。エラーを引数として__exit __()を呼び出します。 args [0]にエラーが含まれている場合、クリーンアップコードの実行後に例外が再発生します。