How to determine if an exception was raised once youre in the finally block?

2024/11/19 17:35:10

Is it possible to tell if there was an exception once you're in the finally clause? Something like:

try:funky code
finally:if ???:print('the funky code raised')

I'm looking to make something like this more DRY:

try:funky code
except HandleThis:# handle itraised = True
except DontHandleThis:raised = Trueraise
else:raised = False
finally:logger.info('funky code raised %s', raised)

I don't like that it requires to catch an exception, which you don't intend to handle, just to set a flag.


Since some comments are asking for less "M" in the MCVE, here is some more background on the use-case. The actual problem is about escalation of logging levels.

  • The funky code is third party and can't be changed.
  • The failure exception and stack trace does not contain any useful diagnostic information, so using logger.exception in an except block is not helpful here.
  • If the funky code raised then some information which I need to see has already been logged, at level DEBUG. We do not and can not handle the error, but want to escalate the DEBUG logging because the information needed is in there.
  • The funky code does not raise, most of the time. I don't want to escalate logging levels for the general case, because it is too verbose.

Hence, the code runs under a log capture context (which sets up custom handlers to intercept log records) and some debug info gets re-logged retrospectively:

try:with LogCapture() as log:funky_code()  # <-- third party badness
finally:# log events are buffered in memory. if there was an exception,# emit everything that was captured at a WARNING levelfor record in log.captured:if <there was an exception>:log_fn = mylogger.warningelse:log_fn = getattr(mylogger, record.levelname.lower())log_fn(record.msg, record.args)
Answer

Using a contextmanager

You could use a custom contextmanager, for example:

class DidWeRaise:__slots__ = ('exception_happened', )  # instances will take less memorydef __enter__(self):return selfdef __exit__(self, exc_type, exc_val, exc_tb):# If no exception happened the `exc_type` is Noneself.exception_happened = exc_type is not None

And then use that inside the try:

try:with DidWeRaise() as error_state:# funky code
finally:if error_state.exception_happened:print('the funky code raised')

It's still an additional variable but it's probably a lot easier to reuse if you want to use it in multiple places. And you don't need to toggle it yourself.

Using a variable

In case you don't want the contextmanager I would reverse the logic of the trigger and toggle it only in case no exception has happened. That way you don't need an except case for exceptions that you don't want to handle. The most appropriate place would be the else clause that is entered in case the try didn't threw an exception:

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')

And as already pointed out instead of having a "toggle" variable you could replace it (in this case) with the desired logging function:

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 levelmylog = mylogger.DEBUG
finally:for record in log.captured:mylog(record.msg, record.args)

Of course it would also work if you put it at the end of your try (as other answers here suggested) but I prefer the else clause because it has more meaning ("that code is meant to be executed only if there was no exception in the try block") and may be easier to maintain in the long run. Although it's still more to maintain than the context manager because the variable is set and toggled in different places.

Using sys.exc_info (works only for unhandled exceptions)

The last approach I want to mention is probably not useful for you but maybe useful for future readers who only want to know if there's an unhandled exception (an exception that was not caught in any except block or has been raised inside an except block). In that case you can use sys.exc_info:

import systry:# 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 exceptionprint('funky code raised')
https://en.xdnf.cn/q/26418.html

Related Q&A

How to use re match objects in a list comprehension

I have a function to pick out lumps from a list of strings and return them as another list:def filterPick(lines,regex):result = []for l in lines:match = re.search(regex,l)if match:result += [match.grou…

How do I run pip on python for windows? [duplicate]

This question already has answers here:Why does "pip install" inside Python raise a SyntaxError?(6 answers)Closed 8 years ago.Ive just installed python 3.5, ran Python 3.5 (32-bit) and typed…

Select certain rows by index of another DataFrame

I have a DataFrame and I would select only rows that contain index value into df1.index. for Example: In [96]: df Out[96]:A B C D 1 1 4 9 1 2 4 5 0 2 3 5 5 1 0 22 1 3 9 6and these ind…

Execute .sql schema in psycopg2 in Python

I have a PostgreSQL schema stored in .sql file. It looks something like:CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY,facebook_id TEXT NOT NULL,name TEXT NOT NULL,access_token TEXT,created I…

AttributeError: module html.parser has no attribute HTMLParseError

This is the hints,how can I resolve it? I use Python 3.5.1 created a virtual envirement by virtualenv The source code works well on my friends computer machineError: Traceback (most recent call last):…

Error message python-pylint C0103:Invalid constant name

Im confused about the error(s) in this photo:I dont know how to fix them. My program is a Python-Flask web frame. When I use Visual Studio Code to debug my program, Pylint shows these errors. I know th…

How to use virtualenv with python3.6 on ubuntu 16.04?

Im using Ubuntu 16.04, which comes with Python 2.7 and Python 3.5. Ive installed Python 3.6 on it and symlink python3 to python3.6 through alias python3=python3.6.Then, Ive installed virtualenv using s…

Is a day always 86,400 epoch seconds long?

While reviewing my past answers, I noticed Id proposed code such as this:import timedef dates_between(start, end):# muck around between the 9k+ time representation systems in Python# now start and end …

Find the first instance of a nonzero number in a list in Python [duplicate]

This question already has answers here:Return the index of the first element of a list which makes a passed function true(7 answers)Closed last year.I have a list like this: myList = [0.0, 0.0, 0.0, 2.…

How to debug a Python module in Visual Studio Codes launch.json

My question may seem simple but, I have a module that I launch in a terminal like this: python -m my_module.my_fileHow do I debug this in Visual Studio Code? I have this in my launch.json (documentati…