pudb icon indicating copy to clipboard operation
pudb copied to clipboard

Adding arbitrary logic to determine if PuDB stops

Open cool-RR opened this issue 8 years ago • 11 comments

I'm building a solution for my team to run tests and debug them. Tests are run by pytest and PuDB is the debugger. I use pytest-pudb to launch pudb, but I want to switch from it because I want PuDB to pause not only on an exception, but I want it to pause immediately when Pytest reaches the first line of the test. (i.e. after it's done with all the Pytest bureaucracy.)

I'm thinking, how can I accomplish this? Now that PuDB is able to start paused ( #234 ) I'll be able to start PuDB, but I want it to stop on the first line of the test. Our test framework only runs one test in a single run. Currently the only solution I can think of is to programmatically figure out the code file and line number of the test, and then set a breakpoint there. But that is really cumbersome and fragile.

I wish PuDB had some way of letting me give it a condition. i.e. "next time you step into a file in the folder /foo/bar whose name starts with test_, pause, and then cancel this condition." Of course, this specific condition is tailor-made to my problem and it's very unlikely to be useful for others. But, maybe PuDB could accept arbitrary conditions? i.e. it'll have an API where I can give it a function, and then on every line it calls that function, and if it returns True, PuDB pauses, and if False it doesn't. Then anyone could put their complex logic in that function.

What do you think?

cool-RR avatar Apr 05 '17 08:04 cool-RR

Pudb/bdb are already dog-slow when they're tracing. Adding more code to that code path (even just the do-we-have-a-condition-if) doesn't sound like a good recipe. But I have not measured the potential impact of this.

inducer avatar Apr 05 '17 15:04 inducer

You can completely avoid any impact if you do something like this (pseudocode):

def get_tracer_function():
    if user_given_condition is None:
        def tracer_function():
            # Your existing code
    else:
        def tracer_function():
            # Your existing code
            if user_given_condition():
                do_whatever()
            
    return tracer_function

cool-RR avatar Apr 05 '17 15:04 cool-RR

True. But then we'd have to replace the trace func from within bdb, which is deep into monkeypatch territory, or just replace bdb...

inducer avatar Apr 05 '17 16:04 inducer

Do you think it's possible to answer my particular need without this functionality?

On Wed, Apr 5, 2017 at 7:01 PM, Andreas Klöckner [email protected] wrote:

True. But then we'd have to replace the trace func from within bdb, which is deep into monkeypatch territory, or just replace bdb...

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/inducer/pudb/issues/241#issuecomment-291909252, or mute the thread https://github.com/notifications/unsubscribe-auth/AADdyoIUMoOzTiaAARp_cDluUsEQlqHNks5rs7q9gaJpZM4Mz8gO .

cool-RR avatar Apr 05 '17 16:04 cool-RR

You might be able to get the behavior you want with a custom import loader.

asmeurer avatar Apr 05 '17 16:04 asmeurer

Care to give a bit more details so I will have an idea on how to do this?

cool-RR avatar Apr 05 '17 16:04 cool-RR

Also: Looking at the bdb and pudb code now, I'm thinking that maybe I can override Debugger.dispatch_call, and put code that calls self.interaction if my condition is met. Do you think that could work?

cool-RR avatar Apr 05 '17 16:04 cool-RR

Is it even possible to subclass pudb.debugger.Debugger and get PuDB to use it?

cool-RR avatar Apr 05 '17 16:04 cool-RR

I believe you can insert it into pudb.CURRENT_DEBUGGER before the first set_trace() call.

asmeurer avatar Apr 05 '17 17:04 asmeurer

Thanks! I'll try.

cool-RR avatar Apr 05 '17 17:04 cool-RR

@asmeurer 's comment "custom import loader" got me thinking there really is no difference between these two, except when they are executed

  • now: "programmatically figure out the code file and line number . . . set a breakpoint"
  • deferred: "a condition to evaluate - next time you step into a file in the folder /foo/bar whose name starts with test_ . . . pause"

It seems it won't require anything more than file+line number, any other debugger details are available but not necessary. If the inputs (file, line number) are immutable then what's the point of deferring? If the inputs use patterns/folders/expressions, will they change over time? If not they can be evaluated once at startup without introducing fragility, then just use breakpoints without needing a change to PuDB.

qneill avatar May 22 '17 18:05 qneill