comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/pyshell.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 #! /usr/bin/env python3
2
3 import sys
4 if __name__ == "__main__":
5 sys.modules['idlelib.pyshell'] = sys.modules['__main__']
6
7 try:
8 from tkinter import *
9 except ImportError:
10 print("** IDLE can't import Tkinter.\n"
11 "Your Python may not be configured for Tk. **", file=sys.__stderr__)
12 raise SystemExit(1)
13
14 # Valid arguments for the ...Awareness call below are defined in the following.
15 # https://msdn.microsoft.com/en-us/library/windows/desktop/dn280512(v=vs.85).aspx
16 if sys.platform == 'win32':
17 try:
18 import ctypes
19 PROCESS_SYSTEM_DPI_AWARE = 1
20 ctypes.OleDLL('shcore').SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
21 except (ImportError, AttributeError, OSError):
22 pass
23
24 import tkinter.messagebox as tkMessageBox
25 if TkVersion < 8.5:
26 root = Tk() # otherwise create root in main
27 root.withdraw()
28 from idlelib.run import fix_scaling
29 fix_scaling(root)
30 tkMessageBox.showerror("Idle Cannot Start",
31 "Idle requires tcl/tk 8.5+, not %s." % TkVersion,
32 parent=root)
33 raise SystemExit(1)
34
35 from code import InteractiveInterpreter
36 import linecache
37 import os
38 import os.path
39 from platform import python_version
40 import re
41 import socket
42 import subprocess
43 from textwrap import TextWrapper
44 import threading
45 import time
46 import tokenize
47 import warnings
48
49 from idlelib.colorizer import ColorDelegator
50 from idlelib.config import idleConf
51 from idlelib import debugger
52 from idlelib import debugger_r
53 from idlelib.editor import EditorWindow, fixwordbreaks
54 from idlelib.filelist import FileList
55 from idlelib.outwin import OutputWindow
56 from idlelib import rpc
57 from idlelib.run import idle_formatwarning, StdInputFile, StdOutputFile
58 from idlelib.undo import UndoDelegator
59
60 HOST = '127.0.0.1' # python execution server on localhost loopback
61 PORT = 0 # someday pass in host, port for remote debug capability
62
63 # Override warnings module to write to warning_stream. Initialize to send IDLE
64 # internal warnings to the console. ScriptBinding.check_syntax() will
65 # temporarily redirect the stream to the shell window to display warnings when
66 # checking user's code.
67 warning_stream = sys.__stderr__ # None, at least on Windows, if no console.
68
69 def idle_showwarning(
70 message, category, filename, lineno, file=None, line=None):
71 """Show Idle-format warning (after replacing warnings.showwarning).
72
73 The differences are the formatter called, the file=None replacement,
74 which can be None, the capture of the consequence AttributeError,
75 and the output of a hard-coded prompt.
76 """
77 if file is None:
78 file = warning_stream
79 try:
80 file.write(idle_formatwarning(
81 message, category, filename, lineno, line=line))
82 file.write(">>> ")
83 except (AttributeError, OSError):
84 pass # if file (probably __stderr__) is invalid, skip warning.
85
86 _warnings_showwarning = None
87
88 def capture_warnings(capture):
89 "Replace warning.showwarning with idle_showwarning, or reverse."
90
91 global _warnings_showwarning
92 if capture:
93 if _warnings_showwarning is None:
94 _warnings_showwarning = warnings.showwarning
95 warnings.showwarning = idle_showwarning
96 else:
97 if _warnings_showwarning is not None:
98 warnings.showwarning = _warnings_showwarning
99 _warnings_showwarning = None
100
101 capture_warnings(True)
102
103 def extended_linecache_checkcache(filename=None,
104 orig_checkcache=linecache.checkcache):
105 """Extend linecache.checkcache to preserve the <pyshell#...> entries
106
107 Rather than repeating the linecache code, patch it to save the
108 <pyshell#...> entries, call the original linecache.checkcache()
109 (skipping them), and then restore the saved entries.
110
111 orig_checkcache is bound at definition time to the original
112 method, allowing it to be patched.
113 """
114 cache = linecache.cache
115 save = {}
116 for key in list(cache):
117 if key[:1] + key[-1:] == '<>':
118 save[key] = cache.pop(key)
119 orig_checkcache(filename)
120 cache.update(save)
121
122 # Patch linecache.checkcache():
123 linecache.checkcache = extended_linecache_checkcache
124
125
126 class PyShellEditorWindow(EditorWindow):
127 "Regular text edit window in IDLE, supports breakpoints"
128
129 def __init__(self, *args):
130 self.breakpoints = []
131 EditorWindow.__init__(self, *args)
132 self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)
133 self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here)
134 self.text.bind("<<open-python-shell>>", self.flist.open_shell)
135
136 #TODO: don't read/write this from/to .idlerc when testing
137 self.breakpointPath = os.path.join(
138 idleConf.userdir, 'breakpoints.lst')
139 # whenever a file is changed, restore breakpoints
140 def filename_changed_hook(old_hook=self.io.filename_change_hook,
141 self=self):
142 self.restore_file_breaks()
143 old_hook()
144 self.io.set_filename_change_hook(filename_changed_hook)
145 if self.io.filename:
146 self.restore_file_breaks()
147 self.color_breakpoint_text()
148
149 rmenu_specs = [
150 ("Cut", "<<cut>>", "rmenu_check_cut"),
151 ("Copy", "<<copy>>", "rmenu_check_copy"),
152 ("Paste", "<<paste>>", "rmenu_check_paste"),
153 (None, None, None),
154 ("Set Breakpoint", "<<set-breakpoint-here>>", None),
155 ("Clear Breakpoint", "<<clear-breakpoint-here>>", None)
156 ]
157
158 def color_breakpoint_text(self, color=True):
159 "Turn colorizing of breakpoint text on or off"
160 if self.io is None:
161 # possible due to update in restore_file_breaks
162 return
163 if color:
164 theme = idleConf.CurrentTheme()
165 cfg = idleConf.GetHighlight(theme, "break")
166 else:
167 cfg = {'foreground': '', 'background': ''}
168 self.text.tag_config('BREAK', cfg)
169
170 def set_breakpoint(self, lineno):
171 text = self.text
172 filename = self.io.filename
173 text.tag_add("BREAK", "%d.0" % lineno, "%d.0" % (lineno+1))
174 try:
175 self.breakpoints.index(lineno)
176 except ValueError: # only add if missing, i.e. do once
177 self.breakpoints.append(lineno)
178 try: # update the subprocess debugger
179 debug = self.flist.pyshell.interp.debugger
180 debug.set_breakpoint_here(filename, lineno)
181 except: # but debugger may not be active right now....
182 pass
183
184 def set_breakpoint_here(self, event=None):
185 text = self.text
186 filename = self.io.filename
187 if not filename:
188 text.bell()
189 return
190 lineno = int(float(text.index("insert")))
191 self.set_breakpoint(lineno)
192
193 def clear_breakpoint_here(self, event=None):
194 text = self.text
195 filename = self.io.filename
196 if not filename:
197 text.bell()
198 return
199 lineno = int(float(text.index("insert")))
200 try:
201 self.breakpoints.remove(lineno)
202 except:
203 pass
204 text.tag_remove("BREAK", "insert linestart",\
205 "insert lineend +1char")
206 try:
207 debug = self.flist.pyshell.interp.debugger
208 debug.clear_breakpoint_here(filename, lineno)
209 except:
210 pass
211
212 def clear_file_breaks(self):
213 if self.breakpoints:
214 text = self.text
215 filename = self.io.filename
216 if not filename:
217 text.bell()
218 return
219 self.breakpoints = []
220 text.tag_remove("BREAK", "1.0", END)
221 try:
222 debug = self.flist.pyshell.interp.debugger
223 debug.clear_file_breaks(filename)
224 except:
225 pass
226
227 def store_file_breaks(self):
228 "Save breakpoints when file is saved"
229 # XXX 13 Dec 2002 KBK Currently the file must be saved before it can
230 # be run. The breaks are saved at that time. If we introduce
231 # a temporary file save feature the save breaks functionality
232 # needs to be re-verified, since the breaks at the time the
233 # temp file is created may differ from the breaks at the last
234 # permanent save of the file. Currently, a break introduced
235 # after a save will be effective, but not persistent.
236 # This is necessary to keep the saved breaks synched with the
237 # saved file.
238 #
239 # Breakpoints are set as tagged ranges in the text.
240 # Since a modified file has to be saved before it is
241 # run, and since self.breakpoints (from which the subprocess
242 # debugger is loaded) is updated during the save, the visible
243 # breaks stay synched with the subprocess even if one of these
244 # unexpected breakpoint deletions occurs.
245 breaks = self.breakpoints
246 filename = self.io.filename
247 try:
248 with open(self.breakpointPath, "r") as fp:
249 lines = fp.readlines()
250 except OSError:
251 lines = []
252 try:
253 with open(self.breakpointPath, "w") as new_file:
254 for line in lines:
255 if not line.startswith(filename + '='):
256 new_file.write(line)
257 self.update_breakpoints()
258 breaks = self.breakpoints
259 if breaks:
260 new_file.write(filename + '=' + str(breaks) + '\n')
261 except OSError as err:
262 if not getattr(self.root, "breakpoint_error_displayed", False):
263 self.root.breakpoint_error_displayed = True
264 tkMessageBox.showerror(title='IDLE Error',
265 message='Unable to update breakpoint list:\n%s'
266 % str(err),
267 parent=self.text)
268
269 def restore_file_breaks(self):
270 self.text.update() # this enables setting "BREAK" tags to be visible
271 if self.io is None:
272 # can happen if IDLE closes due to the .update() call
273 return
274 filename = self.io.filename
275 if filename is None:
276 return
277 if os.path.isfile(self.breakpointPath):
278 with open(self.breakpointPath, "r") as fp:
279 lines = fp.readlines()
280 for line in lines:
281 if line.startswith(filename + '='):
282 breakpoint_linenumbers = eval(line[len(filename)+1:])
283 for breakpoint_linenumber in breakpoint_linenumbers:
284 self.set_breakpoint(breakpoint_linenumber)
285
286 def update_breakpoints(self):
287 "Retrieves all the breakpoints in the current window"
288 text = self.text
289 ranges = text.tag_ranges("BREAK")
290 linenumber_list = self.ranges_to_linenumbers(ranges)
291 self.breakpoints = linenumber_list
292
293 def ranges_to_linenumbers(self, ranges):
294 lines = []
295 for index in range(0, len(ranges), 2):
296 lineno = int(float(ranges[index].string))
297 end = int(float(ranges[index+1].string))
298 while lineno < end:
299 lines.append(lineno)
300 lineno += 1
301 return lines
302
303 # XXX 13 Dec 2002 KBK Not used currently
304 # def saved_change_hook(self):
305 # "Extend base method - clear breaks if module is modified"
306 # if not self.get_saved():
307 # self.clear_file_breaks()
308 # EditorWindow.saved_change_hook(self)
309
310 def _close(self):
311 "Extend base method - clear breaks when module is closed"
312 self.clear_file_breaks()
313 EditorWindow._close(self)
314
315
316 class PyShellFileList(FileList):
317 "Extend base class: IDLE supports a shell and breakpoints"
318
319 # override FileList's class variable, instances return PyShellEditorWindow
320 # instead of EditorWindow when new edit windows are created.
321 EditorWindow = PyShellEditorWindow
322
323 pyshell = None
324
325 def open_shell(self, event=None):
326 if self.pyshell:
327 self.pyshell.top.wakeup()
328 else:
329 self.pyshell = PyShell(self)
330 if self.pyshell:
331 if not self.pyshell.begin():
332 return None
333 return self.pyshell
334
335
336 class ModifiedColorDelegator(ColorDelegator):
337 "Extend base class: colorizer for the shell window itself"
338
339 def __init__(self):
340 ColorDelegator.__init__(self)
341 self.LoadTagDefs()
342
343 def recolorize_main(self):
344 self.tag_remove("TODO", "1.0", "iomark")
345 self.tag_add("SYNC", "1.0", "iomark")
346 ColorDelegator.recolorize_main(self)
347
348 def LoadTagDefs(self):
349 ColorDelegator.LoadTagDefs(self)
350 theme = idleConf.CurrentTheme()
351 self.tagdefs.update({
352 "stdin": {'background':None,'foreground':None},
353 "stdout": idleConf.GetHighlight(theme, "stdout"),
354 "stderr": idleConf.GetHighlight(theme, "stderr"),
355 "console": idleConf.GetHighlight(theme, "console"),
356 })
357
358 def removecolors(self):
359 # Don't remove shell color tags before "iomark"
360 for tag in self.tagdefs:
361 self.tag_remove(tag, "iomark", "end")
362
363 class ModifiedUndoDelegator(UndoDelegator):
364 "Extend base class: forbid insert/delete before the I/O mark"
365
366 def insert(self, index, chars, tags=None):
367 try:
368 if self.delegate.compare(index, "<", "iomark"):
369 self.delegate.bell()
370 return
371 except TclError:
372 pass
373 UndoDelegator.insert(self, index, chars, tags)
374
375 def delete(self, index1, index2=None):
376 try:
377 if self.delegate.compare(index1, "<", "iomark"):
378 self.delegate.bell()
379 return
380 except TclError:
381 pass
382 UndoDelegator.delete(self, index1, index2)
383
384
385 class MyRPCClient(rpc.RPCClient):
386
387 def handle_EOF(self):
388 "Override the base class - just re-raise EOFError"
389 raise EOFError
390
391 def restart_line(width, filename): # See bpo-38141.
392 """Return width long restart line formatted with filename.
393
394 Fill line with balanced '='s, with any extras and at least one at
395 the beginning. Do not end with a trailing space.
396 """
397 tag = f"= RESTART: {filename or 'Shell'} ="
398 if width >= len(tag):
399 div, mod = divmod((width -len(tag)), 2)
400 return f"{(div+mod)*'='}{tag}{div*'='}"
401 else:
402 return tag[:-2] # Remove ' ='.
403
404
405 class ModifiedInterpreter(InteractiveInterpreter):
406
407 def __init__(self, tkconsole):
408 self.tkconsole = tkconsole
409 locals = sys.modules['__main__'].__dict__
410 InteractiveInterpreter.__init__(self, locals=locals)
411 self.restarting = False
412 self.subprocess_arglist = None
413 self.port = PORT
414 self.original_compiler_flags = self.compile.compiler.flags
415
416 _afterid = None
417 rpcclt = None
418 rpcsubproc = None
419
420 def spawn_subprocess(self):
421 if self.subprocess_arglist is None:
422 self.subprocess_arglist = self.build_subprocess_arglist()
423 self.rpcsubproc = subprocess.Popen(self.subprocess_arglist)
424
425 def build_subprocess_arglist(self):
426 assert (self.port!=0), (
427 "Socket should have been assigned a port number.")
428 w = ['-W' + s for s in sys.warnoptions]
429 # Maybe IDLE is installed and is being accessed via sys.path,
430 # or maybe it's not installed and the idle.py script is being
431 # run from the IDLE source directory.
432 del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc',
433 default=False, type='bool')
434 command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,)
435 return [sys.executable] + w + ["-c", command, str(self.port)]
436
437 def start_subprocess(self):
438 addr = (HOST, self.port)
439 # GUI makes several attempts to acquire socket, listens for connection
440 for i in range(3):
441 time.sleep(i)
442 try:
443 self.rpcclt = MyRPCClient(addr)
444 break
445 except OSError:
446 pass
447 else:
448 self.display_port_binding_error()
449 return None
450 # if PORT was 0, system will assign an 'ephemeral' port. Find it out:
451 self.port = self.rpcclt.listening_sock.getsockname()[1]
452 # if PORT was not 0, probably working with a remote execution server
453 if PORT != 0:
454 # To allow reconnection within the 2MSL wait (cf. Stevens TCP
455 # V1, 18.6), set SO_REUSEADDR. Note that this can be problematic
456 # on Windows since the implementation allows two active sockets on
457 # the same address!
458 self.rpcclt.listening_sock.setsockopt(socket.SOL_SOCKET,
459 socket.SO_REUSEADDR, 1)
460 self.spawn_subprocess()
461 #time.sleep(20) # test to simulate GUI not accepting connection
462 # Accept the connection from the Python execution server
463 self.rpcclt.listening_sock.settimeout(10)
464 try:
465 self.rpcclt.accept()
466 except socket.timeout:
467 self.display_no_subprocess_error()
468 return None
469 self.rpcclt.register("console", self.tkconsole)
470 self.rpcclt.register("stdin", self.tkconsole.stdin)
471 self.rpcclt.register("stdout", self.tkconsole.stdout)
472 self.rpcclt.register("stderr", self.tkconsole.stderr)
473 self.rpcclt.register("flist", self.tkconsole.flist)
474 self.rpcclt.register("linecache", linecache)
475 self.rpcclt.register("interp", self)
476 self.transfer_path(with_cwd=True)
477 self.poll_subprocess()
478 return self.rpcclt
479
480 def restart_subprocess(self, with_cwd=False, filename=''):
481 if self.restarting:
482 return self.rpcclt
483 self.restarting = True
484 # close only the subprocess debugger
485 debug = self.getdebugger()
486 if debug:
487 try:
488 # Only close subprocess debugger, don't unregister gui_adap!
489 debugger_r.close_subprocess_debugger(self.rpcclt)
490 except:
491 pass
492 # Kill subprocess, spawn a new one, accept connection.
493 self.rpcclt.close()
494 self.terminate_subprocess()
495 console = self.tkconsole
496 was_executing = console.executing
497 console.executing = False
498 self.spawn_subprocess()
499 try:
500 self.rpcclt.accept()
501 except socket.timeout:
502 self.display_no_subprocess_error()
503 return None
504 self.transfer_path(with_cwd=with_cwd)
505 console.stop_readline()
506 # annotate restart in shell window and mark it
507 console.text.delete("iomark", "end-1c")
508 console.write('\n')
509 console.write(restart_line(console.width, filename))
510 console.text.mark_set("restart", "end-1c")
511 console.text.mark_gravity("restart", "left")
512 if not filename:
513 console.showprompt()
514 # restart subprocess debugger
515 if debug:
516 # Restarted debugger connects to current instance of debug GUI
517 debugger_r.restart_subprocess_debugger(self.rpcclt)
518 # reload remote debugger breakpoints for all PyShellEditWindows
519 debug.load_breakpoints()
520 self.compile.compiler.flags = self.original_compiler_flags
521 self.restarting = False
522 return self.rpcclt
523
524 def __request_interrupt(self):
525 self.rpcclt.remotecall("exec", "interrupt_the_server", (), {})
526
527 def interrupt_subprocess(self):
528 threading.Thread(target=self.__request_interrupt).start()
529
530 def kill_subprocess(self):
531 if self._afterid is not None:
532 self.tkconsole.text.after_cancel(self._afterid)
533 try:
534 self.rpcclt.listening_sock.close()
535 except AttributeError: # no socket
536 pass
537 try:
538 self.rpcclt.close()
539 except AttributeError: # no socket
540 pass
541 self.terminate_subprocess()
542 self.tkconsole.executing = False
543 self.rpcclt = None
544
545 def terminate_subprocess(self):
546 "Make sure subprocess is terminated"
547 try:
548 self.rpcsubproc.kill()
549 except OSError:
550 # process already terminated
551 return
552 else:
553 try:
554 self.rpcsubproc.wait()
555 except OSError:
556 return
557
558 def transfer_path(self, with_cwd=False):
559 if with_cwd: # Issue 13506
560 path = [''] # include Current Working Directory
561 path.extend(sys.path)
562 else:
563 path = sys.path
564
565 self.runcommand("""if 1:
566 import sys as _sys
567 _sys.path = %r
568 del _sys
569 \n""" % (path,))
570
571 active_seq = None
572
573 def poll_subprocess(self):
574 clt = self.rpcclt
575 if clt is None:
576 return
577 try:
578 response = clt.pollresponse(self.active_seq, wait=0.05)
579 except (EOFError, OSError, KeyboardInterrupt):
580 # lost connection or subprocess terminated itself, restart
581 # [the KBI is from rpc.SocketIO.handle_EOF()]
582 if self.tkconsole.closing:
583 return
584 response = None
585 self.restart_subprocess()
586 if response:
587 self.tkconsole.resetoutput()
588 self.active_seq = None
589 how, what = response
590 console = self.tkconsole.console
591 if how == "OK":
592 if what is not None:
593 print(repr(what), file=console)
594 elif how == "EXCEPTION":
595 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
596 self.remote_stack_viewer()
597 elif how == "ERROR":
598 errmsg = "pyshell.ModifiedInterpreter: Subprocess ERROR:\n"
599 print(errmsg, what, file=sys.__stderr__)
600 print(errmsg, what, file=console)
601 # we received a response to the currently active seq number:
602 try:
603 self.tkconsole.endexecuting()
604 except AttributeError: # shell may have closed
605 pass
606 # Reschedule myself
607 if not self.tkconsole.closing:
608 self._afterid = self.tkconsole.text.after(
609 self.tkconsole.pollinterval, self.poll_subprocess)
610
611 debugger = None
612
613 def setdebugger(self, debugger):
614 self.debugger = debugger
615
616 def getdebugger(self):
617 return self.debugger
618
619 def open_remote_stack_viewer(self):
620 """Initiate the remote stack viewer from a separate thread.
621
622 This method is called from the subprocess, and by returning from this
623 method we allow the subprocess to unblock. After a bit the shell
624 requests the subprocess to open the remote stack viewer which returns a
625 static object looking at the last exception. It is queried through
626 the RPC mechanism.
627
628 """
629 self.tkconsole.text.after(300, self.remote_stack_viewer)
630 return
631
632 def remote_stack_viewer(self):
633 from idlelib import debugobj_r
634 oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {})
635 if oid is None:
636 self.tkconsole.root.bell()
637 return
638 item = debugobj_r.StubObjectTreeItem(self.rpcclt, oid)
639 from idlelib.tree import ScrolledCanvas, TreeNode
640 top = Toplevel(self.tkconsole.root)
641 theme = idleConf.CurrentTheme()
642 background = idleConf.GetHighlight(theme, 'normal')['background']
643 sc = ScrolledCanvas(top, bg=background, highlightthickness=0)
644 sc.frame.pack(expand=1, fill="both")
645 node = TreeNode(sc.canvas, None, item)
646 node.expand()
647 # XXX Should GC the remote tree when closing the window
648
649 gid = 0
650
651 def execsource(self, source):
652 "Like runsource() but assumes complete exec source"
653 filename = self.stuffsource(source)
654 self.execfile(filename, source)
655
656 def execfile(self, filename, source=None):
657 "Execute an existing file"
658 if source is None:
659 with tokenize.open(filename) as fp:
660 source = fp.read()
661 if use_subprocess:
662 source = (f"__file__ = r'''{os.path.abspath(filename)}'''\n"
663 + source + "\ndel __file__")
664 try:
665 code = compile(source, filename, "exec")
666 except (OverflowError, SyntaxError):
667 self.tkconsole.resetoutput()
668 print('*** Error in script or command!\n'
669 'Traceback (most recent call last):',
670 file=self.tkconsole.stderr)
671 InteractiveInterpreter.showsyntaxerror(self, filename)
672 self.tkconsole.showprompt()
673 else:
674 self.runcode(code)
675
676 def runsource(self, source):
677 "Extend base class method: Stuff the source in the line cache first"
678 filename = self.stuffsource(source)
679 self.more = 0
680 # at the moment, InteractiveInterpreter expects str
681 assert isinstance(source, str)
682 # InteractiveInterpreter.runsource() calls its runcode() method,
683 # which is overridden (see below)
684 return InteractiveInterpreter.runsource(self, source, filename)
685
686 def stuffsource(self, source):
687 "Stuff source in the filename cache"
688 filename = "<pyshell#%d>" % self.gid
689 self.gid = self.gid + 1
690 lines = source.split("\n")
691 linecache.cache[filename] = len(source)+1, 0, lines, filename
692 return filename
693
694 def prepend_syspath(self, filename):
695 "Prepend sys.path with file's directory if not already included"
696 self.runcommand("""if 1:
697 _filename = %r
698 import sys as _sys
699 from os.path import dirname as _dirname
700 _dir = _dirname(_filename)
701 if not _dir in _sys.path:
702 _sys.path.insert(0, _dir)
703 del _filename, _sys, _dirname, _dir
704 \n""" % (filename,))
705
706 def showsyntaxerror(self, filename=None):
707 """Override Interactive Interpreter method: Use Colorizing
708
709 Color the offending position instead of printing it and pointing at it
710 with a caret.
711
712 """
713 tkconsole = self.tkconsole
714 text = tkconsole.text
715 text.tag_remove("ERROR", "1.0", "end")
716 type, value, tb = sys.exc_info()
717 msg = getattr(value, 'msg', '') or value or "<no detail available>"
718 lineno = getattr(value, 'lineno', '') or 1
719 offset = getattr(value, 'offset', '') or 0
720 if offset == 0:
721 lineno += 1 #mark end of offending line
722 if lineno == 1:
723 pos = "iomark + %d chars" % (offset-1)
724 else:
725 pos = "iomark linestart + %d lines + %d chars" % \
726 (lineno-1, offset-1)
727 tkconsole.colorize_syntax_error(text, pos)
728 tkconsole.resetoutput()
729 self.write("SyntaxError: %s\n" % msg)
730 tkconsole.showprompt()
731
732 def showtraceback(self):
733 "Extend base class method to reset output properly"
734 self.tkconsole.resetoutput()
735 self.checklinecache()
736 InteractiveInterpreter.showtraceback(self)
737 if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
738 self.tkconsole.open_stack_viewer()
739
740 def checklinecache(self):
741 c = linecache.cache
742 for key in list(c.keys()):
743 if key[:1] + key[-1:] != "<>":
744 del c[key]
745
746 def runcommand(self, code):
747 "Run the code without invoking the debugger"
748 # The code better not raise an exception!
749 if self.tkconsole.executing:
750 self.display_executing_dialog()
751 return 0
752 if self.rpcclt:
753 self.rpcclt.remotequeue("exec", "runcode", (code,), {})
754 else:
755 exec(code, self.locals)
756 return 1
757
758 def runcode(self, code):
759 "Override base class method"
760 if self.tkconsole.executing:
761 self.interp.restart_subprocess()
762 self.checklinecache()
763 debugger = self.debugger
764 try:
765 self.tkconsole.beginexecuting()
766 if not debugger and self.rpcclt is not None:
767 self.active_seq = self.rpcclt.asyncqueue("exec", "runcode",
768 (code,), {})
769 elif debugger:
770 debugger.run(code, self.locals)
771 else:
772 exec(code, self.locals)
773 except SystemExit:
774 if not self.tkconsole.closing:
775 if tkMessageBox.askyesno(
776 "Exit?",
777 "Do you want to exit altogether?",
778 default="yes",
779 parent=self.tkconsole.text):
780 raise
781 else:
782 self.showtraceback()
783 else:
784 raise
785 except:
786 if use_subprocess:
787 print("IDLE internal error in runcode()",
788 file=self.tkconsole.stderr)
789 self.showtraceback()
790 self.tkconsole.endexecuting()
791 else:
792 if self.tkconsole.canceled:
793 self.tkconsole.canceled = False
794 print("KeyboardInterrupt", file=self.tkconsole.stderr)
795 else:
796 self.showtraceback()
797 finally:
798 if not use_subprocess:
799 try:
800 self.tkconsole.endexecuting()
801 except AttributeError: # shell may have closed
802 pass
803
804 def write(self, s):
805 "Override base class method"
806 return self.tkconsole.stderr.write(s)
807
808 def display_port_binding_error(self):
809 tkMessageBox.showerror(
810 "Port Binding Error",
811 "IDLE can't bind to a TCP/IP port, which is necessary to "
812 "communicate with its Python execution server. This might be "
813 "because no networking is installed on this computer. "
814 "Run IDLE with the -n command line switch to start without a "
815 "subprocess and refer to Help/IDLE Help 'Running without a "
816 "subprocess' for further details.",
817 parent=self.tkconsole.text)
818
819 def display_no_subprocess_error(self):
820 tkMessageBox.showerror(
821 "Subprocess Connection Error",
822 "IDLE's subprocess didn't make connection.\n"
823 "See the 'Startup failure' section of the IDLE doc, online at\n"
824 "https://docs.python.org/3/library/idle.html#startup-failure",
825 parent=self.tkconsole.text)
826
827 def display_executing_dialog(self):
828 tkMessageBox.showerror(
829 "Already executing",
830 "The Python Shell window is already executing a command; "
831 "please wait until it is finished.",
832 parent=self.tkconsole.text)
833
834
835 class PyShell(OutputWindow):
836
837 shell_title = "Python " + python_version() + " Shell"
838
839 # Override classes
840 ColorDelegator = ModifiedColorDelegator
841 UndoDelegator = ModifiedUndoDelegator
842
843 # Override menus
844 menu_specs = [
845 ("file", "_File"),
846 ("edit", "_Edit"),
847 ("debug", "_Debug"),
848 ("options", "_Options"),
849 ("window", "_Window"),
850 ("help", "_Help"),
851 ]
852
853 # Extend right-click context menu
854 rmenu_specs = OutputWindow.rmenu_specs + [
855 ("Squeeze", "<<squeeze-current-text>>"),
856 ]
857
858 allow_line_numbers = False
859
860 # New classes
861 from idlelib.history import History
862
863 def __init__(self, flist=None):
864 if use_subprocess:
865 ms = self.menu_specs
866 if ms[2][0] != "shell":
867 ms.insert(2, ("shell", "She_ll"))
868 self.interp = ModifiedInterpreter(self)
869 if flist is None:
870 root = Tk()
871 fixwordbreaks(root)
872 root.withdraw()
873 flist = PyShellFileList(root)
874
875 OutputWindow.__init__(self, flist, None, None)
876
877 self.usetabs = True
878 # indentwidth must be 8 when using tabs. See note in EditorWindow:
879 self.indentwidth = 8
880
881 self.sys_ps1 = sys.ps1 if hasattr(sys, 'ps1') else '>>> '
882 self.prompt_last_line = self.sys_ps1.split('\n')[-1]
883 self.prompt = self.sys_ps1 # Changes when debug active
884
885 text = self.text
886 text.configure(wrap="char")
887 text.bind("<<newline-and-indent>>", self.enter_callback)
888 text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
889 text.bind("<<interrupt-execution>>", self.cancel_callback)
890 text.bind("<<end-of-file>>", self.eof_callback)
891 text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
892 text.bind("<<toggle-debugger>>", self.toggle_debugger)
893 text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
894 if use_subprocess:
895 text.bind("<<view-restart>>", self.view_restart_mark)
896 text.bind("<<restart-shell>>", self.restart_shell)
897 squeezer = self.Squeezer(self)
898 text.bind("<<squeeze-current-text>>",
899 squeezer.squeeze_current_text_event)
900
901 self.save_stdout = sys.stdout
902 self.save_stderr = sys.stderr
903 self.save_stdin = sys.stdin
904 from idlelib import iomenu
905 self.stdin = StdInputFile(self, "stdin",
906 iomenu.encoding, iomenu.errors)
907 self.stdout = StdOutputFile(self, "stdout",
908 iomenu.encoding, iomenu.errors)
909 self.stderr = StdOutputFile(self, "stderr",
910 iomenu.encoding, "backslashreplace")
911 self.console = StdOutputFile(self, "console",
912 iomenu.encoding, iomenu.errors)
913 if not use_subprocess:
914 sys.stdout = self.stdout
915 sys.stderr = self.stderr
916 sys.stdin = self.stdin
917 try:
918 # page help() text to shell.
919 import pydoc # import must be done here to capture i/o rebinding.
920 # XXX KBK 27Dec07 use text viewer someday, but must work w/o subproc
921 pydoc.pager = pydoc.plainpager
922 except:
923 sys.stderr = sys.__stderr__
924 raise
925 #
926 self.history = self.History(self.text)
927 #
928 self.pollinterval = 50 # millisec
929
930 def get_standard_extension_names(self):
931 return idleConf.GetExtensions(shell_only=True)
932
933 reading = False
934 executing = False
935 canceled = False
936 endoffile = False
937 closing = False
938 _stop_readline_flag = False
939
940 def set_warning_stream(self, stream):
941 global warning_stream
942 warning_stream = stream
943
944 def get_warning_stream(self):
945 return warning_stream
946
947 def toggle_debugger(self, event=None):
948 if self.executing:
949 tkMessageBox.showerror("Don't debug now",
950 "You can only toggle the debugger when idle",
951 parent=self.text)
952 self.set_debugger_indicator()
953 return "break"
954 else:
955 db = self.interp.getdebugger()
956 if db:
957 self.close_debugger()
958 else:
959 self.open_debugger()
960
961 def set_debugger_indicator(self):
962 db = self.interp.getdebugger()
963 self.setvar("<<toggle-debugger>>", not not db)
964
965 def toggle_jit_stack_viewer(self, event=None):
966 pass # All we need is the variable
967
968 def close_debugger(self):
969 db = self.interp.getdebugger()
970 if db:
971 self.interp.setdebugger(None)
972 db.close()
973 if self.interp.rpcclt:
974 debugger_r.close_remote_debugger(self.interp.rpcclt)
975 self.resetoutput()
976 self.console.write("[DEBUG OFF]\n")
977 self.prompt = self.sys_ps1
978 self.showprompt()
979 self.set_debugger_indicator()
980
981 def open_debugger(self):
982 if self.interp.rpcclt:
983 dbg_gui = debugger_r.start_remote_debugger(self.interp.rpcclt,
984 self)
985 else:
986 dbg_gui = debugger.Debugger(self)
987 self.interp.setdebugger(dbg_gui)
988 dbg_gui.load_breakpoints()
989 self.prompt = "[DEBUG ON]\n" + self.sys_ps1
990 self.showprompt()
991 self.set_debugger_indicator()
992
993 def beginexecuting(self):
994 "Helper for ModifiedInterpreter"
995 self.resetoutput()
996 self.executing = 1
997
998 def endexecuting(self):
999 "Helper for ModifiedInterpreter"
1000 self.executing = 0
1001 self.canceled = 0
1002 self.showprompt()
1003
1004 def close(self):
1005 "Extend EditorWindow.close()"
1006 if self.executing:
1007 response = tkMessageBox.askokcancel(
1008 "Kill?",
1009 "Your program is still running!\n Do you want to kill it?",
1010 default="ok",
1011 parent=self.text)
1012 if response is False:
1013 return "cancel"
1014 self.stop_readline()
1015 self.canceled = True
1016 self.closing = True
1017 return EditorWindow.close(self)
1018
1019 def _close(self):
1020 "Extend EditorWindow._close(), shut down debugger and execution server"
1021 self.close_debugger()
1022 if use_subprocess:
1023 self.interp.kill_subprocess()
1024 # Restore std streams
1025 sys.stdout = self.save_stdout
1026 sys.stderr = self.save_stderr
1027 sys.stdin = self.save_stdin
1028 # Break cycles
1029 self.interp = None
1030 self.console = None
1031 self.flist.pyshell = None
1032 self.history = None
1033 EditorWindow._close(self)
1034
1035 def ispythonsource(self, filename):
1036 "Override EditorWindow method: never remove the colorizer"
1037 return True
1038
1039 def short_title(self):
1040 return self.shell_title
1041
1042 COPYRIGHT = \
1043 'Type "help", "copyright", "credits" or "license()" for more information.'
1044
1045 def begin(self):
1046 self.text.mark_set("iomark", "insert")
1047 self.resetoutput()
1048 if use_subprocess:
1049 nosub = ''
1050 client = self.interp.start_subprocess()
1051 if not client:
1052 self.close()
1053 return False
1054 else:
1055 nosub = ("==== No Subprocess ====\n\n" +
1056 "WARNING: Running IDLE without a Subprocess is deprecated\n" +
1057 "and will be removed in a later version. See Help/IDLE Help\n" +
1058 "for details.\n\n")
1059 sys.displayhook = rpc.displayhook
1060
1061 self.write("Python %s on %s\n%s\n%s" %
1062 (sys.version, sys.platform, self.COPYRIGHT, nosub))
1063 self.text.focus_force()
1064 self.showprompt()
1065 import tkinter
1066 tkinter._default_root = None # 03Jan04 KBK What's this?
1067 return True
1068
1069 def stop_readline(self):
1070 if not self.reading: # no nested mainloop to exit.
1071 return
1072 self._stop_readline_flag = True
1073 self.top.quit()
1074
1075 def readline(self):
1076 save = self.reading
1077 try:
1078 self.reading = 1
1079 self.top.mainloop() # nested mainloop()
1080 finally:
1081 self.reading = save
1082 if self._stop_readline_flag:
1083 self._stop_readline_flag = False
1084 return ""
1085 line = self.text.get("iomark", "end-1c")
1086 if len(line) == 0: # may be EOF if we quit our mainloop with Ctrl-C
1087 line = "\n"
1088 self.resetoutput()
1089 if self.canceled:
1090 self.canceled = 0
1091 if not use_subprocess:
1092 raise KeyboardInterrupt
1093 if self.endoffile:
1094 self.endoffile = 0
1095 line = ""
1096 return line
1097
1098 def isatty(self):
1099 return True
1100
1101 def cancel_callback(self, event=None):
1102 try:
1103 if self.text.compare("sel.first", "!=", "sel.last"):
1104 return # Active selection -- always use default binding
1105 except:
1106 pass
1107 if not (self.executing or self.reading):
1108 self.resetoutput()
1109 self.interp.write("KeyboardInterrupt\n")
1110 self.showprompt()
1111 return "break"
1112 self.endoffile = 0
1113 self.canceled = 1
1114 if (self.executing and self.interp.rpcclt):
1115 if self.interp.getdebugger():
1116 self.interp.restart_subprocess()
1117 else:
1118 self.interp.interrupt_subprocess()
1119 if self.reading:
1120 self.top.quit() # exit the nested mainloop() in readline()
1121 return "break"
1122
1123 def eof_callback(self, event):
1124 if self.executing and not self.reading:
1125 return # Let the default binding (delete next char) take over
1126 if not (self.text.compare("iomark", "==", "insert") and
1127 self.text.compare("insert", "==", "end-1c")):
1128 return # Let the default binding (delete next char) take over
1129 if not self.executing:
1130 self.resetoutput()
1131 self.close()
1132 else:
1133 self.canceled = 0
1134 self.endoffile = 1
1135 self.top.quit()
1136 return "break"
1137
1138 def linefeed_callback(self, event):
1139 # Insert a linefeed without entering anything (still autoindented)
1140 if self.reading:
1141 self.text.insert("insert", "\n")
1142 self.text.see("insert")
1143 else:
1144 self.newline_and_indent_event(event)
1145 return "break"
1146
1147 def enter_callback(self, event):
1148 if self.executing and not self.reading:
1149 return # Let the default binding (insert '\n') take over
1150 # If some text is selected, recall the selection
1151 # (but only if this before the I/O mark)
1152 try:
1153 sel = self.text.get("sel.first", "sel.last")
1154 if sel:
1155 if self.text.compare("sel.last", "<=", "iomark"):
1156 self.recall(sel, event)
1157 return "break"
1158 except:
1159 pass
1160 # If we're strictly before the line containing iomark, recall
1161 # the current line, less a leading prompt, less leading or
1162 # trailing whitespace
1163 if self.text.compare("insert", "<", "iomark linestart"):
1164 # Check if there's a relevant stdin range -- if so, use it
1165 prev = self.text.tag_prevrange("stdin", "insert")
1166 if prev and self.text.compare("insert", "<", prev[1]):
1167 self.recall(self.text.get(prev[0], prev[1]), event)
1168 return "break"
1169 next = self.text.tag_nextrange("stdin", "insert")
1170 if next and self.text.compare("insert lineend", ">=", next[0]):
1171 self.recall(self.text.get(next[0], next[1]), event)
1172 return "break"
1173 # No stdin mark -- just get the current line, less any prompt
1174 indices = self.text.tag_nextrange("console", "insert linestart")
1175 if indices and \
1176 self.text.compare(indices[0], "<=", "insert linestart"):
1177 self.recall(self.text.get(indices[1], "insert lineend"), event)
1178 else:
1179 self.recall(self.text.get("insert linestart", "insert lineend"), event)
1180 return "break"
1181 # If we're between the beginning of the line and the iomark, i.e.
1182 # in the prompt area, move to the end of the prompt
1183 if self.text.compare("insert", "<", "iomark"):
1184 self.text.mark_set("insert", "iomark")
1185 # If we're in the current input and there's only whitespace
1186 # beyond the cursor, erase that whitespace first
1187 s = self.text.get("insert", "end-1c")
1188 if s and not s.strip():
1189 self.text.delete("insert", "end-1c")
1190 # If we're in the current input before its last line,
1191 # insert a newline right at the insert point
1192 if self.text.compare("insert", "<", "end-1c linestart"):
1193 self.newline_and_indent_event(event)
1194 return "break"
1195 # We're in the last line; append a newline and submit it
1196 self.text.mark_set("insert", "end-1c")
1197 if self.reading:
1198 self.text.insert("insert", "\n")
1199 self.text.see("insert")
1200 else:
1201 self.newline_and_indent_event(event)
1202 self.text.tag_add("stdin", "iomark", "end-1c")
1203 self.text.update_idletasks()
1204 if self.reading:
1205 self.top.quit() # Break out of recursive mainloop()
1206 else:
1207 self.runit()
1208 return "break"
1209
1210 def recall(self, s, event):
1211 # remove leading and trailing empty or whitespace lines
1212 s = re.sub(r'^\s*\n', '' , s)
1213 s = re.sub(r'\n\s*$', '', s)
1214 lines = s.split('\n')
1215 self.text.undo_block_start()
1216 try:
1217 self.text.tag_remove("sel", "1.0", "end")
1218 self.text.mark_set("insert", "end-1c")
1219 prefix = self.text.get("insert linestart", "insert")
1220 if prefix.rstrip().endswith(':'):
1221 self.newline_and_indent_event(event)
1222 prefix = self.text.get("insert linestart", "insert")
1223 self.text.insert("insert", lines[0].strip())
1224 if len(lines) > 1:
1225 orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0)
1226 new_base_indent = re.search(r'^([ \t]*)', prefix).group(0)
1227 for line in lines[1:]:
1228 if line.startswith(orig_base_indent):
1229 # replace orig base indentation with new indentation
1230 line = new_base_indent + line[len(orig_base_indent):]
1231 self.text.insert('insert', '\n'+line.rstrip())
1232 finally:
1233 self.text.see("insert")
1234 self.text.undo_block_stop()
1235
1236 def runit(self):
1237 line = self.text.get("iomark", "end-1c")
1238 # Strip off last newline and surrounding whitespace.
1239 # (To allow you to hit return twice to end a statement.)
1240 i = len(line)
1241 while i > 0 and line[i-1] in " \t":
1242 i = i-1
1243 if i > 0 and line[i-1] == "\n":
1244 i = i-1
1245 while i > 0 and line[i-1] in " \t":
1246 i = i-1
1247 line = line[:i]
1248 self.interp.runsource(line)
1249
1250 def open_stack_viewer(self, event=None):
1251 if self.interp.rpcclt:
1252 return self.interp.remote_stack_viewer()
1253 try:
1254 sys.last_traceback
1255 except:
1256 tkMessageBox.showerror("No stack trace",
1257 "There is no stack trace yet.\n"
1258 "(sys.last_traceback is not defined)",
1259 parent=self.text)
1260 return
1261 from idlelib.stackviewer import StackBrowser
1262 StackBrowser(self.root, self.flist)
1263
1264 def view_restart_mark(self, event=None):
1265 self.text.see("iomark")
1266 self.text.see("restart")
1267
1268 def restart_shell(self, event=None):
1269 "Callback for Run/Restart Shell Cntl-F6"
1270 self.interp.restart_subprocess(with_cwd=True)
1271
1272 def showprompt(self):
1273 self.resetoutput()
1274 self.console.write(self.prompt)
1275 self.text.mark_set("insert", "end-1c")
1276 self.set_line_and_column()
1277 self.io.reset_undo()
1278
1279 def show_warning(self, msg):
1280 width = self.interp.tkconsole.width
1281 wrapper = TextWrapper(width=width, tabsize=8, expand_tabs=True)
1282 wrapped_msg = '\n'.join(wrapper.wrap(msg))
1283 if not wrapped_msg.endswith('\n'):
1284 wrapped_msg += '\n'
1285 self.per.bottom.insert("iomark linestart", wrapped_msg, "stderr")
1286
1287 def resetoutput(self):
1288 source = self.text.get("iomark", "end-1c")
1289 if self.history:
1290 self.history.store(source)
1291 if self.text.get("end-2c") != "\n":
1292 self.text.insert("end-1c", "\n")
1293 self.text.mark_set("iomark", "end-1c")
1294 self.set_line_and_column()
1295
1296 def write(self, s, tags=()):
1297 try:
1298 self.text.mark_gravity("iomark", "right")
1299 count = OutputWindow.write(self, s, tags, "iomark")
1300 self.text.mark_gravity("iomark", "left")
1301 except:
1302 raise ###pass # ### 11Aug07 KBK if we are expecting exceptions
1303 # let's find out what they are and be specific.
1304 if self.canceled:
1305 self.canceled = 0
1306 if not use_subprocess:
1307 raise KeyboardInterrupt
1308 return count
1309
1310 def rmenu_check_cut(self):
1311 try:
1312 if self.text.compare('sel.first', '<', 'iomark'):
1313 return 'disabled'
1314 except TclError: # no selection, so the index 'sel.first' doesn't exist
1315 return 'disabled'
1316 return super().rmenu_check_cut()
1317
1318 def rmenu_check_paste(self):
1319 if self.text.compare('insert','<','iomark'):
1320 return 'disabled'
1321 return super().rmenu_check_paste()
1322
1323
1324 def fix_x11_paste(root):
1325 "Make paste replace selection on x11. See issue #5124."
1326 if root._windowingsystem == 'x11':
1327 for cls in 'Text', 'Entry', 'Spinbox':
1328 root.bind_class(
1329 cls,
1330 '<<Paste>>',
1331 'catch {%W delete sel.first sel.last}\n' +
1332 root.bind_class(cls, '<<Paste>>'))
1333
1334
1335 usage_msg = """\
1336
1337 USAGE: idle [-deins] [-t title] [file]*
1338 idle [-dns] [-t title] (-c cmd | -r file) [arg]*
1339 idle [-dns] [-t title] - [arg]*
1340
1341 -h print this help message and exit
1342 -n run IDLE without a subprocess (DEPRECATED,
1343 see Help/IDLE Help for details)
1344
1345 The following options will override the IDLE 'settings' configuration:
1346
1347 -e open an edit window
1348 -i open a shell window
1349
1350 The following options imply -i and will open a shell:
1351
1352 -c cmd run the command in a shell, or
1353 -r file run script from file
1354
1355 -d enable the debugger
1356 -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
1357 -t title set title of shell window
1358
1359 A default edit window will be bypassed when -c, -r, or - are used.
1360
1361 [arg]* are passed to the command (-c) or script (-r) in sys.argv[1:].
1362
1363 Examples:
1364
1365 idle
1366 Open an edit window or shell depending on IDLE's configuration.
1367
1368 idle foo.py foobar.py
1369 Edit the files, also open a shell if configured to start with shell.
1370
1371 idle -est "Baz" foo.py
1372 Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell
1373 window with the title "Baz".
1374
1375 idle -c "import sys; print(sys.argv)" "foo"
1376 Open a shell window and run the command, passing "-c" in sys.argv[0]
1377 and "foo" in sys.argv[1].
1378
1379 idle -d -s -r foo.py "Hello World"
1380 Open a shell window, run a startup script, enable the debugger, and
1381 run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in
1382 sys.argv[1].
1383
1384 echo "import sys; print(sys.argv)" | idle - "foobar"
1385 Open a shell window, run the script piped in, passing '' in sys.argv[0]
1386 and "foobar" in sys.argv[1].
1387 """
1388
1389 def main():
1390 import getopt
1391 from platform import system
1392 from idlelib import testing # bool value
1393 from idlelib import macosx
1394
1395 global flist, root, use_subprocess
1396
1397 capture_warnings(True)
1398 use_subprocess = True
1399 enable_shell = False
1400 enable_edit = False
1401 debug = False
1402 cmd = None
1403 script = None
1404 startup = False
1405 try:
1406 opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:")
1407 except getopt.error as msg:
1408 print("Error: %s\n%s" % (msg, usage_msg), file=sys.stderr)
1409 sys.exit(2)
1410 for o, a in opts:
1411 if o == '-c':
1412 cmd = a
1413 enable_shell = True
1414 if o == '-d':
1415 debug = True
1416 enable_shell = True
1417 if o == '-e':
1418 enable_edit = True
1419 if o == '-h':
1420 sys.stdout.write(usage_msg)
1421 sys.exit()
1422 if o == '-i':
1423 enable_shell = True
1424 if o == '-n':
1425 print(" Warning: running IDLE without a subprocess is deprecated.",
1426 file=sys.stderr)
1427 use_subprocess = False
1428 if o == '-r':
1429 script = a
1430 if os.path.isfile(script):
1431 pass
1432 else:
1433 print("No script file: ", script)
1434 sys.exit()
1435 enable_shell = True
1436 if o == '-s':
1437 startup = True
1438 enable_shell = True
1439 if o == '-t':
1440 PyShell.shell_title = a
1441 enable_shell = True
1442 if args and args[0] == '-':
1443 cmd = sys.stdin.read()
1444 enable_shell = True
1445 # process sys.argv and sys.path:
1446 for i in range(len(sys.path)):
1447 sys.path[i] = os.path.abspath(sys.path[i])
1448 if args and args[0] == '-':
1449 sys.argv = [''] + args[1:]
1450 elif cmd:
1451 sys.argv = ['-c'] + args
1452 elif script:
1453 sys.argv = [script] + args
1454 elif args:
1455 enable_edit = True
1456 pathx = []
1457 for filename in args:
1458 pathx.append(os.path.dirname(filename))
1459 for dir in pathx:
1460 dir = os.path.abspath(dir)
1461 if not dir in sys.path:
1462 sys.path.insert(0, dir)
1463 else:
1464 dir = os.getcwd()
1465 if dir not in sys.path:
1466 sys.path.insert(0, dir)
1467 # check the IDLE settings configuration (but command line overrides)
1468 edit_start = idleConf.GetOption('main', 'General',
1469 'editor-on-startup', type='bool')
1470 enable_edit = enable_edit or edit_start
1471 enable_shell = enable_shell or not enable_edit
1472
1473 # Setup root. Don't break user code run in IDLE process.
1474 # Don't change environment when testing.
1475 if use_subprocess and not testing:
1476 NoDefaultRoot()
1477 root = Tk(className="Idle")
1478 root.withdraw()
1479 from idlelib.run import fix_scaling
1480 fix_scaling(root)
1481
1482 # set application icon
1483 icondir = os.path.join(os.path.dirname(__file__), 'Icons')
1484 if system() == 'Windows':
1485 iconfile = os.path.join(icondir, 'idle.ico')
1486 root.wm_iconbitmap(default=iconfile)
1487 elif not macosx.isAquaTk():
1488 ext = '.png' if TkVersion >= 8.6 else '.gif'
1489 iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext))
1490 for size in (16, 32, 48)]
1491 icons = [PhotoImage(master=root, file=iconfile)
1492 for iconfile in iconfiles]
1493 root.wm_iconphoto(True, *icons)
1494
1495 # start editor and/or shell windows:
1496 fixwordbreaks(root)
1497 fix_x11_paste(root)
1498 flist = PyShellFileList(root)
1499 macosx.setupApp(root, flist)
1500
1501 if enable_edit:
1502 if not (cmd or script):
1503 for filename in args[:]:
1504 if flist.open(filename) is None:
1505 # filename is a directory actually, disconsider it
1506 args.remove(filename)
1507 if not args:
1508 flist.new()
1509
1510 if enable_shell:
1511 shell = flist.open_shell()
1512 if not shell:
1513 return # couldn't open shell
1514 if macosx.isAquaTk() and flist.dict:
1515 # On OSX: when the user has double-clicked on a file that causes
1516 # IDLE to be launched the shell window will open just in front of
1517 # the file she wants to see. Lower the interpreter window when
1518 # there are open files.
1519 shell.top.lower()
1520 else:
1521 shell = flist.pyshell
1522
1523 # Handle remaining options. If any of these are set, enable_shell
1524 # was set also, so shell must be true to reach here.
1525 if debug:
1526 shell.open_debugger()
1527 if startup:
1528 filename = os.environ.get("IDLESTARTUP") or \
1529 os.environ.get("PYTHONSTARTUP")
1530 if filename and os.path.isfile(filename):
1531 shell.interp.execfile(filename)
1532 if cmd or script:
1533 shell.interp.runcommand("""if 1:
1534 import sys as _sys
1535 _sys.argv = %r
1536 del _sys
1537 \n""" % (sys.argv,))
1538 if cmd:
1539 shell.interp.execsource(cmd)
1540 elif script:
1541 shell.interp.prepend_syspath(script)
1542 shell.interp.execfile(script)
1543 elif shell:
1544 # If there is a shell window and no cmd or script in progress,
1545 # check for problematic issues and print warning message(s) in
1546 # the IDLE shell window; this is less intrusive than always
1547 # opening a separate window.
1548
1549 # Warn if using a problematic OS X Tk version.
1550 tkversionwarning = macosx.tkVersionWarning(root)
1551 if tkversionwarning:
1552 shell.show_warning(tkversionwarning)
1553
1554 # Warn if the "Prefer tabs when opening documents" system
1555 # preference is set to "Always".
1556 prefer_tabs_preference_warning = macosx.preferTabsPreferenceWarning()
1557 if prefer_tabs_preference_warning:
1558 shell.show_warning(prefer_tabs_preference_warning)
1559
1560 while flist.inversedict: # keep IDLE running while files are open.
1561 root.mainloop()
1562 root.destroy()
1563 capture_warnings(False)
1564
1565 if __name__ == "__main__":
1566 main()
1567
1568 capture_warnings(False) # Make sure turned off; see issue 18081