11# -*- coding: utf-8 -*-
22#
3- # Copyright (C) 2009-2010, 2013, 2015, 2017, 2020, 2023-2025
3+ # Copyright (C) 2009-2010, 2013, 2015, 2017, 2020, 2023-2026
44# Rocky Bernstein
55#
66# This program is free software: you can redistribute it and/or modify
1717# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818import code
1919import sys
20+ from typing import Callable
2021
2122from trepan .interfaces .server import ServerInterface
2223
2324# Our local modules
2425from trepan .processor .command .base_cmd import DebuggerCommand
2526
27+ use_pyrepl = False
28+ try :
29+ from _pyrepl .console import InteractiveColoredConsole
30+ from _pyrepl .unix_console import InvalidTerminal
31+ from _pyrepl .simple_interact import run_multiline_interactive_console
32+
33+ import os
34+
35+ if os .name == "nt" :
36+ from _pyrepl .windows_console import WindowsConsole as Console
37+ else :
38+ from _pyrepl .unix_console import UnixConsole as Console
39+ use_pyrepl = True
40+
41+ except ImportError :
42+ use_pyrepl = False
43+
44+
2645
2746class PythonCommand (DebuggerCommand ):
2847 """**python** [**-d**]
2948
3049 Run Python as a command subshell. The *sys.ps1* prompt will be set to
31- `trepan3 >>> `.
50+ `trepan3k >>> `.
51+
52+ If running Python 3.13 or it is possible to use pyrepl, this will be used,
53+ providing its colorization and language completion facilities.
3254
3355 If *-d* is passed, you can access debugger state via local variable *debugger*.
3456
@@ -72,7 +94,7 @@ def run(self, args):
7294 pass
7395 pass
7496
75- banner_tmpl = """trepan3 python shell%s
97+ banner_tmpl = """trepan3k python shell%s
7698Use dbgr(*string*) to issue debugger command: *string*"""
7799
78100 debug = len (args ) > 1 and args [1 ] == "-d"
@@ -109,12 +131,14 @@ def run(self, args):
109131 sys .excepthook = None
110132 if len (my_locals ):
111133 interact (
134+ msg_func = self .msg ,
135+ errmsg_func = self .errmsg ,
112136 banner = (banner_tmpl % " with locals" ),
113137 my_locals = my_locals ,
114138 my_globals = my_globals ,
115139 )
116140 else :
117- interact (banner = ( banner_tmpl % "" ) )
141+ interact (errmsg_func = self . errmsg , banner = banner_tmpl % "" )
118142 pass
119143 finally :
120144 sys .excepthook = old_sys_excepthook
@@ -124,6 +148,7 @@ def run(self, args):
124148 if hasattr (interface , "complete" ) and interface .complete is not None :
125149 try :
126150 from readline import parse_and_bind , set_completer
151+
127152 parse_and_bind ("tab: complete" )
128153 set_completer (proc .intf [- 1 ].complete )
129154 except ImportError :
@@ -138,11 +163,20 @@ def run(self, args):
138163 pass
139164
140165
141- # Monkey-patched from code.py
142- # FIXME: get changes into Python.
143- def interact (banner = None , readfunc = None , my_locals = None , my_globals = None ):
144- """Almost a copy of ``code.interact``.
145- Closely emulate the interactive Python interpreter.
166+ pyrepl_console = None
167+
168+
169+ # Modified from code.py
170+ def interact (
171+ msg_func : Callable ,
172+ errmsg_func : Callable ,
173+ banner = None ,
174+ readfunc = None ,
175+ my_locals = None ,
176+ my_globals = None ,
177+ ):
178+ """
179+ Emulate the interactive Python interpreter. This is Similar to ``code.interact``.
146180
147181 This is a backwards compatible interface to the InteractiveConsole
148182 class. When readfunc is not specified, it attempts to import the
@@ -157,19 +191,44 @@ def interact(banner=None, readfunc=None, my_locals=None, my_globals=None):
157191 """
158192
159193 def console_runcode (code_obj ):
160- runcode (console , code_obj )
161-
162- console = code .InteractiveConsole (my_locals , filename = "<trepan>" )
163- console .runcode = console_runcode
164- setattr (console , "globals" , my_globals )
165- if readfunc is not None :
166- console .raw_input = readfunc
167- console .interact (banner )
168- pass
194+ runcode (pyrepl_console , code_obj )
195+
196+ global pyrepl_console
197+ global use_pyrepl
198+
199+ if use_pyrepl :
200+ if not pyrepl_console :
201+ pyrepl_console = InteractiveColoredConsole (my_locals , filename = "<trepan>" )
169202
203+ pyrepl_console .runcode = console_runcode
204+ setattr (pyrepl_console , "globals" , my_globals )
205+ if readfunc is not None :
206+ pyrepl_console .raw_input = readfunc
170207
171- # Also monkey-patched from code.py
172- # FIXME: get changes into Python.
208+
209+ try :
210+ Console (sys .stdin , sys .stdout )
211+ except InvalidTerminal :
212+ use_pyrepl = None
213+ errmsg_func (
214+ "Colored PyREPL can't be used here; using standard Python shell"
215+ )
216+
217+ msg_func (banner )
218+ run_multiline_interactive_console (pyrepl_console )
219+
220+ # Fancy color pyrepl console can't be used, so use InteractiveConsole.
221+ if not pyrepl_console :
222+ pyrepl_console = code .InteractiveConsole (my_locals , filename = "<trepan>" )
223+ pyrepl_console .runcode = console_runcode
224+ setattr (pyrepl_console , "globals" , my_globals )
225+ if readfunc is not None :
226+ pyrepl_console .raw_input = readfunc
227+
228+ pyrepl_console .interact (banner )
229+ return
230+
231+ # Monkey-patched from code.py
173232def runcode (obj , code_obj ):
174233 """Execute a code object.
175234
@@ -190,8 +249,7 @@ def runcode(obj, code_obj):
190249 except SystemExit :
191250 raise
192251 except Exception :
193- info = sys .exc_info ()
194- print (f"{ info [0 ]} ; { info [1 ]} " )
252+ obj .showtraceback ()
195253 else :
196254 pass
197255 return
0 commit comments