From 56ac2d3f87fc9c134af3f3508d32de38fb538d4f Mon Sep 17 00:00:00 2001 From: Matt Rixman Date: Sun, 14 Jul 2024 11:53:15 -0600 Subject: [PATCH] Ensuring opened tty file is closed on exit --- pudb/__init__.py | 20 ++++++++++++++------ pudb/debugger.py | 16 +++++++++++++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/pudb/__init__.py b/pudb/__init__.py index 278943f3..c0daa4cd 100644 --- a/pudb/__init__.py +++ b/pudb/__init__.py @@ -83,6 +83,7 @@ def _get_debugger(**kwargs): kwargs.setdefault("stdin", tty_file) kwargs.setdefault("stdout", tty_file) kwargs.setdefault("term_size", term_size) + kwargs.setdefault("tty_file", tty_file) from pudb.debugger import Debugger dbg = Debugger(**kwargs) @@ -111,12 +112,19 @@ def runmodule(*args, **kwargs): runscript(*args, **kwargs) -def runscript(mainpyfile, args=None, pre_run="", steal_output=False, - _continue_at_start=False, run_as_module=False): - dbg = _get_debugger( - steal_output=steal_output, - _continue_at_start=_continue_at_start, - ) +def runscript(mainpyfile, steal_output=False, _continue_at_start=False, + **kwargs): + try: + dbg = _get_debugger( + steal_output=steal_output, + _continue_at_start=_continue_at_start, + ) + _runscript(dbg, **kwargs) + finally: + dbg.__del__() + + +def _runscript(mainpyfile, args=None, pre_run="", run_as_module=False): # Note on saving/restoring sys.argv: it's a good idea when sys.argv was # modified by the script being debugged. It's a bad idea when it was diff --git a/pudb/debugger.py b/pudb/debugger.py index a9f808f9..3abb26f7 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -187,7 +187,7 @@ class Debugger(bdb.Bdb): _current_debugger = [] def __init__(self, stdin=None, stdout=None, term_size=None, steal_output=False, - _continue_at_start=False, **kwargs): + _continue_at_start=False, tty_file=None, **kwargs): if Debugger._current_debugger: raise ValueError("a Debugger instance already exists") @@ -197,6 +197,7 @@ def __init__(self, stdin=None, stdout=None, term_size=None, steal_output=False, self.ui = DebuggerUI(self, stdin=stdin, stdout=stdout, term_size=term_size) self.steal_output = steal_output self._continue_at_start__setting = _continue_at_start + self._tty_file = tty_file self.setup_state() @@ -214,8 +215,17 @@ def __init__(self, stdin=None, stdout=None, term_size=None, steal_output=False, self._current_debugger.append(self) def __del__(self): - assert self._current_debugger == [self] - self._current_debugger.pop() + # according to https://stackoverflow.com/a/1481512/1054322, the garbage + # collector cannot be relied on to call this, so we call it explicitly + # in a finally (see __init__.py:runscript). But then, the garbage + # collector *might* call it, so it should tolerate being called twice. + + if self._current_debugger: + assert self._current_debugger == [self] + self._current_debugger.pop() + if self._tty_file: + self._tty_file.close() + self._tty_file = None # These (dispatch_line and set_continue) are copied from bdb with the # patch from https://bugs.python.org/issue16482 applied. See