1

ipdb is just great; wow. The thing is, when a script blows up, I still have to go into the code and add four lines that require not a ton of typing, but not nothing either. For example, let's say this is the bad line:

1 = 2

Naturally, I get this:

SyntaxError: can't assign to literal

If for whatever reason I wanted to debug that line and see what was going on just before that line (or somewhere else in the stack at that moment), I would typically change that line of code as follows:

try:
    1 = 2
except:
    import traceback;traceback.print_exc()
    import ipdb;ipdb.set_trace()

That gets the job done, but I'd love to be able to run a script in a "mode" where whenever ANYTHING blows up (assuming the exception is otherwise unhandled), I get the same result.

Does that exist?

*******EDIT*******

Thanks to @np8's response, I redid the driver script to look like this (where main is any arbitrary function):

if __name__ == "__main__":
    start = time.time()
    args = parser.parse_args()

    if args.verbosity > 1:
        from ipdb import launch_ipdb_on_exception
        with launch_ipdb_on_exception():
            print("Launching ipdb on exception!")
            main(start, args)

    else:
        print("NOT launching ipdb on exception!")
        main(start, args)

This allows me, from the command line, to determine whether exceptions should launch ipdb (i.e. when I'm developing) or not (i.e. when the script is running in production and therefore with a verbosity argument below 2, in this example).

HaPsantran
  • 5,581
  • 6
  • 24
  • 39

3 Answers3

1

What you are looking for is "post mortem debugging". The easiest way to get what you want is to run the script using

ipdb script.py

or

python -m pdb script.py

instead of

python script.py
dseuss
  • 971
  • 6
  • 8
  • Thanks, @dseuss, but when I do that, and I hit the exception, the tracer puts me at the lowest frame of the stack but thinks the next one up is the top of the script (which is from __future__ import print_function). (I'm no longer using the simple script in my example.) It's a much longer script, but the line that actually triggered the exception doesn't seem available in the stack the way it is when I use import ipdb;ipdb.set_trace(). Am I missing something? – HaPsantran Oct 17 '17 at 04:02
  • I just noticed that post mortem debugging does not work the way I though it would when the error occurs outside any function like in your case. Not sure what's going on, but a simple workaround would be to pack everything into a `main()` function. – dseuss Oct 17 '17 at 06:50
  • Thanks, I'll try that shortly! – HaPsantran Oct 19 '17 at 15:14
  • I'm noticing that even when I run a script with a main function in this way, it still stops at the from __future___ line and does not get the desired result. – HaPsantran Oct 20 '17 at 15:49
  • When I try to run `ipdb script.py`, I get `command not found: ipdb`. Any idea how I can install this command? – abrac Nov 18 '20 at 16:02
  • I just had to install the `python-ipdb` package from my package manager (on Linux). It works flawlessly. I don't have @HaPsantran's problem. – abrac Nov 18 '20 at 17:41
0

You could use the launch_ipdb_on_exception context manager:

# test.py
from ipdb import launch_ipdb_on_exception

with launch_ipdb_on_exception():
    print(x)

Running the above will result into launching ipdb:

python .\test.py
NameError("name 'x' is not defined",)
> c:\tmp\delete_me\test.py(4)<module>()
      2
      3 with launch_ipdb_on_exception():
----> 4     print(x)

ipdb>
Niko Föhr
  • 28,336
  • 10
  • 93
  • 96
  • This is exactly what I needed; the one downside is having to nest everything with an additional layer of indentation (because this only exists—and maybe only can exist—as a context manager, per [the source](https://github.com/gotcha/ipdb/blob/master/ipdb/__main__.py)), but it's easy enough to just wrap the whole function and call *that* from within the `with` statement. I'll update the question with how I'm doing it. – HaPsantran Jan 07 '18 at 15:26
0

pm is a function decorator that mirrors the functionality of the ipdb context manager launch_ipdb_on_exception. Use it to decorate a function, and that function will go to ipdb on error. The name 'pm' comes from the ipdb.pm() function, which stands for postmortem, and behaves similarly.

With this, you can decorate the top-level function which you want to debug with @pm, and any exception at that level, or below, in the call stack will trigger ipdb.

import ipdb


class Decontext(object):
    """
    Makes a context manager also act as decorator
    """
    def __init__(self, context_manager):
        self._cm = context_manager

    def __enter__(self):
        return self._cm.__enter__()

    def __exit__(self, *args, **kwds):
        return self._cm.__exit__(*args, **kwds)

    def __call__(self, func):
        def wrapper(*args, **kwds):
            with self:
                return func(*args, **kwds)
        return wrapper

pm = Decontext(ipdb.launch_ipdb_on_exception())
monguin
  • 374
  • 4
  • 13