summaryrefslogtreecommitdiffstats
path: root/Lib/idlelib/PyShell.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/idlelib/PyShell.py')
-rw-r--r--Lib/idlelib/PyShell.py124
1 files changed, 72 insertions, 52 deletions
diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py
index 6524dff..96efcec 100644
--- a/Lib/idlelib/PyShell.py
+++ b/Lib/idlelib/PyShell.py
@@ -36,7 +36,8 @@ from idlelib import RemoteDebugger
from idlelib import macosxSupport
IDENTCHARS = string.ascii_letters + string.digits + "_"
-LOCALHOST = '127.0.0.1'
+HOST = '127.0.0.1' # python execution server on localhost loopback
+PORT = 0 # someday pass in host, port for remote debug capability
try:
from signal import SIGTERM
@@ -56,20 +57,21 @@ except ImportError:
else:
def idle_showwarning(message, category, filename, lineno,
file=None, line=None):
- file = warning_stream
+ if file is None:
+ file = warning_stream
try:
- file.write(warnings.formatwarning(message, category, filename,\
- lineno, file=file, line=line))
+ file.write(warnings.formatwarning(message, category, filename,
+ lineno, line=line))
except IOError:
pass ## file (probably __stderr__) is invalid, warning dropped.
warnings.showwarning = idle_showwarning
- def idle_formatwarning(message, category, filename, lineno,
- file=None, line=None):
+ def idle_formatwarning(message, category, filename, lineno, line=None):
"""Format warnings the IDLE way"""
s = "\nWarning (from warnings module):\n"
s += ' File \"%s\", line %s\n' % (filename, lineno)
- line = linecache.getline(filename, lineno).strip() \
- if line is None else line
+ if line is None:
+ line = linecache.getline(filename, lineno)
+ line = line.strip()
if line:
s += " %s\n" % line
s += "%s: %s\n>>> " % (category.__name__, message)
@@ -82,18 +84,17 @@ def extended_linecache_checkcache(filename=None,
Rather than repeating the linecache code, patch it to save the
<pyshell#...> entries, call the original linecache.checkcache()
- (which destroys them), and then restore the saved entries.
+ (skipping them), and then restore the saved entries.
orig_checkcache is bound at definition time to the original
method, allowing it to be patched.
-
"""
cache = linecache.cache
save = {}
- for filename in cache.keys():
- if filename[:1] + filename[-1:] == '<>':
- save[filename] = cache[filename]
- orig_checkcache()
+ for key in list(cache):
+ if key[:1] + key[-1:] == '<>':
+ save[key] = cache.pop(key)
+ orig_checkcache(filename)
cache.update(save)
# Patch linecache.checkcache():
@@ -206,18 +207,26 @@ class PyShellEditorWindow(EditorWindow):
breaks = self.breakpoints
filename = self.io.filename
try:
- lines = open(self.breakpointPath,"r").readlines()
+ with open(self.breakpointPath,"r") as old_file:
+ lines = old_file.readlines()
except IOError:
lines = []
- new_file = open(self.breakpointPath,"w")
- for line in lines:
- if not line.startswith(filename + '='):
- new_file.write(line)
- self.update_breakpoints()
- breaks = self.breakpoints
- if breaks:
- new_file.write(filename + '=' + str(breaks) + '\n')
- new_file.close()
+ try:
+ with open(self.breakpointPath,"w") as new_file:
+ for line in lines:
+ if not line.startswith(filename + '='):
+ new_file.write(line)
+ self.update_breakpoints()
+ breaks = self.breakpoints
+ if breaks:
+ new_file.write(filename + '=' + str(breaks) + '\n')
+ except IOError as err:
+ if not getattr(self.root, "breakpoint_error_displayed", False):
+ self.root.breakpoint_error_displayed = True
+ tkMessageBox.showerror(title='IDLE Error',
+ message='Unable to update breakpoint list:\n%s'
+ % str(err),
+ parent=self.text)
def restore_file_breaks(self):
self.text.update() # this enables setting "BREAK" tags to be visible
@@ -341,17 +350,22 @@ class ModifiedInterpreter(InteractiveInterpreter):
InteractiveInterpreter.__init__(self, locals=locals)
self.save_warnings_filters = None
self.restarting = False
- self.subprocess_arglist = self.build_subprocess_arglist()
+ self.subprocess_arglist = None
+ self.port = PORT
+ self.original_compiler_flags = self.compile.compiler.flags
- port = 8833
rpcclt = None
rpcpid = None
def spawn_subprocess(self):
+ if self.subprocess_arglist is None:
+ self.subprocess_arglist = self.build_subprocess_arglist()
args = self.subprocess_arglist
self.rpcpid = os.spawnv(os.P_NOWAIT, sys.executable, args)
def build_subprocess_arglist(self):
+ assert (self.port!=0), (
+ "Socket should have been assigned a port number.")
w = ['-W' + s for s in sys.warnoptions]
if 1/2 > 0: # account for new division
w.append('-Qnew')
@@ -372,11 +386,8 @@ class ModifiedInterpreter(InteractiveInterpreter):
return [decorated_exec] + w + ["-c", command, str(self.port)]
def start_subprocess(self):
- # spawning first avoids passing a listening socket to the subprocess
- self.spawn_subprocess()
- #time.sleep(20) # test to simulate GUI not accepting connection
- addr = (LOCALHOST, self.port)
- # Idle starts listening for connection on localhost
+ addr = (HOST, self.port)
+ # GUI makes several attempts to acquire socket, listens for connection
for i in range(3):
time.sleep(i)
try:
@@ -387,6 +398,18 @@ class ModifiedInterpreter(InteractiveInterpreter):
else:
self.display_port_binding_error()
return None
+ # if PORT was 0, system will assign an 'ephemeral' port. Find it out:
+ self.port = self.rpcclt.listening_sock.getsockname()[1]
+ # if PORT was not 0, probably working with a remote execution server
+ if PORT != 0:
+ # To allow reconnection within the 2MSL wait (cf. Stevens TCP
+ # V1, 18.6), set SO_REUSEADDR. Note that this can be problematic
+ # on Windows since the implementation allows two active sockets on
+ # the same address!
+ self.rpcclt.listening_sock.setsockopt(socket.SOL_SOCKET,
+ socket.SO_REUSEADDR, 1)
+ self.spawn_subprocess()
+ #time.sleep(20) # test to simulate GUI not accepting connection
# Accept the connection from the Python execution server
self.rpcclt.listening_sock.settimeout(10)
try:
@@ -445,6 +468,7 @@ class ModifiedInterpreter(InteractiveInterpreter):
gui = RemoteDebugger.restart_subprocess_debugger(self.rpcclt)
# reload remote debugger breakpoints for all PyShellEditWindows
debug.load_breakpoints()
+ self.compile.compiler.flags = self.original_compiler_flags
self.restarting = False
return self.rpcclt
@@ -753,13 +777,12 @@ class ModifiedInterpreter(InteractiveInterpreter):
def display_port_binding_error(self):
tkMessageBox.showerror(
"Port Binding Error",
- "IDLE can't bind TCP/IP port 8833, which is necessary to "
- "communicate with its Python execution server. Either "
- "no networking is installed on this computer or another "
- "process (another IDLE?) is using the port. Run IDLE with the -n "
- "command line switch to start without a subprocess and refer to "
- "Help/IDLE Help 'Running without a subprocess' for further "
- "details.",
+ "IDLE can't bind to a TCP/IP port, which is necessary to "
+ "communicate with its Python execution server. This might be "
+ "because no networking is installed on this computer. "
+ "Run IDLE with the -n command line switch to start without a "
+ "subprocess and refer to Help/IDLE Help 'Running without a "
+ "subprocess' for further details.",
master=self.tkconsole.text)
def display_no_subprocess_error(self):
@@ -972,15 +995,6 @@ class PyShell(OutputWindow):
COPYRIGHT = \
'Type "copyright", "credits" or "license()" for more information.'
- firewallmessage = """
- ****************************************************************
- Personal firewall software may warn about the connection IDLE
- makes to its subprocess using this computer's internal loopback
- interface. This connection is not visible on any external
- interface and no data is sent to or received from the Internet.
- ****************************************************************
- """
-
def begin(self):
self.resetoutput()
if use_subprocess:
@@ -991,9 +1005,8 @@ class PyShell(OutputWindow):
return False
else:
nosub = "==== No Subprocess ===="
- self.write("Python %s on %s\n%s\n%s\nIDLE %s %s\n" %
- (sys.version, sys.platform, self.COPYRIGHT,
- self.firewallmessage, idlever.IDLE_VERSION, nosub))
+ self.write("Python %s on %s\n%s\n%s" %
+ (sys.version, sys.platform, self.COPYRIGHT, nosub))
self.showprompt()
import Tkinter
Tkinter._default_root = None # 03Jan04 KBK What's this?
@@ -1310,7 +1323,7 @@ def main():
global flist, root, use_subprocess
use_subprocess = True
- enable_shell = False
+ enable_shell = True
enable_edit = False
debug = False
cmd = None
@@ -1331,6 +1344,7 @@ def main():
enable_shell = True
if o == '-e':
enable_edit = True
+ enable_shell = False
if o == '-h':
sys.stdout.write(usage_msg)
sys.exit()
@@ -1381,7 +1395,6 @@ def main():
edit_start = idleConf.GetOption('main', 'General',
'editor-on-startup', type='bool')
enable_edit = enable_edit or edit_start
- enable_shell = enable_shell or not edit_start
# start editor and/or shell windows:
root = Tk(className="Idle")
@@ -1429,6 +1442,13 @@ def main():
shell.interp.prepend_syspath(script)
shell.interp.execfile(script)
+ # Check for problematic OS X Tk versions and print a warning message
+ # in the IDLE shell window; this is less intrusive than always opening
+ # a separate window.
+ tkversionwarning = macosxSupport.tkVersionWarning(root)
+ if tkversionwarning:
+ shell.interp.runcommand(''.join(("print('", tkversionwarning, "')")))
+
root.mainloop()
root.destroy()