14

I'm debugging a function that I wrote as part of some form of plug-in framework. The function does not appear to be doing what it should, and I have the suspicion that, somewhere up the stack, someone is catching exceptions, or raise (either a very specific or a very generic) exception and test what happens (but if it's swallowed, it still doesn't tell me where). I could enter the debugger and inspect the source code at every stack level. Is there a more direct way to list any try-except blocks that the current code may be part of — specifically, the try-part of any such block?

This is, of course, exclusively for debugging purposes.

1
  • 1
    This is an interesting question! Let's see... Commented Apr 17, 2019 at 14:04

2 Answers 2

11

It's spectacularly possible I'm missing something here (I just eyeballed the dis.dis() output for the catcher function), but at least this catches simple cases of catching things on Python 3.7:

import sys
import dis


def are_we_being_caught():
    frame = sys._getframe(1)
    while frame:
        bytecode = dis.Bytecode(frame.f_code)
        except_stack = 0
        for instr in bytecode:
            if instr.opname == "SETUP_EXCEPT":  # Going into a try: except: block
                except_stack += 1
            elif instr.opname == "POP_EXCEPT":  # Exiting a try: except: block
                except_stack -= 1
            if instr.offset > frame.f_lasti:  # Past the current instruction, bail out
                break
        if except_stack:  # If we `break`ed in the middle of a SETUP/POP pair
            print(frame, "may be catching exceptions now")
        frame = frame.f_back


def catcher(fn):
    try:
        x = fn()
    except:
        x = None  # YOLO :D
    return x


def f1():
    return 8


def f2():
    are_we_being_caught()
    raise ValueError("foo")


print(catcher(f1))
print(catcher(f2))

outputs

8
<frame at 0x109d2d238, file 'so55729254.py', line 24, code catcher> may be catching exceptions now
None
Sign up to request clarification or add additional context in comments.

5 Comments

@gerrit Good catch, yeah. :)
That's a beautiful one, though you may also want to handle try/finally blocks (which IIRC are handled separately from try/except) as Python allows e.g. return statements in finally blocks and that will implicitly suppress exceptions.
@AKX May I develop your recipe into a github gist? Or perhaps you are interested in doing so yourself? Or under what license may I reuse/develop your recipe?
@gerrit Feel free to! I was actually thinking it might be useful as a library. While the general SO license is cc-by-sa 3.0 with attribution, I guess MIT would be useful for code. :)
Warning on this: If you use Python IDLE instead of the command line, then it will report /usr/lib/python3.7/idlelib/run.py is catching exceptions. Might be a better idea to check whether the catcher == __file__...
2

That's a pretty difficult one: internally, each frame maintains a stack of blocks but I don't think there's any API to access it (let alone from Python). So you'd have to walk the stackframes, disassemble the code to infer the span of your try blocks (see the SETUP_EXCEPT and SETUP_FINALLY opcodes), and see if the "current line" of the stack frame falls within that block.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.