blob: 47c4cbdcb8c3f9e2200b278a878ca78358331e9a [file] [log] [blame]
Terry Jan Reedy81bb97d2019-05-24 21:59:53 -04001""" idlelib.run
2
3Simplified, pyshell.ModifiedInterpreter spawns a subprocess with
4f'''{sys.executable} -c "__import__('idlelib.run').run.main()"'''
5'.run' is needed because __import__ returns idlelib, not idlelib.run.
6"""
Miss Islington (bot)5a5237c2021-05-07 17:35:25 -07007import contextlib
Tal Einatfcf1d002019-07-06 15:35:24 +03008import functools
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -04009import io
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +000010import linecache
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -040011import queue
12import sys
Tal Einatfcf1d002019-07-06 15:35:24 +030013import textwrap
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -040014import time
15import traceback
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040016import _thread as thread
17import threading
18import warnings
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000019
Ken7a343802021-01-27 07:55:52 +080020import idlelib # testing
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040021from idlelib import autocomplete # AutoComplete, fetch_encodings
Terry Jan Reedy06e20292018-06-19 23:00:35 -040022from idlelib import calltip # Calltip
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040023from idlelib import debugger_r # start_debugger
24from idlelib import debugobj_r # remote_object_tree_item
25from idlelib import iomenu # encoding
26from idlelib import rpc # multiple objects
27from idlelib import stackviewer # StackTreeItem
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000028import __main__
29
Terry Jan Reedy81bb97d2019-05-24 21:59:53 -040030import tkinter # Use tcl and, if startup fails, messagebox.
31if not hasattr(sys.modules['idlelib.run'], 'firstrun'):
32 # Undo modifications of tkinter by idlelib imports; see bpo-25507.
33 for mod in ('simpledialog', 'messagebox', 'font',
34 'dialog', 'filedialog', 'commondialog',
35 'ttk'):
36 delattr(tkinter, mod)
37 del sys.modules['tkinter.' + mod]
38 # Avoid AttributeError if run again; see bpo-37038.
39 sys.modules['idlelib.run'].firstrun = False
Terry Jan Reedyff1d5ab2016-07-16 18:26:32 -040040
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000041LOCALHOST = '127.0.0.1'
42
Miss Islington (bot)acd46fe2021-09-28 05:35:23 -070043try:
44 eof = 'Ctrl-D (end-of-file)'
45 exit.eof = eof
46 quit.eof = eof
47except NameError: # In case subprocess started with -S (maybe in future).
48 pass
49
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +000050
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -040051def idle_formatwarning(message, category, filename, lineno, line=None):
52 """Format warnings the IDLE way."""
53
54 s = "\nWarning (from warnings module):\n"
55 s += ' File \"%s\", line %s\n' % (filename, lineno)
56 if line is None:
57 line = linecache.getline(filename, lineno)
58 line = line.strip()
59 if line:
60 s += " %s\n" % line
61 s += "%s: %s\n" % (category.__name__, message)
62 return s
63
Terry Jan Reedy95a3f112013-06-28 23:50:12 -040064def idle_showwarning_subproc(
65 message, category, filename, lineno, file=None, line=None):
66 """Show Idle-format warning after replacing warnings.showwarning.
Andrew Svetlova2251aa2012-03-13 18:36:13 -070067
Terry Jan Reedy95a3f112013-06-28 23:50:12 -040068 The only difference is the formatter called.
69 """
70 if file is None:
71 file = sys.stderr
72 try:
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -040073 file.write(idle_formatwarning(
Terry Jan Reedy95a3f112013-06-28 23:50:12 -040074 message, category, filename, lineno, line))
Serhiy Storchaka55fe1ae2017-04-16 10:46:38 +030075 except OSError:
Terry Jan Reedy95a3f112013-06-28 23:50:12 -040076 pass # the file (probably stderr) is invalid - this warning gets lost.
77
78_warnings_showwarning = None
79
80def capture_warnings(capture):
81 "Replace warning.showwarning with idle_showwarning_subproc, or reverse."
82
83 global _warnings_showwarning
84 if capture:
85 if _warnings_showwarning is None:
86 _warnings_showwarning = warnings.showwarning
87 warnings.showwarning = idle_showwarning_subproc
88 else:
89 if _warnings_showwarning is not None:
90 warnings.showwarning = _warnings_showwarning
91 _warnings_showwarning = None
92
93capture_warnings(True)
Andrew Svetlov753445a2012-03-26 21:56:44 +030094tcl = tkinter.Tcl()
95
Andrew Svetlov753445a2012-03-26 21:56:44 +030096def handle_tk_events(tcl=tcl):
Andrew Svetlova2251aa2012-03-13 18:36:13 -070097 """Process any tk events that are ready to be dispatched if tkinter
98 has been imported, a tcl interpreter has been created and tk has been
99 loaded."""
Andrew Svetlov753445a2012-03-26 21:56:44 +0300100 tcl.eval("update")
Andrew Svetlova2251aa2012-03-13 18:36:13 -0700101
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000102# Thread shared globals: Establish a queue between a subthread (which handles
103# the socket) and the main thread (which runs user code), plus global
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000104# completion, exit and interruptable (the main thread) flags:
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000105
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000106exit_now = False
107quitting = False
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000108interruptable = False
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000109
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000110def main(del_exitfunc=False):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000111 """Start the Python execution server in a subprocess
112
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000113 In the Python subprocess, RPCServer is instantiated with handlerclass
114 MyHandler, which inherits register/unregister methods from RPCHandler via
115 the mix-in class SocketIO.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000116
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000117 When the RPCServer 'server' is instantiated, the TCPServer initialization
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000118 creates an instance of run.MyHandler and calls its handle() method.
119 handle() instantiates a run.Executive object, passing it a reference to the
120 MyHandler object. That reference is saved as attribute rpchandler of the
121 Executive instance. The Executive methods have access to the reference and
122 can pass it on to entities that they command
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400123 (e.g. debugger_r.Debugger.start_debugger()). The latter, in turn, can
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000124 call MyHandler(SocketIO) register/unregister methods via the reference to
125 register and unregister themselves.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000126
127 """
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000128 global exit_now
129 global quitting
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000130 global no_exitfunc
131 no_exitfunc = del_exitfunc
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000132 #time.sleep(15) # test subprocess not responding
Kurt B. Kaisere67842a2009-04-04 20:13:23 +0000133 try:
134 assert(len(sys.argv) > 1)
135 port = int(sys.argv[-1])
136 except:
Kurt B. Kaiser8fc98c32009-04-04 20:20:29 +0000137 print("IDLE Subprocess: no IP port passed in sys.argv.",
138 file=sys.__stderr__)
Kurt B. Kaisere67842a2009-04-04 20:13:23 +0000139 return
Terry Jan Reedy95a3f112013-06-28 23:50:12 -0400140
141 capture_warnings(True)
Chui Tey5d2af632002-05-26 13:36:41 +0000142 sys.argv[:] = [""]
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000143 sockthread = threading.Thread(target=manage_socket,
144 name='SockThread',
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +0000145 args=((LOCALHOST, port),))
Benjamin Peterson71088cc2008-09-19 21:49:37 +0000146 sockthread.daemon = True
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000147 sockthread.start()
148 while 1:
149 try:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000150 if exit_now:
151 try:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000152 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000153 except KeyboardInterrupt:
154 # exiting but got an extra KBI? Try again!
155 continue
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000156 try:
Terry Jan Reedy1e2fcac2017-12-04 16:16:18 -0500157 request = rpc.request_queue.get(block=True, timeout=0.05)
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000158 except queue.Empty:
Terry Jan Reedy1e2fcac2017-12-04 16:16:18 -0500159 request = None
160 # Issue 32207: calling handle_tk_events here adds spurious
161 # queue.Empty traceback to event handling exceptions.
162 if request:
163 seq, (method, args, kwargs) = request
164 ret = method(*args, **kwargs)
165 rpc.response_queue.put((seq, ret))
166 else:
Andrew Svetlova2251aa2012-03-13 18:36:13 -0700167 handle_tk_events()
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +0000168 except KeyboardInterrupt:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000169 if quitting:
170 exit_now = True
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +0000171 continue
Kurt B. Kaisera2792be2003-05-17 21:04:10 +0000172 except SystemExit:
Terry Jan Reedy95a3f112013-06-28 23:50:12 -0400173 capture_warnings(False)
Kurt B. Kaisera2792be2003-05-17 21:04:10 +0000174 raise
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000175 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000176 type, value, tb = sys.exc_info()
Kurt B. Kaisera2792be2003-05-17 21:04:10 +0000177 try:
178 print_exception()
179 rpc.response_queue.put((seq, None))
180 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000181 # Link didn't work, print same exception to __stderr__
182 traceback.print_exception(type, value, tb, file=sys.__stderr__)
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000183 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000184 else:
185 continue
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000186
187def manage_socket(address):
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000188 for i in range(3):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000189 time.sleep(i)
190 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000191 server = MyRPCServer(address, MyHandler)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000192 break
Andrew Svetlov0832af62012-12-18 23:10:48 +0200193 except OSError as err:
194 print("IDLE Subprocess: OSError: " + err.args[1] +
Georg Brandl6464d472007-10-22 16:16:13 +0000195 ", retrying....", file=sys.__stderr__)
Amaury Forgeot d'Arcefae8c42008-11-21 23:08:09 +0000196 socket_error = err
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000197 else:
Amaury Forgeot d'Arcefae8c42008-11-21 23:08:09 +0000198 print("IDLE Subprocess: Connection to "
199 "IDLE GUI failed, exiting.", file=sys.__stderr__)
200 show_socket_error(socket_error, address)
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000201 global exit_now
202 exit_now = True
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000203 return
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000204 server.handle_request() # A single request only
205
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000206def show_socket_error(err, address):
terryjreedy188aedf2017-06-13 21:32:16 -0400207 "Display socket error from manage_socket."
Georg Brandl14fc4272008-05-17 18:39:55 +0000208 import tkinter
terryjreedy188aedf2017-06-13 21:32:16 -0400209 from tkinter.messagebox import showerror
Georg Brandl14fc4272008-05-17 18:39:55 +0000210 root = tkinter.Tk()
Serhiy Storchakaa96c96f2017-09-21 11:20:06 +0300211 fix_scaling(root)
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000212 root.withdraw()
Terry Jan Reedy8fac1222019-06-17 17:23:28 -0400213 showerror(
214 "Subprocess Connection Error",
215 f"IDLE's subprocess can't connect to {address[0]}:{address[1]}.\n"
216 f"Fatal OSError #{err.errno}: {err.strerror}.\n"
217 "See the 'Startup failure' section of the IDLE doc, online at\n"
218 "https://docs.python.org/3/library/idle.html#startup-failure",
219 parent=root)
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000220 root.destroy()
221
Miss Islington (bot)5a5237c2021-05-07 17:35:25 -0700222
223def get_message_lines(typ, exc, tb):
224 "Return line composing the exception message."
225 if typ in (AttributeError, NameError):
226 # 3.10+ hints are not directly accessible from python (#44026).
227 err = io.StringIO()
228 with contextlib.redirect_stderr(err):
229 sys.__excepthook__(typ, exc, tb)
230 return [err.getvalue().split("\n")[-2] + "\n"]
231 else:
232 return traceback.format_exception_only(typ, exc)
233
234
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000235def print_exception():
Kurt B. Kaisere9802a32004-01-02 04:04:04 +0000236 import linecache
237 linecache.checkcache()
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000238 flush_stdout()
239 efile = sys.stderr
Kurt B. Kaiser924f6162003-11-19 04:52:32 +0000240 typ, val, tb = excinfo = sys.exc_info()
241 sys.last_type, sys.last_value, sys.last_traceback = excinfo
Serhiy Storchaka78470b42013-01-09 12:21:57 +0200242 seen = set()
243
244 def print_exc(typ, exc, tb):
Zane Bitterde860732017-10-17 17:29:39 -0400245 seen.add(id(exc))
Serhiy Storchaka78470b42013-01-09 12:21:57 +0200246 context = exc.__context__
247 cause = exc.__cause__
Zane Bitterde860732017-10-17 17:29:39 -0400248 if cause is not None and id(cause) not in seen:
Serhiy Storchaka78470b42013-01-09 12:21:57 +0200249 print_exc(type(cause), cause, cause.__traceback__)
250 print("\nThe above exception was the direct cause "
251 "of the following exception:\n", file=efile)
Serhiy Storchaka71317492013-01-09 12:24:48 +0200252 elif (context is not None and
253 not exc.__suppress_context__ and
Zane Bitterde860732017-10-17 17:29:39 -0400254 id(context) not in seen):
Serhiy Storchaka78470b42013-01-09 12:21:57 +0200255 print_exc(type(context), context, context.__traceback__)
256 print("\nDuring handling of the above exception, "
257 "another exception occurred:\n", file=efile)
258 if tb:
259 tbe = traceback.extract_tb(tb)
260 print('Traceback (most recent call last):', file=efile)
261 exclude = ("run.py", "rpc.py", "threading.py", "queue.py",
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400262 "debugger_r.py", "bdb.py")
Serhiy Storchaka78470b42013-01-09 12:21:57 +0200263 cleanup_traceback(tbe, exclude)
264 traceback.print_list(tbe, file=efile)
Miss Islington (bot)5a5237c2021-05-07 17:35:25 -0700265 lines = get_message_lines(typ, exc, tb)
Serhiy Storchaka78470b42013-01-09 12:21:57 +0200266 for line in lines:
267 print(line, end='', file=efile)
268
269 print_exc(typ, val, tb)
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000270
271def cleanup_traceback(tb, exclude):
272 "Remove excluded traces from beginning/end of tb; get cached lines"
273 orig_tb = tb[:]
274 while tb:
275 for rpcfile in exclude:
276 if tb[0][0].count(rpcfile):
277 break # found an exclude, break for: and delete tb[0]
278 else:
279 break # no excludes, have left RPC code, break while:
280 del tb[0]
281 while tb:
282 for rpcfile in exclude:
283 if tb[-1][0].count(rpcfile):
284 break
285 else:
286 break
287 del tb[-1]
288 if len(tb) == 0:
289 # exception was in IDLE internals, don't prune!
290 tb[:] = orig_tb[:]
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000291 print("** IDLE Internal Exception: ", file=sys.stderr)
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000292 rpchandler = rpc.objecttable['exec'].rpchandler
293 for i in range(len(tb)):
294 fn, ln, nm, line = tb[i]
295 if nm == '?':
296 nm = "-toplevel-"
297 if not line and fn.startswith("<pyshell#"):
298 line = rpchandler.remotecall('linecache', 'getline',
299 (fn, ln), {})
300 tb[i] = fn, ln, nm, line
301
302def flush_stdout():
Guido van Rossum79139b22007-02-09 23:20:19 +0000303 """XXX How to do this now?"""
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000304
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000305def exit():
Guido van Rossumc76a2502007-08-09 14:26:58 +0000306 """Exit subprocess, possibly after first clearing exit functions.
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000307
308 If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any
Guido van Rossumc76a2502007-08-09 14:26:58 +0000309 functions registered with atexit will be removed before exiting.
310 (VPython support)
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000311
312 """
313 if no_exitfunc:
Guido van Rossumc76a2502007-08-09 14:26:58 +0000314 import atexit
315 atexit._clear()
Terry Jan Reedy95a3f112013-06-28 23:50:12 -0400316 capture_warnings(False)
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000317 sys.exit(0)
Chui Tey5d2af632002-05-26 13:36:41 +0000318
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400319
Serhiy Storchakaa96c96f2017-09-21 11:20:06 +0300320def fix_scaling(root):
321 """Scale fonts on HiDPI displays."""
322 import tkinter.font
323 scaling = float(root.tk.call('tk', 'scaling'))
324 if scaling > 1.4:
325 for name in tkinter.font.names(root):
326 font = tkinter.font.Font(root=root, name=name, exists=True)
327 size = int(font['size'])
328 if size < 0:
329 font['size'] = round(-0.75*size)
330
331
Terry Jan Reedy6aeb2fe2019-07-09 14:37:25 -0400332def fixdoc(fun, text):
333 tem = (fun.__doc__ + '\n\n') if fun.__doc__ is not None else ''
334 fun.__doc__ = tem + textwrap.fill(textwrap.dedent(text))
335
Tal Einatfcf1d002019-07-06 15:35:24 +0300336RECURSIONLIMIT_DELTA = 30
Terry Jan Reedy6aeb2fe2019-07-09 14:37:25 -0400337
Tal Einatfcf1d002019-07-06 15:35:24 +0300338def install_recursionlimit_wrappers():
339 """Install wrappers to always add 30 to the recursion limit."""
340 # see: bpo-26806
341
342 @functools.wraps(sys.setrecursionlimit)
343 def setrecursionlimit(*args, **kwargs):
344 # mimic the original sys.setrecursionlimit()'s input handling
345 if kwargs:
346 raise TypeError(
347 "setrecursionlimit() takes no keyword arguments")
348 try:
349 limit, = args
350 except ValueError:
351 raise TypeError(f"setrecursionlimit() takes exactly one "
352 f"argument ({len(args)} given)")
353 if not limit > 0:
354 raise ValueError(
355 "recursion limit must be greater or equal than 1")
356
357 return setrecursionlimit.__wrapped__(limit + RECURSIONLIMIT_DELTA)
358
Terry Jan Reedy6aeb2fe2019-07-09 14:37:25 -0400359 fixdoc(setrecursionlimit, f"""\
360 This IDLE wrapper adds {RECURSIONLIMIT_DELTA} to prevent possible
361 uninterruptible loops.""")
Tal Einatfcf1d002019-07-06 15:35:24 +0300362
363 @functools.wraps(sys.getrecursionlimit)
364 def getrecursionlimit():
365 return getrecursionlimit.__wrapped__() - RECURSIONLIMIT_DELTA
366
Terry Jan Reedy6aeb2fe2019-07-09 14:37:25 -0400367 fixdoc(getrecursionlimit, f"""\
368 This IDLE wrapper subtracts {RECURSIONLIMIT_DELTA} to compensate
369 for the {RECURSIONLIMIT_DELTA} IDLE adds when setting the limit.""")
Tal Einatfcf1d002019-07-06 15:35:24 +0300370
371 # add the delta to the default recursion limit, to compensate
372 sys.setrecursionlimit(sys.getrecursionlimit() + RECURSIONLIMIT_DELTA)
373
374 sys.setrecursionlimit = setrecursionlimit
375 sys.getrecursionlimit = getrecursionlimit
376
377
378def uninstall_recursionlimit_wrappers():
379 """Uninstall the recursion limit wrappers from the sys module.
380
381 IDLE only uses this for tests. Users can import run and call
382 this to remove the wrapping.
383 """
384 if (
385 getattr(sys.setrecursionlimit, '__wrapped__', None) and
386 getattr(sys.getrecursionlimit, '__wrapped__', None)
387 ):
388 sys.setrecursionlimit = sys.setrecursionlimit.__wrapped__
389 sys.getrecursionlimit = sys.getrecursionlimit.__wrapped__
390 sys.setrecursionlimit(sys.getrecursionlimit() - RECURSIONLIMIT_DELTA)
391
392
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000393class MyRPCServer(rpc.RPCServer):
394
395 def handle_error(self, request, client_address):
396 """Override RPCServer method for IDLE
397
398 Interrupt the MainThread and exit server if link is dropped.
399
400 """
Kurt B. Kaisere9535112004-11-19 15:46:49 +0000401 global quitting
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000402 try:
403 raise
404 except SystemExit:
405 raise
406 except EOFError:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000407 global exit_now
408 exit_now = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000409 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000410 except:
411 erf = sys.__stderr__
Terry Jan Reedyf2e161c2020-08-09 16:08:30 -0400412 print(textwrap.dedent(f"""
413 {'-'*40}
414 Unhandled exception in user code execution server!'
415 Thread: {threading.current_thread().name}
416 IDLE Client Address: {client_address}
417 Request: {request!r}
418 """), file=erf)
419 traceback.print_exc(limit=-20, file=erf)
420 print(textwrap.dedent(f"""
421 *** Unrecoverable, server exiting!
422
423 Users should never see this message; it is likely transient.
424 If this recurs, report this with a copy of the message
425 and an explanation of how to make it repeat.
426 {'-'*40}"""), file=erf)
Kurt B. Kaisere9535112004-11-19 15:46:49 +0000427 quitting = True
428 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000429
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400430
431# Pseudofiles for shell-remote communication (also used in pyshell)
432
Serhiy Storchakab690a272019-10-08 14:32:25 +0300433class StdioFile(io.TextIOBase):
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400434
Serhiy Storchakab690a272019-10-08 14:32:25 +0300435 def __init__(self, shell, tags, encoding='utf-8', errors='strict'):
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400436 self.shell = shell
437 self.tags = tags
438 self._encoding = encoding
Serhiy Storchakab690a272019-10-08 14:32:25 +0300439 self._errors = errors
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400440
441 @property
442 def encoding(self):
443 return self._encoding
444
445 @property
Serhiy Storchakab690a272019-10-08 14:32:25 +0300446 def errors(self):
447 return self._errors
448
449 @property
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400450 def name(self):
451 return '<%s>' % self.tags
452
453 def isatty(self):
454 return True
455
456
Serhiy Storchakab690a272019-10-08 14:32:25 +0300457class StdOutputFile(StdioFile):
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400458
459 def writable(self):
460 return True
461
462 def write(self, s):
463 if self.closed:
464 raise ValueError("write to closed file")
Serhiy Storchakab690a272019-10-08 14:32:25 +0300465 s = str.encode(s, self.encoding, self.errors).decode(self.encoding, self.errors)
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400466 return self.shell.write(s, self.tags)
467
468
Serhiy Storchakab690a272019-10-08 14:32:25 +0300469class StdInputFile(StdioFile):
470 _line_buffer = ''
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400471
472 def readable(self):
473 return True
474
475 def read(self, size=-1):
476 if self.closed:
477 raise ValueError("read from closed file")
478 if size is None:
479 size = -1
480 elif not isinstance(size, int):
481 raise TypeError('must be int, not ' + type(size).__name__)
482 result = self._line_buffer
483 self._line_buffer = ''
484 if size < 0:
485 while True:
486 line = self.shell.readline()
487 if not line: break
488 result += line
489 else:
490 while len(result) < size:
491 line = self.shell.readline()
492 if not line: break
493 result += line
494 self._line_buffer = result[size:]
495 result = result[:size]
496 return result
497
498 def readline(self, size=-1):
499 if self.closed:
500 raise ValueError("read from closed file")
501 if size is None:
502 size = -1
503 elif not isinstance(size, int):
504 raise TypeError('must be int, not ' + type(size).__name__)
505 line = self._line_buffer or self.shell.readline()
506 if size < 0:
507 size = len(line)
508 eol = line.find('\n', 0, size)
509 if eol >= 0:
510 size = eol + 1
511 self._line_buffer = line[size:]
512 return line[:size]
513
514 def close(self):
515 self.shell.close()
516
517
Chui Tey5d2af632002-05-26 13:36:41 +0000518class MyHandler(rpc.RPCHandler):
519
520 def handle(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000521 """Override base method"""
Chui Tey5d2af632002-05-26 13:36:41 +0000522 executive = Executive(self)
523 self.register("exec", executive)
Serhiy Storchaka39e70a42013-01-25 15:30:58 +0200524 self.console = self.get_remote_proxy("console")
Serhiy Storchakab690a272019-10-08 14:32:25 +0300525 sys.stdin = StdInputFile(self.console, "stdin",
526 iomenu.encoding, iomenu.errors)
527 sys.stdout = StdOutputFile(self.console, "stdout",
528 iomenu.encoding, iomenu.errors)
529 sys.stderr = StdOutputFile(self.console, "stderr",
530 iomenu.encoding, "backslashreplace")
Serhiy Storchaka39e70a42013-01-25 15:30:58 +0200531
Andrew Svetlovcd49d532012-03-25 11:43:02 +0300532 sys.displayhook = rpc.displayhook
Kurt B. Kaiserf609a342007-12-28 03:57:56 +0000533 # page help() text to shell.
534 import pydoc # import must be done here to capture i/o binding
535 pydoc.pager = pydoc.plainpager
Benjamin Peterson0d4931e2013-05-11 22:24:28 -0500536
537 # Keep a reference to stdin so that it won't try to exit IDLE if
538 # sys.stdin gets changed from within IDLE's shell. See issue17838.
539 self._keep_stdin = sys.stdin
540
Tal Einatfcf1d002019-07-06 15:35:24 +0300541 install_recursionlimit_wrappers()
542
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000543 self.interp = self.get_remote_proxy("interp")
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000544 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
545
546 def exithook(self):
547 "override SocketIO method - wait for MainThread to shut us down"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000548 time.sleep(10)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000549
550 def EOFhook(self):
551 "Override SocketIO method - terminate wait on callback and exit thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000552 global quitting
553 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000554 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000555
556 def decode_interrupthook(self):
557 "interrupt awakened thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000558 global quitting
559 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000560 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000561
Chui Tey5d2af632002-05-26 13:36:41 +0000562
Terry Jan Reedy8dfe1562021-01-24 14:08:50 -0500563class Executive:
Chui Tey5d2af632002-05-26 13:36:41 +0000564
565 def __init__(self, rpchandler):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000566 self.rpchandler = rpchandler
Ken7a343802021-01-27 07:55:52 +0800567 if idlelib.testing is False:
568 self.locals = __main__.__dict__
569 self.calltip = calltip.Calltip()
570 self.autocomplete = autocomplete.AutoComplete()
571 else:
572 self.locals = {}
Chui Tey5d2af632002-05-26 13:36:41 +0000573
574 def runcode(self, code):
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000575 global interruptable
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000576 try:
Ken7a343802021-01-27 07:55:52 +0800577 self.user_exc_info = None
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000578 interruptable = True
579 try:
580 exec(code, self.locals)
581 finally:
582 interruptable = False
Terry Jan Reedy6d965b32019-05-19 22:52:22 -0400583 except SystemExit as e:
584 if e.args: # SystemExit called with an argument.
585 ob = e.args[0]
586 if not isinstance(ob, (type(None), int)):
587 print('SystemExit: ' + str(ob), file=sys.stderr)
588 # Return to the interactive prompt.
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000589 except:
Ken7a343802021-01-27 07:55:52 +0800590 self.user_exc_info = sys.exc_info() # For testing, hook, viewer.
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000591 if quitting:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000592 exit()
Ken7a343802021-01-27 07:55:52 +0800593 if sys.excepthook is sys.__excepthook__:
594 print_exception()
595 else:
596 try:
597 sys.excepthook(*self.user_exc_info)
598 except:
599 self.user_exc_info = sys.exc_info() # For testing.
600 print_exception()
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000601 jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
602 if jit:
603 self.rpchandler.interp.open_remote_stack_viewer()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000604 else:
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000605 flush_stdout()
Chui Tey5d2af632002-05-26 13:36:41 +0000606
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000607 def interrupt_the_server(self):
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000608 if interruptable:
609 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000610
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000611 def start_the_debugger(self, gui_adap_oid):
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400612 return debugger_r.start_debugger(self.rpchandler, gui_adap_oid)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000613
614 def stop_the_debugger(self, idb_adap_oid):
615 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
616 self.rpchandler.unregister(idb_adap_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000617
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000618 def get_the_calltip(self, name):
619 return self.calltip.fetch_tip(name)
620
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000621 def get_the_completion_list(self, what, mode):
622 return self.autocomplete.fetch_completions(what, mode)
623
Chui Tey5d2af632002-05-26 13:36:41 +0000624 def stackviewer(self, flist_oid=None):
Ken7a343802021-01-27 07:55:52 +0800625 if self.user_exc_info:
626 typ, val, tb = self.user_exc_info
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000627 else:
Chui Tey5d2af632002-05-26 13:36:41 +0000628 return None
629 flist = None
630 if flist_oid is not None:
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000631 flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000632 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
633 tb = tb.tb_next
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000634 sys.last_type = typ
635 sys.last_value = val
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400636 item = stackviewer.StackTreeItem(flist, tb)
637 return debugobj_r.remote_object_tree_item(item)
Terry Jan Reedy95a3f112013-06-28 23:50:12 -0400638
Terry Jan Reedy81bb97d2019-05-24 21:59:53 -0400639
640if __name__ == '__main__':
641 from unittest import main
642 main('idlelib.idle_test.test_run', verbosity=2)
643
644capture_warnings(False) # Make sure turned off; see bpo-18081.