8989import tempfile
9090import textwrap
9191import tokenize
92+ import functools
9293import itertools
9394import traceback
9495import linecache
@@ -1466,6 +1467,13 @@ def do_ignore(self, arg):
14661467
14671468 complete_ignore = _complete_bpnumber
14681469
1470+ def _prompt_for_confirmation (self , prompt , choices , default ):
1471+ try :
1472+ reply = input (prompt )
1473+ except EOFError :
1474+ reply = default
1475+ return reply .strip ().lower ()
1476+
14691477 def do_clear (self , arg ):
14701478 """cl(ear) [filename:lineno | bpnumber ...]
14711479
@@ -1475,11 +1483,11 @@ def do_clear(self, arg):
14751483 clear all breaks at that line in that file.
14761484 """
14771485 if not arg :
1478- try :
1479- reply = input ( 'Clear all breaks? ' )
1480- except EOFError :
1481- reply = 'no'
1482- reply = reply . strip (). lower ( )
1486+ reply = self . _prompt_for_confirmation (
1487+ 'Clear all breaks? ' ,
1488+ choices = ( 'y' , 'yes' , 'n' , 'no' ),
1489+ default = 'no' ,
1490+ )
14831491 if reply in ('y' , 'yes' ):
14841492 bplist = [bp for bp in bdb .Breakpoint .bpbynumber if bp ]
14851493 self .clear_all_breaks ()
@@ -2725,6 +2733,26 @@ def do_commands(self, arg):
27252733 def _create_recursive_debugger (self ):
27262734 return _RemotePdb (self ._sockfile , owns_sockfile = False )
27272735
2736+ @typing .override
2737+ def _prompt_for_confirmation (self , prompt , choices , default ):
2738+ self ._send (
2739+ confirm = {"prompt" : prompt , "choices" : choices , "default" : default }
2740+ )
2741+ payload = self ._sockfile .readline ()
2742+
2743+ if not payload :
2744+ return default
2745+
2746+ try :
2747+ match json .loads (payload ):
2748+ case {"confirmation_reply" : str (reply )}:
2749+ return reply
2750+ except json .JSONDecodeError :
2751+ pass
2752+
2753+ self .error (f"Ignoring unexpected remote message: { payload } " )
2754+ return default
2755+
27282756 def do_run (self , arg ):
27292757 self .error ("remote PDB cannot restart the program" )
27302758
@@ -2807,7 +2835,7 @@ def read_command(self, prompt):
28072835 return prefix + buffer
28082836
28092837 @contextmanager
2810- def readline_completion (self ):
2838+ def readline_completion (self , completer ):
28112839 try :
28122840 import readline
28132841 except ImportError :
@@ -2816,7 +2844,7 @@ def readline_completion(self):
28162844
28172845 old_completer = readline .get_completer ()
28182846 try :
2819- readline .set_completer (self . complete )
2847+ readline .set_completer (completer )
28202848 if readline .backend == "editline" :
28212849 # libedit uses "^I" instead of "tab"
28222850 command_string = "bind ^I rl_complete"
@@ -2828,7 +2856,7 @@ def readline_completion(self):
28282856 readline .set_completer (old_completer )
28292857
28302858 def cmdloop (self ):
2831- with self .readline_completion ():
2859+ with self .readline_completion (self . complete ):
28322860 while True :
28332861 try :
28342862 if not (payload_bytes := self .sockfile .readline ()):
@@ -2876,6 +2904,14 @@ def process_payload(self, payload):
28762904 self .prompt_for_breakpoint_command_list ("(com) " )
28772905 else :
28782906 self .prompt_for_repl_command (prompt )
2907+ case {
2908+ "confirm" : {
2909+ "prompt" : str (prompt ),
2910+ "choices" : list (choices ),
2911+ "default" : str (default ),
2912+ }
2913+ } if all (isinstance (c , str ) for c in choices ):
2914+ self .prompt_for_confirmation (prompt , choices , default )
28792915 case {
28802916 "commands_entry" : {
28812917 "bpnum" : int (bpnum ),
@@ -2930,6 +2966,20 @@ def prompt_for_repl_command(self, prompt):
29302966 self .sockfile .flush ()
29312967 return
29322968
2969+ def prompt_for_confirmation (self , prompt , choices , default ):
2970+ try :
2971+ with self .readline_completion (
2972+ functools .partial (self .complete_choices , choices = choices )
2973+ ):
2974+ reply = input (prompt ).strip ()
2975+ except (KeyboardInterrupt , EOFError ):
2976+ print (flush = True )
2977+ reply = default
2978+
2979+ payload = {"confirmation_reply" : reply }
2980+ self .sockfile .write ((json .dumps (payload ) + "\n " ).encode ())
2981+ self .sockfile .flush ()
2982+
29332983 def complete (self , text , state ):
29342984 import readline
29352985
@@ -2964,6 +3014,15 @@ def complete(self, text, state):
29643014 except IndexError :
29653015 return None
29663016
3017+ def complete_choices (self , text , state , choices ):
3018+ if state == 0 :
3019+ self .completion_matches = [c for c in choices if c .startswith (text )]
3020+
3021+ try :
3022+ return choices [state ]
3023+ except IndexError :
3024+ return None
3025+
29673026
29683027def _connect (host , port , frame ):
29693028 with closing (socket .create_connection ((host , port ))) as conn :
0 commit comments