finally
節に入ったら、例外があったかどうかを知ることはできますか?何かのようなもの:
try:
funky code
finally:
if ???:
print('the funky code raised')
私はこのようなものをもっと乾燥させたいと思っています:
try:
funky code
except HandleThis:
# handle it
raised = True
except DontHandleThis:
raised = True
raise
else:
raised = False
finally:
logger.info('funky code raised %s', raised)
フラグを設定するためだけに、処理するつもりのない例外をキャッチする必要があるのは好きではありません。
一部の comments がMCVEで「M」を少なくするように要求しているため、ここではユースケースの背景について説明します。実際の問題は、ログレベルのエスカレーションです。
logger.exception
例外ブロックの場合、ここでは役に立ちません。したがって、コードはログキャプチャコンテキスト(ログレコードをインターセプトするカスタムハンドラーをセットアップする)の下で実行され、一部のデバッグ情報は遡及的に再記録されます。
try:
with LogCapture() as log:
funky_code() # <-- third party badness
finally:
mylog = mylogger.WARNING if <there was exception> else mylogger.DEBUG
for record in log.captured:
mylog(record.msg, record.args)
raised = True
try:
funky code
raised = False
except HandleThis:
# handle it
finally:
logger.info('funky code raised %s', raised)
ログレベルの選択に関する質問に追加の背景情報が追加されていることを考えると、これは意図したユースケースに非常に簡単に適合しているように見えます。
mylog = WARNING
try:
funky code
mylog = DEBUG
except HandleThis:
# handle it
finally:
mylog(...)
たとえば、カスタムコンテキストマネージャーを使用できます。
class DidWeRaise:
__slots__ = ('exception_happened', ) # instances will take less memory
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# If no exception happened the `exc_type` is None
self.exception_happened = exc_type is not None
そして、それをtry
内で使用します:
try:
with DidWeRaise() as error_state:
# funky code
finally:
if error_state.exception_happened:
print('the funky code raised')
これはまだ追加の変数ですが、複数の場所で使用する場合はおそらく再利用がはるかに簡単です。そして、自分で切り替える必要はありません。
Contextmanagerが必要ない場合は、トリガーのロジックを逆にして、no例外が発生した場合にのみonlyを切り替えます。そうすれば、処理したくない例外に対してexcept
ケースは必要ありません。最も適切な場所は、else
が例外をスローしなかった場合に入力されるtry
句です。
exception_happened = True
try:
# funky code
except HandleThis:
# handle this kind of exception
else:
exception_happened = False
finally:
if exception_happened:
print('the funky code raised')
そして、「トグル」変数を持つ代わりに既に指摘したように、(この場合)目的のロギング関数に置き換えることができます:
mylog = mylogger.WARNING
try:
with LogCapture() as log:
funky_code()
except HandleThis:
# handle this kind of exception
else:
# In case absolutely no exception was thrown in the try we can log on debug level
mylog = mylogger.DEBUG
finally:
for record in log.captured:
mylog(record.msg, record.args)
もちろん、あなたのtry
の最後にそれを置いても機能します(ここで他の回答が示唆しているように)が、より意味があるのでelse
節を好みます(「そのコードはtry
ブロックに例外がなかった場合にのみ実行されます)」と長期的に維持する方が簡単かもしれません。ただし、変数はさまざまな場所で設定および切り替えられるため、コンテキストマネージャーよりも維持する必要があります。
sys.exc_info
の使用(未処理の例外に対してのみ機能します)私が言及したい最後のアプローチはおそらくあなたには役に立たないでしょうが、おそらく未処理例外(notキャッチされた例外) except
ブロック内、またはexcept
ブロック内で発生した)。その場合、 sys.exc_info
を使用できます。
import sys
try:
# funky code
except HandleThis:
pass
finally:
if sys.exc_info()[0] is not None:
# only entered if there's an *unhandled* exception, e.g. NOT a HandleThis exception
print('funky code raised')
さて、実際に既存のコンテキストマネージャーを変更するか、同様のアプローチを使用するだけのように聞こえますが、logbook
には実際に FingersCrossedHandler
と呼ばれるものがありますまさにあなたが望むことをするでしょう。ただし、次のように自分で実行できます。
@contextmanager
def LogCapture():
# your existing buffer code here
level = logging.WARN
try:
yield
except UselessException:
level = logging.DEBUG
raise # Or don't, if you just want it to go away
finally:
# emit logs here
元の応答
あなたはこれについて少し横向きに考えています。
あなたはdo例外を処理するつもりです-フラグを設定することで例外を処理しています。たぶんあなたは他に何も気にしない(悪い考えのように思えます)が、an例外が発生したときに何かをすることを気にするなら、あなたは明示的にしたいですそれ。
変数を設定しているが、例外を続行したいという事実は、reallyしたいことは、発生した例外から独自の特定の例外を発生させることであることを意味します:
class MyPkgException(Exception): pass
class MyError(PyPkgException): pass # If there's another exception type, you can also inherit from that
def do_the_badness():
try:
raise FileNotFoundError('Or some other code that raises an error')
except FileNotFoundError as e:
raise MyError('File was not found, doh!') from e
finally:
do_some_cleanup()
try:
do_the_badness()
except MyError as e:
print('The error? Yeah, it happened')
これは解決します:
MyPkgException
をキャッチしてすべての例外をキャッチできるようにして、soいスタックトレースの代わりにNiceステータスで何かを記録して終了できるようにします。キャッチした例外を変数に簡単に割り当てて、finallyブロックで使用できます。例:
>>> x = 1
>>> error = None
>>> try:
... x.foo()
... except Exception as e:
... error = e
... finally:
... if error is not None:
... print(error)
...
'int' object has no attribute 'foo'