comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/run.py @ 68:5028fdace37b

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 16:23:26 -0400
parents
children
comparison
equal deleted inserted replaced
67:0e9998148a16 68:5028fdace37b
1 """ idlelib.run
2
3 Simplified, pyshell.ModifiedInterpreter spawns a subprocess with
4 f'''{sys.executable} -c "__import__('idlelib.run').run.main()"'''
5 '.run' is needed because __import__ returns idlelib, not idlelib.run.
6 """
7 import functools
8 import io
9 import linecache
10 import queue
11 import sys
12 import textwrap
13 import time
14 import traceback
15 import _thread as thread
16 import threading
17 import warnings
18
19 from idlelib import autocomplete # AutoComplete, fetch_encodings
20 from idlelib import calltip # Calltip
21 from idlelib import debugger_r # start_debugger
22 from idlelib import debugobj_r # remote_object_tree_item
23 from idlelib import iomenu # encoding
24 from idlelib import rpc # multiple objects
25 from idlelib import stackviewer # StackTreeItem
26 import __main__
27
28 import tkinter # Use tcl and, if startup fails, messagebox.
29 if not hasattr(sys.modules['idlelib.run'], 'firstrun'):
30 # Undo modifications of tkinter by idlelib imports; see bpo-25507.
31 for mod in ('simpledialog', 'messagebox', 'font',
32 'dialog', 'filedialog', 'commondialog',
33 'ttk'):
34 delattr(tkinter, mod)
35 del sys.modules['tkinter.' + mod]
36 # Avoid AttributeError if run again; see bpo-37038.
37 sys.modules['idlelib.run'].firstrun = False
38
39 LOCALHOST = '127.0.0.1'
40
41
42 def idle_formatwarning(message, category, filename, lineno, line=None):
43 """Format warnings the IDLE way."""
44
45 s = "\nWarning (from warnings module):\n"
46 s += ' File \"%s\", line %s\n' % (filename, lineno)
47 if line is None:
48 line = linecache.getline(filename, lineno)
49 line = line.strip()
50 if line:
51 s += " %s\n" % line
52 s += "%s: %s\n" % (category.__name__, message)
53 return s
54
55 def idle_showwarning_subproc(
56 message, category, filename, lineno, file=None, line=None):
57 """Show Idle-format warning after replacing warnings.showwarning.
58
59 The only difference is the formatter called.
60 """
61 if file is None:
62 file = sys.stderr
63 try:
64 file.write(idle_formatwarning(
65 message, category, filename, lineno, line))
66 except OSError:
67 pass # the file (probably stderr) is invalid - this warning gets lost.
68
69 _warnings_showwarning = None
70
71 def capture_warnings(capture):
72 "Replace warning.showwarning with idle_showwarning_subproc, or reverse."
73
74 global _warnings_showwarning
75 if capture:
76 if _warnings_showwarning is None:
77 _warnings_showwarning = warnings.showwarning
78 warnings.showwarning = idle_showwarning_subproc
79 else:
80 if _warnings_showwarning is not None:
81 warnings.showwarning = _warnings_showwarning
82 _warnings_showwarning = None
83
84 capture_warnings(True)
85 tcl = tkinter.Tcl()
86
87 def handle_tk_events(tcl=tcl):
88 """Process any tk events that are ready to be dispatched if tkinter
89 has been imported, a tcl interpreter has been created and tk has been
90 loaded."""
91 tcl.eval("update")
92
93 # Thread shared globals: Establish a queue between a subthread (which handles
94 # the socket) and the main thread (which runs user code), plus global
95 # completion, exit and interruptable (the main thread) flags:
96
97 exit_now = False
98 quitting = False
99 interruptable = False
100
101 def main(del_exitfunc=False):
102 """Start the Python execution server in a subprocess
103
104 In the Python subprocess, RPCServer is instantiated with handlerclass
105 MyHandler, which inherits register/unregister methods from RPCHandler via
106 the mix-in class SocketIO.
107
108 When the RPCServer 'server' is instantiated, the TCPServer initialization
109 creates an instance of run.MyHandler and calls its handle() method.
110 handle() instantiates a run.Executive object, passing it a reference to the
111 MyHandler object. That reference is saved as attribute rpchandler of the
112 Executive instance. The Executive methods have access to the reference and
113 can pass it on to entities that they command
114 (e.g. debugger_r.Debugger.start_debugger()). The latter, in turn, can
115 call MyHandler(SocketIO) register/unregister methods via the reference to
116 register and unregister themselves.
117
118 """
119 global exit_now
120 global quitting
121 global no_exitfunc
122 no_exitfunc = del_exitfunc
123 #time.sleep(15) # test subprocess not responding
124 try:
125 assert(len(sys.argv) > 1)
126 port = int(sys.argv[-1])
127 except:
128 print("IDLE Subprocess: no IP port passed in sys.argv.",
129 file=sys.__stderr__)
130 return
131
132 capture_warnings(True)
133 sys.argv[:] = [""]
134 sockthread = threading.Thread(target=manage_socket,
135 name='SockThread',
136 args=((LOCALHOST, port),))
137 sockthread.daemon = True
138 sockthread.start()
139 while 1:
140 try:
141 if exit_now:
142 try:
143 exit()
144 except KeyboardInterrupt:
145 # exiting but got an extra KBI? Try again!
146 continue
147 try:
148 request = rpc.request_queue.get(block=True, timeout=0.05)
149 except queue.Empty:
150 request = None
151 # Issue 32207: calling handle_tk_events here adds spurious
152 # queue.Empty traceback to event handling exceptions.
153 if request:
154 seq, (method, args, kwargs) = request
155 ret = method(*args, **kwargs)
156 rpc.response_queue.put((seq, ret))
157 else:
158 handle_tk_events()
159 except KeyboardInterrupt:
160 if quitting:
161 exit_now = True
162 continue
163 except SystemExit:
164 capture_warnings(False)
165 raise
166 except:
167 type, value, tb = sys.exc_info()
168 try:
169 print_exception()
170 rpc.response_queue.put((seq, None))
171 except:
172 # Link didn't work, print same exception to __stderr__
173 traceback.print_exception(type, value, tb, file=sys.__stderr__)
174 exit()
175 else:
176 continue
177
178 def manage_socket(address):
179 for i in range(3):
180 time.sleep(i)
181 try:
182 server = MyRPCServer(address, MyHandler)
183 break
184 except OSError as err:
185 print("IDLE Subprocess: OSError: " + err.args[1] +
186 ", retrying....", file=sys.__stderr__)
187 socket_error = err
188 else:
189 print("IDLE Subprocess: Connection to "
190 "IDLE GUI failed, exiting.", file=sys.__stderr__)
191 show_socket_error(socket_error, address)
192 global exit_now
193 exit_now = True
194 return
195 server.handle_request() # A single request only
196
197 def show_socket_error(err, address):
198 "Display socket error from manage_socket."
199 import tkinter
200 from tkinter.messagebox import showerror
201 root = tkinter.Tk()
202 fix_scaling(root)
203 root.withdraw()
204 showerror(
205 "Subprocess Connection Error",
206 f"IDLE's subprocess can't connect to {address[0]}:{address[1]}.\n"
207 f"Fatal OSError #{err.errno}: {err.strerror}.\n"
208 "See the 'Startup failure' section of the IDLE doc, online at\n"
209 "https://docs.python.org/3/library/idle.html#startup-failure",
210 parent=root)
211 root.destroy()
212
213 def print_exception():
214 import linecache
215 linecache.checkcache()
216 flush_stdout()
217 efile = sys.stderr
218 typ, val, tb = excinfo = sys.exc_info()
219 sys.last_type, sys.last_value, sys.last_traceback = excinfo
220 seen = set()
221
222 def print_exc(typ, exc, tb):
223 seen.add(id(exc))
224 context = exc.__context__
225 cause = exc.__cause__
226 if cause is not None and id(cause) not in seen:
227 print_exc(type(cause), cause, cause.__traceback__)
228 print("\nThe above exception was the direct cause "
229 "of the following exception:\n", file=efile)
230 elif (context is not None and
231 not exc.__suppress_context__ and
232 id(context) not in seen):
233 print_exc(type(context), context, context.__traceback__)
234 print("\nDuring handling of the above exception, "
235 "another exception occurred:\n", file=efile)
236 if tb:
237 tbe = traceback.extract_tb(tb)
238 print('Traceback (most recent call last):', file=efile)
239 exclude = ("run.py", "rpc.py", "threading.py", "queue.py",
240 "debugger_r.py", "bdb.py")
241 cleanup_traceback(tbe, exclude)
242 traceback.print_list(tbe, file=efile)
243 lines = traceback.format_exception_only(typ, exc)
244 for line in lines:
245 print(line, end='', file=efile)
246
247 print_exc(typ, val, tb)
248
249 def cleanup_traceback(tb, exclude):
250 "Remove excluded traces from beginning/end of tb; get cached lines"
251 orig_tb = tb[:]
252 while tb:
253 for rpcfile in exclude:
254 if tb[0][0].count(rpcfile):
255 break # found an exclude, break for: and delete tb[0]
256 else:
257 break # no excludes, have left RPC code, break while:
258 del tb[0]
259 while tb:
260 for rpcfile in exclude:
261 if tb[-1][0].count(rpcfile):
262 break
263 else:
264 break
265 del tb[-1]
266 if len(tb) == 0:
267 # exception was in IDLE internals, don't prune!
268 tb[:] = orig_tb[:]
269 print("** IDLE Internal Exception: ", file=sys.stderr)
270 rpchandler = rpc.objecttable['exec'].rpchandler
271 for i in range(len(tb)):
272 fn, ln, nm, line = tb[i]
273 if nm == '?':
274 nm = "-toplevel-"
275 if not line and fn.startswith("<pyshell#"):
276 line = rpchandler.remotecall('linecache', 'getline',
277 (fn, ln), {})
278 tb[i] = fn, ln, nm, line
279
280 def flush_stdout():
281 """XXX How to do this now?"""
282
283 def exit():
284 """Exit subprocess, possibly after first clearing exit functions.
285
286 If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any
287 functions registered with atexit will be removed before exiting.
288 (VPython support)
289
290 """
291 if no_exitfunc:
292 import atexit
293 atexit._clear()
294 capture_warnings(False)
295 sys.exit(0)
296
297
298 def fix_scaling(root):
299 """Scale fonts on HiDPI displays."""
300 import tkinter.font
301 scaling = float(root.tk.call('tk', 'scaling'))
302 if scaling > 1.4:
303 for name in tkinter.font.names(root):
304 font = tkinter.font.Font(root=root, name=name, exists=True)
305 size = int(font['size'])
306 if size < 0:
307 font['size'] = round(-0.75*size)
308
309
310 def fixdoc(fun, text):
311 tem = (fun.__doc__ + '\n\n') if fun.__doc__ is not None else ''
312 fun.__doc__ = tem + textwrap.fill(textwrap.dedent(text))
313
314 RECURSIONLIMIT_DELTA = 30
315
316 def install_recursionlimit_wrappers():
317 """Install wrappers to always add 30 to the recursion limit."""
318 # see: bpo-26806
319
320 @functools.wraps(sys.setrecursionlimit)
321 def setrecursionlimit(*args, **kwargs):
322 # mimic the original sys.setrecursionlimit()'s input handling
323 if kwargs:
324 raise TypeError(
325 "setrecursionlimit() takes no keyword arguments")
326 try:
327 limit, = args
328 except ValueError:
329 raise TypeError(f"setrecursionlimit() takes exactly one "
330 f"argument ({len(args)} given)")
331 if not limit > 0:
332 raise ValueError(
333 "recursion limit must be greater or equal than 1")
334
335 return setrecursionlimit.__wrapped__(limit + RECURSIONLIMIT_DELTA)
336
337 fixdoc(setrecursionlimit, f"""\
338 This IDLE wrapper adds {RECURSIONLIMIT_DELTA} to prevent possible
339 uninterruptible loops.""")
340
341 @functools.wraps(sys.getrecursionlimit)
342 def getrecursionlimit():
343 return getrecursionlimit.__wrapped__() - RECURSIONLIMIT_DELTA
344
345 fixdoc(getrecursionlimit, f"""\
346 This IDLE wrapper subtracts {RECURSIONLIMIT_DELTA} to compensate
347 for the {RECURSIONLIMIT_DELTA} IDLE adds when setting the limit.""")
348
349 # add the delta to the default recursion limit, to compensate
350 sys.setrecursionlimit(sys.getrecursionlimit() + RECURSIONLIMIT_DELTA)
351
352 sys.setrecursionlimit = setrecursionlimit
353 sys.getrecursionlimit = getrecursionlimit
354
355
356 def uninstall_recursionlimit_wrappers():
357 """Uninstall the recursion limit wrappers from the sys module.
358
359 IDLE only uses this for tests. Users can import run and call
360 this to remove the wrapping.
361 """
362 if (
363 getattr(sys.setrecursionlimit, '__wrapped__', None) and
364 getattr(sys.getrecursionlimit, '__wrapped__', None)
365 ):
366 sys.setrecursionlimit = sys.setrecursionlimit.__wrapped__
367 sys.getrecursionlimit = sys.getrecursionlimit.__wrapped__
368 sys.setrecursionlimit(sys.getrecursionlimit() - RECURSIONLIMIT_DELTA)
369
370
371 class MyRPCServer(rpc.RPCServer):
372
373 def handle_error(self, request, client_address):
374 """Override RPCServer method for IDLE
375
376 Interrupt the MainThread and exit server if link is dropped.
377
378 """
379 global quitting
380 try:
381 raise
382 except SystemExit:
383 raise
384 except EOFError:
385 global exit_now
386 exit_now = True
387 thread.interrupt_main()
388 except:
389 erf = sys.__stderr__
390 print('\n' + '-'*40, file=erf)
391 print('Unhandled server exception!', file=erf)
392 print('Thread: %s' % threading.current_thread().name, file=erf)
393 print('Client Address: ', client_address, file=erf)
394 print('Request: ', repr(request), file=erf)
395 traceback.print_exc(file=erf)
396 print('\n*** Unrecoverable, server exiting!', file=erf)
397 print('-'*40, file=erf)
398 quitting = True
399 thread.interrupt_main()
400
401
402 # Pseudofiles for shell-remote communication (also used in pyshell)
403
404 class StdioFile(io.TextIOBase):
405
406 def __init__(self, shell, tags, encoding='utf-8', errors='strict'):
407 self.shell = shell
408 self.tags = tags
409 self._encoding = encoding
410 self._errors = errors
411
412 @property
413 def encoding(self):
414 return self._encoding
415
416 @property
417 def errors(self):
418 return self._errors
419
420 @property
421 def name(self):
422 return '<%s>' % self.tags
423
424 def isatty(self):
425 return True
426
427
428 class StdOutputFile(StdioFile):
429
430 def writable(self):
431 return True
432
433 def write(self, s):
434 if self.closed:
435 raise ValueError("write to closed file")
436 s = str.encode(s, self.encoding, self.errors).decode(self.encoding, self.errors)
437 return self.shell.write(s, self.tags)
438
439
440 class StdInputFile(StdioFile):
441 _line_buffer = ''
442
443 def readable(self):
444 return True
445
446 def read(self, size=-1):
447 if self.closed:
448 raise ValueError("read from closed file")
449 if size is None:
450 size = -1
451 elif not isinstance(size, int):
452 raise TypeError('must be int, not ' + type(size).__name__)
453 result = self._line_buffer
454 self._line_buffer = ''
455 if size < 0:
456 while True:
457 line = self.shell.readline()
458 if not line: break
459 result += line
460 else:
461 while len(result) < size:
462 line = self.shell.readline()
463 if not line: break
464 result += line
465 self._line_buffer = result[size:]
466 result = result[:size]
467 return result
468
469 def readline(self, size=-1):
470 if self.closed:
471 raise ValueError("read from closed file")
472 if size is None:
473 size = -1
474 elif not isinstance(size, int):
475 raise TypeError('must be int, not ' + type(size).__name__)
476 line = self._line_buffer or self.shell.readline()
477 if size < 0:
478 size = len(line)
479 eol = line.find('\n', 0, size)
480 if eol >= 0:
481 size = eol + 1
482 self._line_buffer = line[size:]
483 return line[:size]
484
485 def close(self):
486 self.shell.close()
487
488
489 class MyHandler(rpc.RPCHandler):
490
491 def handle(self):
492 """Override base method"""
493 executive = Executive(self)
494 self.register("exec", executive)
495 self.console = self.get_remote_proxy("console")
496 sys.stdin = StdInputFile(self.console, "stdin",
497 iomenu.encoding, iomenu.errors)
498 sys.stdout = StdOutputFile(self.console, "stdout",
499 iomenu.encoding, iomenu.errors)
500 sys.stderr = StdOutputFile(self.console, "stderr",
501 iomenu.encoding, "backslashreplace")
502
503 sys.displayhook = rpc.displayhook
504 # page help() text to shell.
505 import pydoc # import must be done here to capture i/o binding
506 pydoc.pager = pydoc.plainpager
507
508 # Keep a reference to stdin so that it won't try to exit IDLE if
509 # sys.stdin gets changed from within IDLE's shell. See issue17838.
510 self._keep_stdin = sys.stdin
511
512 install_recursionlimit_wrappers()
513
514 self.interp = self.get_remote_proxy("interp")
515 rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
516
517 def exithook(self):
518 "override SocketIO method - wait for MainThread to shut us down"
519 time.sleep(10)
520
521 def EOFhook(self):
522 "Override SocketIO method - terminate wait on callback and exit thread"
523 global quitting
524 quitting = True
525 thread.interrupt_main()
526
527 def decode_interrupthook(self):
528 "interrupt awakened thread"
529 global quitting
530 quitting = True
531 thread.interrupt_main()
532
533
534 class Executive(object):
535
536 def __init__(self, rpchandler):
537 self.rpchandler = rpchandler
538 self.locals = __main__.__dict__
539 self.calltip = calltip.Calltip()
540 self.autocomplete = autocomplete.AutoComplete()
541
542 def runcode(self, code):
543 global interruptable
544 try:
545 self.usr_exc_info = None
546 interruptable = True
547 try:
548 exec(code, self.locals)
549 finally:
550 interruptable = False
551 except SystemExit as e:
552 if e.args: # SystemExit called with an argument.
553 ob = e.args[0]
554 if not isinstance(ob, (type(None), int)):
555 print('SystemExit: ' + str(ob), file=sys.stderr)
556 # Return to the interactive prompt.
557 except:
558 self.usr_exc_info = sys.exc_info()
559 if quitting:
560 exit()
561 print_exception()
562 jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
563 if jit:
564 self.rpchandler.interp.open_remote_stack_viewer()
565 else:
566 flush_stdout()
567
568 def interrupt_the_server(self):
569 if interruptable:
570 thread.interrupt_main()
571
572 def start_the_debugger(self, gui_adap_oid):
573 return debugger_r.start_debugger(self.rpchandler, gui_adap_oid)
574
575 def stop_the_debugger(self, idb_adap_oid):
576 "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
577 self.rpchandler.unregister(idb_adap_oid)
578
579 def get_the_calltip(self, name):
580 return self.calltip.fetch_tip(name)
581
582 def get_the_completion_list(self, what, mode):
583 return self.autocomplete.fetch_completions(what, mode)
584
585 def stackviewer(self, flist_oid=None):
586 if self.usr_exc_info:
587 typ, val, tb = self.usr_exc_info
588 else:
589 return None
590 flist = None
591 if flist_oid is not None:
592 flist = self.rpchandler.get_remote_proxy(flist_oid)
593 while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
594 tb = tb.tb_next
595 sys.last_type = typ
596 sys.last_value = val
597 item = stackviewer.StackTreeItem(flist, tb)
598 return debugobj_r.remote_object_tree_item(item)
599
600
601 if __name__ == '__main__':
602 from unittest import main
603 main('idlelib.idle_test.test_run', verbosity=2)
604
605 capture_warnings(False) # Make sure turned off; see bpo-18081.