blob: 07e9a2bf9ceeae1d356e404c58c2247cd43a688f [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"""
Tal Einatfcf1d002019-07-06 15:35:24 +03007import functools
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -04008import io
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +00009import linecache
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -040010import queue
11import sys
Tal Einatfcf1d002019-07-06 15:35:24 +030012import textwrap
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -040013import time
14import traceback
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040015import _thread as thread
16import threading
17import warnings
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000018
Ken7a343802021-01-27 07:55:52 +080019import idlelib # testing
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040020from idlelib import autocomplete # AutoComplete, fetch_encodings
Terry Jan Reedy06e20292018-06-19 23:00:35 -040021from idlelib import calltip # Calltip
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040022from idlelib import debugger_r # start_debugger
23from idlelib import debugobj_r # remote_object_tree_item
24from idlelib import iomenu # encoding
25from idlelib import rpc # multiple objects
26from idlelib import stackviewer # StackTreeItem
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +000027import __main__
28
Terry Jan Reedy81bb97d2019-05-24 21:59:53 -040029import tkinter # Use tcl and, if startup fails, messagebox.
30if not hasattr(sys.modules['idlelib.run'], 'firstrun'):
31 # Undo modifications of tkinter by idlelib imports; see bpo-25507.
32 for mod in ('simpledialog', 'messagebox', 'font',
33 'dialog', 'filedialog', 'commondialog',
34 'ttk'):
35 delattr(tkinter, mod)
36 del sys.modules['tkinter.' + mod]
37 # Avoid AttributeError if run again; see bpo-37038.
38 sys.modules['idlelib.run'].firstrun = False
Terry Jan Reedyff1d5ab2016-07-16 18:26:32 -040039
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +000040LOCALHOST = '127.0.0.1'
41
Kurt B. Kaiser49a5fe12004-07-04 01:25:56 +000042
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -040043def idle_formatwarning(message, category, filename, lineno, line=None):
44 """Format warnings the IDLE way."""
45
46 s = "\nWarning (from warnings module):\n"
47 s += ' File \"%s\", line %s\n' % (filename, lineno)
48 if line is None:
49 line = linecache.getline(filename, lineno)
50 line = line.strip()
51 if line:
52 s += " %s\n" % line
53 s += "%s: %s\n" % (category.__name__, message)
54 return s
55
Terry Jan Reedy95a3f112013-06-28 23:50:12 -040056def idle_showwarning_subproc(
57 message, category, filename, lineno, file=None, line=None):
58 """Show Idle-format warning after replacing warnings.showwarning.
Andrew Svetlova2251aa2012-03-13 18:36:13 -070059
Terry Jan Reedy95a3f112013-06-28 23:50:12 -040060 The only difference is the formatter called.
61 """
62 if file is None:
63 file = sys.stderr
64 try:
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -040065 file.write(idle_formatwarning(
Terry Jan Reedy95a3f112013-06-28 23:50:12 -040066 message, category, filename, lineno, line))
Serhiy Storchaka55fe1ae2017-04-16 10:46:38 +030067 except OSError:
Terry Jan Reedy95a3f112013-06-28 23:50:12 -040068 pass # the file (probably stderr) is invalid - this warning gets lost.
69
70_warnings_showwarning = None
71
72def capture_warnings(capture):
73 "Replace warning.showwarning with idle_showwarning_subproc, or reverse."
74
75 global _warnings_showwarning
76 if capture:
77 if _warnings_showwarning is None:
78 _warnings_showwarning = warnings.showwarning
79 warnings.showwarning = idle_showwarning_subproc
80 else:
81 if _warnings_showwarning is not None:
82 warnings.showwarning = _warnings_showwarning
83 _warnings_showwarning = None
84
85capture_warnings(True)
Andrew Svetlov753445a2012-03-26 21:56:44 +030086tcl = tkinter.Tcl()
87
Andrew Svetlov753445a2012-03-26 21:56:44 +030088def handle_tk_events(tcl=tcl):
Andrew Svetlova2251aa2012-03-13 18:36:13 -070089 """Process any tk events that are ready to be dispatched if tkinter
90 has been imported, a tcl interpreter has been created and tk has been
91 loaded."""
Andrew Svetlov753445a2012-03-26 21:56:44 +030092 tcl.eval("update")
Andrew Svetlova2251aa2012-03-13 18:36:13 -070093
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000094# Thread shared globals: Establish a queue between a subthread (which handles
95# the socket) and the main thread (which runs user code), plus global
Guido van Rossum8ce8a782007-11-01 19:42:39 +000096# completion, exit and interruptable (the main thread) flags:
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +000097
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +000098exit_now = False
99quitting = False
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000100interruptable = False
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000101
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000102def main(del_exitfunc=False):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000103 """Start the Python execution server in a subprocess
104
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000105 In the Python subprocess, RPCServer is instantiated with handlerclass
106 MyHandler, which inherits register/unregister methods from RPCHandler via
107 the mix-in class SocketIO.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000108
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000109 When the RPCServer 'server' is instantiated, the TCPServer initialization
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000110 creates an instance of run.MyHandler and calls its handle() method.
111 handle() instantiates a run.Executive object, passing it a reference to the
112 MyHandler object. That reference is saved as attribute rpchandler of the
113 Executive instance. The Executive methods have access to the reference and
114 can pass it on to entities that they command
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400115 (e.g. debugger_r.Debugger.start_debugger()). The latter, in turn, can
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000116 call MyHandler(SocketIO) register/unregister methods via the reference to
117 register and unregister themselves.
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000118
119 """
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000120 global exit_now
121 global quitting
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000122 global no_exitfunc
123 no_exitfunc = del_exitfunc
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000124 #time.sleep(15) # test subprocess not responding
Kurt B. Kaisere67842a2009-04-04 20:13:23 +0000125 try:
126 assert(len(sys.argv) > 1)
127 port = int(sys.argv[-1])
128 except:
Kurt B. Kaiser8fc98c32009-04-04 20:20:29 +0000129 print("IDLE Subprocess: no IP port passed in sys.argv.",
130 file=sys.__stderr__)
Kurt B. Kaisere67842a2009-04-04 20:13:23 +0000131 return
Terry Jan Reedy95a3f112013-06-28 23:50:12 -0400132
133 capture_warnings(True)
Chui Tey5d2af632002-05-26 13:36:41 +0000134 sys.argv[:] = [""]
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000135 sockthread = threading.Thread(target=manage_socket,
136 name='SockThread',
Kurt B. Kaiser24d7e0c2003-06-05 23:51:29 +0000137 args=((LOCALHOST, port),))
Benjamin Peterson71088cc2008-09-19 21:49:37 +0000138 sockthread.daemon = True
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000139 sockthread.start()
140 while 1:
141 try:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000142 if exit_now:
143 try:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000144 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000145 except KeyboardInterrupt:
146 # exiting but got an extra KBI? Try again!
147 continue
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000148 try:
Terry Jan Reedy1e2fcac2017-12-04 16:16:18 -0500149 request = rpc.request_queue.get(block=True, timeout=0.05)
Alexandre Vassalottif260e442008-05-11 19:59:59 +0000150 except queue.Empty:
Terry Jan Reedy1e2fcac2017-12-04 16:16:18 -0500151 request = None
152 # Issue 32207: calling handle_tk_events here adds spurious
153 # queue.Empty traceback to event handling exceptions.
154 if request:
155 seq, (method, args, kwargs) = request
156 ret = method(*args, **kwargs)
157 rpc.response_queue.put((seq, ret))
158 else:
Andrew Svetlova2251aa2012-03-13 18:36:13 -0700159 handle_tk_events()
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +0000160 except KeyboardInterrupt:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000161 if quitting:
162 exit_now = True
Kurt B. Kaiseraa6b8562003-05-14 18:15:40 +0000163 continue
Kurt B. Kaisera2792be2003-05-17 21:04:10 +0000164 except SystemExit:
Terry Jan Reedy95a3f112013-06-28 23:50:12 -0400165 capture_warnings(False)
Kurt B. Kaisera2792be2003-05-17 21:04:10 +0000166 raise
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000167 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000168 type, value, tb = sys.exc_info()
Kurt B. Kaisera2792be2003-05-17 21:04:10 +0000169 try:
170 print_exception()
171 rpc.response_queue.put((seq, None))
172 except:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000173 # Link didn't work, print same exception to __stderr__
174 traceback.print_exception(type, value, tb, file=sys.__stderr__)
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000175 exit()
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000176 else:
177 continue
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000178
179def manage_socket(address):
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000180 for i in range(3):
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000181 time.sleep(i)
182 try:
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000183 server = MyRPCServer(address, MyHandler)
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000184 break
Andrew Svetlov0832af62012-12-18 23:10:48 +0200185 except OSError as err:
186 print("IDLE Subprocess: OSError: " + err.args[1] +
Georg Brandl6464d472007-10-22 16:16:13 +0000187 ", retrying....", file=sys.__stderr__)
Amaury Forgeot d'Arcefae8c42008-11-21 23:08:09 +0000188 socket_error = err
Kurt B. Kaiserb4179362002-07-26 00:06:42 +0000189 else:
Amaury Forgeot d'Arcefae8c42008-11-21 23:08:09 +0000190 print("IDLE Subprocess: Connection to "
191 "IDLE GUI failed, exiting.", file=sys.__stderr__)
192 show_socket_error(socket_error, address)
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000193 global exit_now
194 exit_now = True
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000195 return
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000196 server.handle_request() # A single request only
197
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000198def show_socket_error(err, address):
terryjreedy188aedf2017-06-13 21:32:16 -0400199 "Display socket error from manage_socket."
Georg Brandl14fc4272008-05-17 18:39:55 +0000200 import tkinter
terryjreedy188aedf2017-06-13 21:32:16 -0400201 from tkinter.messagebox import showerror
Georg Brandl14fc4272008-05-17 18:39:55 +0000202 root = tkinter.Tk()
Serhiy Storchakaa96c96f2017-09-21 11:20:06 +0300203 fix_scaling(root)
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000204 root.withdraw()
Terry Jan Reedy8fac1222019-06-17 17:23:28 -0400205 showerror(
206 "Subprocess Connection Error",
207 f"IDLE's subprocess can't connect to {address[0]}:{address[1]}.\n"
208 f"Fatal OSError #{err.errno}: {err.strerror}.\n"
209 "See the 'Startup failure' section of the IDLE doc, online at\n"
210 "https://docs.python.org/3/library/idle.html#startup-failure",
211 parent=root)
Kurt B. Kaiseraf3eb872004-01-21 18:54:30 +0000212 root.destroy()
213
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000214def print_exception():
Kurt B. Kaisere9802a32004-01-02 04:04:04 +0000215 import linecache
216 linecache.checkcache()
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000217 flush_stdout()
218 efile = sys.stderr
Kurt B. Kaiser924f6162003-11-19 04:52:32 +0000219 typ, val, tb = excinfo = sys.exc_info()
220 sys.last_type, sys.last_value, sys.last_traceback = excinfo
Serhiy Storchaka78470b42013-01-09 12:21:57 +0200221 seen = set()
222
223 def print_exc(typ, exc, tb):
Zane Bitterde860732017-10-17 17:29:39 -0400224 seen.add(id(exc))
Serhiy Storchaka78470b42013-01-09 12:21:57 +0200225 context = exc.__context__
226 cause = exc.__cause__
Zane Bitterde860732017-10-17 17:29:39 -0400227 if cause is not None and id(cause) not in seen:
Serhiy Storchaka78470b42013-01-09 12:21:57 +0200228 print_exc(type(cause), cause, cause.__traceback__)
229 print("\nThe above exception was the direct cause "
230 "of the following exception:\n", file=efile)
Serhiy Storchaka71317492013-01-09 12:24:48 +0200231 elif (context is not None and
232 not exc.__suppress_context__ and
Zane Bitterde860732017-10-17 17:29:39 -0400233 id(context) not in seen):
Serhiy Storchaka78470b42013-01-09 12:21:57 +0200234 print_exc(type(context), context, context.__traceback__)
235 print("\nDuring handling of the above exception, "
236 "another exception occurred:\n", file=efile)
237 if tb:
238 tbe = traceback.extract_tb(tb)
239 print('Traceback (most recent call last):', file=efile)
240 exclude = ("run.py", "rpc.py", "threading.py", "queue.py",
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400241 "debugger_r.py", "bdb.py")
Serhiy Storchaka78470b42013-01-09 12:21:57 +0200242 cleanup_traceback(tbe, exclude)
243 traceback.print_list(tbe, file=efile)
244 lines = traceback.format_exception_only(typ, exc)
245 for line in lines:
246 print(line, end='', file=efile)
247
248 print_exc(typ, val, tb)
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000249
250def cleanup_traceback(tb, exclude):
251 "Remove excluded traces from beginning/end of tb; get cached lines"
252 orig_tb = tb[:]
253 while tb:
254 for rpcfile in exclude:
255 if tb[0][0].count(rpcfile):
256 break # found an exclude, break for: and delete tb[0]
257 else:
258 break # no excludes, have left RPC code, break while:
259 del tb[0]
260 while tb:
261 for rpcfile in exclude:
262 if tb[-1][0].count(rpcfile):
263 break
264 else:
265 break
266 del tb[-1]
267 if len(tb) == 0:
268 # exception was in IDLE internals, don't prune!
269 tb[:] = orig_tb[:]
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000270 print("** IDLE Internal Exception: ", file=sys.stderr)
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000271 rpchandler = rpc.objecttable['exec'].rpchandler
272 for i in range(len(tb)):
273 fn, ln, nm, line = tb[i]
274 if nm == '?':
275 nm = "-toplevel-"
276 if not line and fn.startswith("<pyshell#"):
277 line = rpchandler.remotecall('linecache', 'getline',
278 (fn, ln), {})
279 tb[i] = fn, ln, nm, line
280
281def flush_stdout():
Guido van Rossum79139b22007-02-09 23:20:19 +0000282 """XXX How to do this now?"""
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000283
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000284def exit():
Guido van Rossumc76a2502007-08-09 14:26:58 +0000285 """Exit subprocess, possibly after first clearing exit functions.
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000286
287 If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any
Guido van Rossumc76a2502007-08-09 14:26:58 +0000288 functions registered with atexit will be removed before exiting.
289 (VPython support)
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000290
291 """
292 if no_exitfunc:
Guido van Rossumc76a2502007-08-09 14:26:58 +0000293 import atexit
294 atexit._clear()
Terry Jan Reedy95a3f112013-06-28 23:50:12 -0400295 capture_warnings(False)
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000296 sys.exit(0)
Chui Tey5d2af632002-05-26 13:36:41 +0000297
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -0400298
Serhiy Storchakaa96c96f2017-09-21 11:20:06 +0300299def fix_scaling(root):
300 """Scale fonts on HiDPI displays."""
301 import tkinter.font
302 scaling = float(root.tk.call('tk', 'scaling'))
303 if scaling > 1.4:
304 for name in tkinter.font.names(root):
305 font = tkinter.font.Font(root=root, name=name, exists=True)
306 size = int(font['size'])
307 if size < 0:
308 font['size'] = round(-0.75*size)
309
310
Terry Jan Reedy6aeb2fe2019-07-09 14:37:25 -0400311def fixdoc(fun, text):
312 tem = (fun.__doc__ + '\n\n') if fun.__doc__ is not None else ''
313 fun.__doc__ = tem + textwrap.fill(textwrap.dedent(text))
314
Tal Einatfcf1d002019-07-06 15:35:24 +0300315RECURSIONLIMIT_DELTA = 30
Terry Jan Reedy6aeb2fe2019-07-09 14:37:25 -0400316
Tal Einatfcf1d002019-07-06 15:35:24 +0300317def install_recursionlimit_wrappers():
318 """Install wrappers to always add 30 to the recursion limit."""
319 # see: bpo-26806
320
321 @functools.wraps(sys.setrecursionlimit)
322 def setrecursionlimit(*args, **kwargs):
323 # mimic the original sys.setrecursionlimit()'s input handling
324 if kwargs:
325 raise TypeError(
326 "setrecursionlimit() takes no keyword arguments")
327 try:
328 limit, = args
329 except ValueError:
330 raise TypeError(f"setrecursionlimit() takes exactly one "
331 f"argument ({len(args)} given)")
332 if not limit > 0:
333 raise ValueError(
334 "recursion limit must be greater or equal than 1")
335
336 return setrecursionlimit.__wrapped__(limit + RECURSIONLIMIT_DELTA)
337
Terry Jan Reedy6aeb2fe2019-07-09 14:37:25 -0400338 fixdoc(setrecursionlimit, f"""\
339 This IDLE wrapper adds {RECURSIONLIMIT_DELTA} to prevent possible
340 uninterruptible loops.""")
Tal Einatfcf1d002019-07-06 15:35:24 +0300341
342 @functools.wraps(sys.getrecursionlimit)
343 def getrecursionlimit():
344 return getrecursionlimit.__wrapped__() - RECURSIONLIMIT_DELTA
345
Terry Jan Reedy6aeb2fe2019-07-09 14:37:25 -0400346 fixdoc(getrecursionlimit, f"""\
347 This IDLE wrapper subtracts {RECURSIONLIMIT_DELTA} to compensate
348 for the {RECURSIONLIMIT_DELTA} IDLE adds when setting the limit.""")
Tal Einatfcf1d002019-07-06 15:35:24 +0300349
350 # add the delta to the default recursion limit, to compensate
351 sys.setrecursionlimit(sys.getrecursionlimit() + RECURSIONLIMIT_DELTA)
352
353 sys.setrecursionlimit = setrecursionlimit
354 sys.getrecursionlimit = getrecursionlimit
355
356
357def uninstall_recursionlimit_wrappers():
358 """Uninstall the recursion limit wrappers from the sys module.
359
360 IDLE only uses this for tests. Users can import run and call
361 this to remove the wrapping.
362 """
363 if (
364 getattr(sys.setrecursionlimit, '__wrapped__', None) and
365 getattr(sys.getrecursionlimit, '__wrapped__', None)
366 ):
367 sys.setrecursionlimit = sys.setrecursionlimit.__wrapped__
368 sys.getrecursionlimit = sys.getrecursionlimit.__wrapped__
369 sys.setrecursionlimit(sys.getrecursionlimit() - RECURSIONLIMIT_DELTA)
370
371
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000372class MyRPCServer(rpc.RPCServer):
373
374 def handle_error(self, request, client_address):
375 """Override RPCServer method for IDLE
376
377 Interrupt the MainThread and exit server if link is dropped.
378
379 """
Kurt B. Kaisere9535112004-11-19 15:46:49 +0000380 global quitting
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000381 try:
382 raise
383 except SystemExit:
384 raise
385 except EOFError:
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000386 global exit_now
387 exit_now = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000388 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000389 except:
390 erf = sys.__stderr__
Terry Jan Reedyf2e161c2020-08-09 16:08:30 -0400391 print(textwrap.dedent(f"""
392 {'-'*40}
393 Unhandled exception in user code execution server!'
394 Thread: {threading.current_thread().name}
395 IDLE Client Address: {client_address}
396 Request: {request!r}
397 """), file=erf)
398 traceback.print_exc(limit=-20, file=erf)
399 print(textwrap.dedent(f"""
400 *** Unrecoverable, server exiting!
401
402 Users should never see this message; it is likely transient.
403 If this recurs, report this with a copy of the message
404 and an explanation of how to make it repeat.
405 {'-'*40}"""), file=erf)
Kurt B. Kaisere9535112004-11-19 15:46:49 +0000406 quitting = True
407 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000408
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400409
410# Pseudofiles for shell-remote communication (also used in pyshell)
411
Serhiy Storchakab690a272019-10-08 14:32:25 +0300412class StdioFile(io.TextIOBase):
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400413
Serhiy Storchakab690a272019-10-08 14:32:25 +0300414 def __init__(self, shell, tags, encoding='utf-8', errors='strict'):
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400415 self.shell = shell
416 self.tags = tags
417 self._encoding = encoding
Serhiy Storchakab690a272019-10-08 14:32:25 +0300418 self._errors = errors
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400419
420 @property
421 def encoding(self):
422 return self._encoding
423
424 @property
Serhiy Storchakab690a272019-10-08 14:32:25 +0300425 def errors(self):
426 return self._errors
427
428 @property
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400429 def name(self):
430 return '<%s>' % self.tags
431
432 def isatty(self):
433 return True
434
435
Serhiy Storchakab690a272019-10-08 14:32:25 +0300436class StdOutputFile(StdioFile):
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400437
438 def writable(self):
439 return True
440
441 def write(self, s):
442 if self.closed:
443 raise ValueError("write to closed file")
Serhiy Storchakab690a272019-10-08 14:32:25 +0300444 s = str.encode(s, self.encoding, self.errors).decode(self.encoding, self.errors)
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400445 return self.shell.write(s, self.tags)
446
447
Serhiy Storchakab690a272019-10-08 14:32:25 +0300448class StdInputFile(StdioFile):
449 _line_buffer = ''
Terry Jan Reedy6cf0e132016-07-15 02:43:03 -0400450
451 def readable(self):
452 return True
453
454 def read(self, size=-1):
455 if self.closed:
456 raise ValueError("read from closed file")
457 if size is None:
458 size = -1
459 elif not isinstance(size, int):
460 raise TypeError('must be int, not ' + type(size).__name__)
461 result = self._line_buffer
462 self._line_buffer = ''
463 if size < 0:
464 while True:
465 line = self.shell.readline()
466 if not line: break
467 result += line
468 else:
469 while len(result) < size:
470 line = self.shell.readline()
471 if not line: break
472 result += line
473 self._line_buffer = result[size:]
474 result = result[:size]
475 return result
476
477 def readline(self, size=-1):
478 if self.closed:
479 raise ValueError("read from closed file")
480 if size is None:
481 size = -1
482 elif not isinstance(size, int):
483 raise TypeError('must be int, not ' + type(size).__name__)
484 line = self._line_buffer or self.shell.readline()
485 if size < 0:
486 size = len(line)
487 eol = line.find('\n', 0, size)
488 if eol >= 0:
489 size = eol + 1
490 self._line_buffer = line[size:]
491 return line[:size]
492
493 def close(self):
494 self.shell.close()
495
496
Chui Tey5d2af632002-05-26 13:36:41 +0000497class MyHandler(rpc.RPCHandler):
498
499 def handle(self):
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000500 """Override base method"""
Chui Tey5d2af632002-05-26 13:36:41 +0000501 executive = Executive(self)
502 self.register("exec", executive)
Serhiy Storchaka39e70a42013-01-25 15:30:58 +0200503 self.console = self.get_remote_proxy("console")
Serhiy Storchakab690a272019-10-08 14:32:25 +0300504 sys.stdin = StdInputFile(self.console, "stdin",
505 iomenu.encoding, iomenu.errors)
506 sys.stdout = StdOutputFile(self.console, "stdout",
507 iomenu.encoding, iomenu.errors)
508 sys.stderr = StdOutputFile(self.console, "stderr",
509 iomenu.encoding, "backslashreplace")
Serhiy Storchaka39e70a42013-01-25 15:30:58 +0200510
Andrew Svetlovcd49d532012-03-25 11:43:02 +0300511 sys.displayhook = rpc.displayhook
Kurt B. Kaiserf609a342007-12-28 03:57:56 +0000512 # page help() text to shell.
513 import pydoc # import must be done here to capture i/o binding
514 pydoc.pager = pydoc.plainpager
Benjamin Peterson0d4931e2013-05-11 22:24:28 -0500515
516 # Keep a reference to stdin so that it won't try to exit IDLE if
517 # sys.stdin gets changed from within IDLE's shell. See issue17838.
518 self._keep_stdin = sys.stdin
519
Tal Einatfcf1d002019-07-06 15:35:24 +0300520 install_recursionlimit_wrappers()
521
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000522 self.interp = self.get_remote_proxy("interp")
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000523 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
524
525 def exithook(self):
526 "override SocketIO method - wait for MainThread to shut us down"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000527 time.sleep(10)
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000528
529 def EOFhook(self):
530 "Override SocketIO method - terminate wait on callback and exit thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000531 global quitting
532 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000533 thread.interrupt_main()
Kurt B. Kaisera00050f2003-05-08 20:26:55 +0000534
535 def decode_interrupthook(self):
536 "interrupt awakened thread"
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000537 global quitting
538 quitting = True
Kurt B. Kaiser93e8e542003-06-13 22:03:43 +0000539 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000540
Chui Tey5d2af632002-05-26 13:36:41 +0000541
Terry Jan Reedy8dfe1562021-01-24 14:08:50 -0500542class Executive:
Chui Tey5d2af632002-05-26 13:36:41 +0000543
544 def __init__(self, rpchandler):
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000545 self.rpchandler = rpchandler
Ken7a343802021-01-27 07:55:52 +0800546 if idlelib.testing is False:
547 self.locals = __main__.__dict__
548 self.calltip = calltip.Calltip()
549 self.autocomplete = autocomplete.AutoComplete()
550 else:
551 self.locals = {}
Chui Tey5d2af632002-05-26 13:36:41 +0000552
553 def runcode(self, code):
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000554 global interruptable
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000555 try:
Ken7a343802021-01-27 07:55:52 +0800556 self.user_exc_info = None
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000557 interruptable = True
558 try:
559 exec(code, self.locals)
560 finally:
561 interruptable = False
Terry Jan Reedy6d965b32019-05-19 22:52:22 -0400562 except SystemExit as e:
563 if e.args: # SystemExit called with an argument.
564 ob = e.args[0]
565 if not isinstance(ob, (type(None), int)):
566 print('SystemExit: ' + str(ob), file=sys.stderr)
567 # Return to the interactive prompt.
Kurt B. Kaiser86bc4642003-02-27 23:04:17 +0000568 except:
Ken7a343802021-01-27 07:55:52 +0800569 self.user_exc_info = sys.exc_info() # For testing, hook, viewer.
Kurt B. Kaiser67fd0ea2003-05-24 20:59:15 +0000570 if quitting:
Kurt B. Kaiser62df0442003-05-28 01:47:46 +0000571 exit()
Ken7a343802021-01-27 07:55:52 +0800572 if sys.excepthook is sys.__excepthook__:
573 print_exception()
574 else:
575 try:
576 sys.excepthook(*self.user_exc_info)
577 except:
578 self.user_exc_info = sys.exc_info() # For testing.
579 print_exception()
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000580 jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
581 if jit:
582 self.rpchandler.interp.open_remote_stack_viewer()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000583 else:
Kurt B. Kaiser9ec454e2003-05-12 02:33:47 +0000584 flush_stdout()
Chui Tey5d2af632002-05-26 13:36:41 +0000585
Kurt B. Kaiser003091c2003-02-17 18:57:16 +0000586 def interrupt_the_server(self):
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000587 if interruptable:
588 thread.interrupt_main()
Kurt B. Kaiser11c53e22003-03-22 19:40:19 +0000589
Kurt B. Kaiser0e3a5772002-06-16 03:32:24 +0000590 def start_the_debugger(self, gui_adap_oid):
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400591 return debugger_r.start_debugger(self.rpchandler, gui_adap_oid)
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000592
593 def stop_the_debugger(self, idb_adap_oid):
594 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
595 self.rpchandler.unregister(idb_adap_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000596
Kurt B. Kaiser5afa1df2002-10-10 08:25:24 +0000597 def get_the_calltip(self, name):
598 return self.calltip.fetch_tip(name)
599
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000600 def get_the_completion_list(self, what, mode):
601 return self.autocomplete.fetch_completions(what, mode)
602
Chui Tey5d2af632002-05-26 13:36:41 +0000603 def stackviewer(self, flist_oid=None):
Ken7a343802021-01-27 07:55:52 +0800604 if self.user_exc_info:
605 typ, val, tb = self.user_exc_info
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000606 else:
Chui Tey5d2af632002-05-26 13:36:41 +0000607 return None
608 flist = None
609 if flist_oid is not None:
Kurt B. Kaiserffd3a422002-06-26 02:32:09 +0000610 flist = self.rpchandler.get_remote_proxy(flist_oid)
Chui Tey5d2af632002-05-26 13:36:41 +0000611 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
612 tb = tb.tb_next
Kurt B. Kaiser9f366092003-06-02 01:50:19 +0000613 sys.last_type = typ
614 sys.last_value = val
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400615 item = stackviewer.StackTreeItem(flist, tb)
616 return debugobj_r.remote_object_tree_item(item)
Terry Jan Reedy95a3f112013-06-28 23:50:12 -0400617
Terry Jan Reedy81bb97d2019-05-24 21:59:53 -0400618
619if __name__ == '__main__':
620 from unittest import main
621 main('idlelib.idle_test.test_run', verbosity=2)
622
623capture_warnings(False) # Make sure turned off; see bpo-18081.