Skip to content

Commit

Permalink
Make the Debugger a singleton and do not reset the stopframe to None …
Browse files Browse the repository at this point in the history
…during postmortem.

This fixes #607. Making the Debugger a singleton should be enough to fix #607.
HOWEVER, pytest def postmortem() calls .reset() which resets the stopframe.
Somehow, this causes the debugger to stop somewhere inside internal debugger source code,
instead of in the code the postmortem should be done.

So, this also includes an override of the .reset() method and detects when the debugger
is already running and does not reset the stopframe. See also #67.
  • Loading branch information
hwalinga committed Nov 29, 2024
1 parent f0b2aa5 commit 5a3993e
Showing 1 changed file with 34 additions and 4 deletions.
38 changes: 34 additions & 4 deletions pudb/debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,15 +183,21 @@

# {{{ debugger interface

class Debugger(bdb.Bdb):
class Singleton(type):
_instance = None

def __call__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instance


class Debugger(bdb.Bdb, metaclass=Singleton):
_current_debugger = []

def __init__(self, stdin=None, stdout=None, term_size=None, steal_output=False,
_continue_at_start=False, tty_file=None, **kwargs):

if Debugger._current_debugger:
raise ValueError("a Debugger instance already exists")

# Pass remaining kwargs to python debugger framework
bdb.Bdb.__init__(self, **kwargs)
self.ui = DebuggerUI(self, stdin=stdin, stdout=stdout, term_size=term_size)
Expand All @@ -214,6 +220,30 @@ def __init__(self, stdin=None, stdout=None, term_size=None, steal_output=False,
# Okay, now we have a debugger
self._current_debugger.append(self)

def reset(self):
"""Set values of attributes as ready to start debugging.
Override from Bdb.reset()
When pytest starts a postmortem analysis, but the debugger is already active,
calling .reset() in src/_pytest/debugging.py::post_mortem
causes the self.stopframe to be set to None.
This pauses the debugger somewhere in the source code of the debugger. See #67
We detect using _current_debugger that this is the case and do not set the
stopframe to None then.
Related #607, #52
"""
import linecache
linecache.checkcache()
self.botframe = None
if not self._current_debugger:
self.stopframe = None
self.returnframe = None
self.quitting = False
self.stoplineno = 0

def __del__(self):
# according to https://stackoverflow.com/a/1481512/1054322, the garbage
# collector cannot be relied on to call this, so we call it explicitly
Expand Down

0 comments on commit 5a3993e

Please sign in to comment.