11

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.

  • This is an interesting question! Let's see... – AKX Apr 17 at 14:04
9

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
  • 1
    @gerrit Good catch, yeah. :) – AKX Apr 17 at 14:28
  • 1
    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. – Masklinn Apr 17 at 16:07
  • @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 Apr 23 at 8:22
  • @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. :) – AKX Apr 23 at 8:58
1

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.