diff options
Diffstat (limited to 'Lib/idlelib')
-rw-r--r-- | Lib/idlelib/AutoComplete.py | 6 | ||||
-rw-r--r-- | Lib/idlelib/Bindings.py | 8 | ||||
-rw-r--r-- | Lib/idlelib/CallTipWindow.py | 6 | ||||
-rw-r--r-- | Lib/idlelib/ColorDelegator.py | 11 | ||||
-rw-r--r-- | Lib/idlelib/EditorWindow.py | 96 | ||||
-rw-r--r-- | Lib/idlelib/IOBinding.py | 2 | ||||
-rw-r--r-- | Lib/idlelib/NEWS.txt | 4 | ||||
-rw-r--r-- | Lib/idlelib/PyShell.py | 144 | ||||
-rw-r--r-- | Lib/idlelib/ScriptBinding.py | 65 | ||||
-rw-r--r-- | Lib/idlelib/config-main.def | 4 | ||||
-rw-r--r-- | Lib/idlelib/configDialog.py | 2 | ||||
-rw-r--r-- | Lib/idlelib/idlever.py | 2 | ||||
-rw-r--r-- | Lib/idlelib/macosxSupport.py | 96 | ||||
-rw-r--r-- | Lib/idlelib/rpc.py | 4 | ||||
-rw-r--r-- | Lib/idlelib/run.py | 7 | ||||
-rw-r--r-- | Lib/idlelib/textView.py | 27 |
16 files changed, 309 insertions, 175 deletions
diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py index fa1733f9a6..4e173252d6 100644 --- a/Lib/idlelib/AutoComplete.py +++ b/Lib/idlelib/AutoComplete.py @@ -190,8 +190,7 @@ class AutoComplete: bigl = eval("dir()", namespace) bigl.sort() if "__all__" in bigl: - smalll = eval("__all__", namespace) - smalll.sort() + smalll = sorted(eval("__all__", namespace)) else: smalll = [s for s in bigl if s[:1] != '_'] else: @@ -200,8 +199,7 @@ class AutoComplete: bigl = dir(entity) bigl.sort() if "__all__" in bigl: - smalll = entity.__all__ - smalll.sort() + smalll = sorted(entity.__all__) else: smalll = [s for s in bigl if s[:1] != '_'] except: diff --git a/Lib/idlelib/Bindings.py b/Lib/idlelib/Bindings.py index 74a93d3b14..ec2720b0d0 100644 --- a/Lib/idlelib/Bindings.py +++ b/Lib/idlelib/Bindings.py @@ -98,14 +98,6 @@ if macosxSupport.runningAsOSXApp(): # menu del menudefs[-1][1][0:2] - menudefs.insert(0, - ('application', [ - ('About IDLE', '<<about-idle>>'), - None, - ('_Preferences....', '<<open-config-dialog>>'), - ])) - - default_keydefs = idleConf.GetCurrentKeySet() del sys diff --git a/Lib/idlelib/CallTipWindow.py b/Lib/idlelib/CallTipWindow.py index 27ed0859c8..a2431f8eff 100644 --- a/Lib/idlelib/CallTipWindow.py +++ b/Lib/idlelib/CallTipWindow.py @@ -22,6 +22,7 @@ class CallTip: self.parenline = self.parencol = None self.lastline = None self.hideid = self.checkhideid = None + self.checkhide_after_id = None def position_window(self): """Check if needs to reposition the window, and if so - do it.""" @@ -102,7 +103,10 @@ class CallTip: self.hidetip() else: self.position_window() - self.widget.after(CHECKHIDE_TIME, self.checkhide_event) + if self.checkhide_after_id is not None: + self.widget.after_cancel(self.checkhide_after_id) + self.checkhide_after_id = \ + self.widget.after(CHECKHIDE_TIME, self.checkhide_event) def hide_event(self, event): if not self.tipwindow: diff --git a/Lib/idlelib/ColorDelegator.py b/Lib/idlelib/ColorDelegator.py index ab69b8ac56..e188192994 100644 --- a/Lib/idlelib/ColorDelegator.py +++ b/Lib/idlelib/ColorDelegator.py @@ -15,15 +15,16 @@ def any(name, alternates): def make_pat(): kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b" builtinlist = [str(name) for name in dir(builtins) - if not name.startswith('_')] + if not name.startswith('_') and \ + name not in keyword.kwlist] # self.file = open("file") : # 1st 'file' colorized normal, 2nd as builtin, 3rd as string builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b" comment = any("COMMENT", [r"#[^\n]*"]) - sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" - dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' - sq3string = r"(\b[rRuU])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" - dq3string = r'(\b[rRuU])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?' + sqstring = r"(\b[rRbB])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" + dqstring = r'(\b[rRbB])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' + sq3string = r"(\b[rRbB])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" + dq3string = r'(\b[rRbB])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?' string = any("STRING", [sq3string, dq3string, sqstring, dqstring]) return kw + "|" + builtin + "|" + comment + "|" + string +\ "|" + any("SYNC", [r"\n"]) diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py index 98ec02bf7d..6a01db0125 100644 --- a/Lib/idlelib/EditorWindow.py +++ b/Lib/idlelib/EditorWindow.py @@ -3,7 +3,6 @@ import os import re import string import imp -from itertools import count from tkinter import * import tkinter.simpledialog as tkSimpleDialog import tkinter.messagebox as tkMessageBox @@ -51,8 +50,63 @@ def _find_module(fullname, path=None): path = module.__path__ except AttributeError: raise ImportError('No source for module ' + module.__name__) + if descr[2] != imp.PY_SOURCE: + # If all of the above fails and didn't raise an exception,fallback + # to a straight import which can find __init__.py in a package. + m = __import__(fullname) + try: + filename = m.__file__ + except AttributeError: + pass + else: + file = None + descr = os.path.splitext(filename)[1], None, imp.PY_SOURCE return file, filename, descr + +class HelpDialog(object): + + def __init__(self): + self.parent = None # parent of help window + self.dlg = None # the help window iteself + + def display(self, parent, near=None): + """ Display the help dialog. + + parent - parent widget for the help window + + near - a Toplevel widget (e.g. EditorWindow or PyShell) + to use as a reference for placing the help window + """ + if self.dlg is None: + self.show_dialog(parent) + if near: + self.nearwindow(near) + + def show_dialog(self, parent): + self.parent = parent + fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt') + self.dlg = dlg = textView.view_file(parent,'Help',fn, modal=False) + dlg.bind('<Destroy>', self.destroy, '+') + + def nearwindow(self, near): + # Place the help dialog near the window specified by parent. + # Note - this may not reposition the window in Metacity + # if "/apps/metacity/general/disable_workarounds" is enabled + dlg = self.dlg + geom = (near.winfo_rootx() + 10, near.winfo_rooty() + 10) + dlg.withdraw() + dlg.geometry("=+%d+%d" % geom) + dlg.deiconify() + dlg.lift() + + def destroy(self, ev=None): + self.dlg = None + self.parent = None + +helpDialog = HelpDialog() # singleton instance + + class EditorWindow(object): from idlelib.Percolator import Percolator from idlelib.ColorDelegator import ColorDelegator @@ -385,7 +439,7 @@ class EditorWindow(object): underline, label = prepstr(label) menudict[name] = menu = Menu(mbar, name=name) mbar.add_cascade(label=label, menu=menu, underline=underline) - if macosxSupport.runningAsOSXApp(): + if macosxSupport.isCarbonAquaTk(self.root): # Insert the application menu menudict['application'] = menu = Menu(mbar, name='apple') mbar.add_cascade(label='IDLE', menu=menu) @@ -443,8 +497,11 @@ class EditorWindow(object): configDialog.ConfigDialog(self.top,'Settings') def help_dialog(self, event=None): - fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt') - textView.view_file(self.top,'Help',fn) + if self.root: + parent = self.root + else: + parent = self.top + helpDialog.display(parent, near=self.top) def python_docs(self, event=None): if sys.platform[:3] == 'win': @@ -789,18 +846,23 @@ class EditorWindow(object): rf_list = [path for path in rf_list if path not in bad_paths] ulchars = "1234567890ABCDEFGHIJK" rf_list = rf_list[0:len(ulchars)] - rf_file = open(self.recent_files_path, 'w', - encoding='utf_8', errors='replace') try: - rf_file.writelines(rf_list) - finally: - rf_file.close() + with open(self.recent_files_path, 'w', + encoding='utf_8', errors='replace') as rf_file: + rf_file.writelines(rf_list) + except IOError as err: + if not getattr(self.root, "recentfilelist_error_displayed", False): + self.root.recentfilelist_error_displayed = True + tkMessageBox.showerror(title='IDLE Error', + message='Unable to update Recent Files list:\n%s' + % str(err), + parent=self.text) # for each edit window instance, construct the recent files menu for instance in self.top.instance_dict: menu = instance.recent_files_menu menu.delete(1, END) # clear, and rebuild: - for i, file in zip(count(), rf_list): - file_name = file[0:-1] # zap \n + for i, file_name in enumerate(rf_list): + file_name = file_name.rstrip() # zap \n # make unicode string to display non-ASCII chars correctly ufile_name = self._filename_to_unicode(file_name) callback = instance.__recent_file_callback(file_name) @@ -1119,7 +1181,10 @@ class EditorWindow(object): assert have > 0 want = ((have - 1) // self.indentwidth) * self.indentwidth # Debug prompt is multilined.... - last_line_of_prompt = sys.ps1.split('\n')[-1] + if self.context_use_ps1: + last_line_of_prompt = sys.ps1.split('\n')[-1] + else: + last_line_of_prompt = '' ncharsdeleted = 0 while 1: if chars == last_line_of_prompt: @@ -1544,7 +1609,12 @@ keynames = { def get_accelerator(keydefs, eventname): keylist = keydefs.get(eventname) - if not keylist: + # issue10940: temporary workaround to prevent hang with OS X Cocoa Tk 8.5 + # if not keylist: + if (not keylist) or (macosxSupport.runningAsOSXApp() and eventname in { + "<<open-module>>", + "<<goto-line>>", + "<<change-indentwidth>>"}): return "" s = keylist[0] s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s) diff --git a/Lib/idlelib/IOBinding.py b/Lib/idlelib/IOBinding.py index 3f5d556bb6..d20c708ef9 100644 --- a/Lib/idlelib/IOBinding.py +++ b/Lib/idlelib/IOBinding.py @@ -232,7 +232,7 @@ class IOBinding: # before being able to execute the code self.set_saved(False) self.text.mark_set("insert", "1.0") - self.text.see("insert") + self.text.yview("insert") self.updaterecentfileslist(filename) return True diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 28882947cf..0a4f98e9fb 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,7 +1,7 @@ -What's New in IDLE 3.1.4? +What's New in IDLE 3.2.1? ========================= -*Release date: XX-XXX-11* +*Release date: 15-May-11* - Issue #6378: Further adjust idle.bat to start associated Python diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py index 0fa3d765aa..6bf0a8c65d 100644 --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -1,13 +1,15 @@ -#! /usr/bin/env python +#! /usr/bin/env python3 +import getopt import os import os.path -import sys -import getopt import re import socket -import time +import subprocess +import sys import threading +import time +import tokenize import traceback import types @@ -37,11 +39,6 @@ from idlelib import macosxSupport 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 -except ImportError: - SIGTERM = 15 - # Override warnings module to write to warning_stream. Initialize to send IDLE # internal warnings to the console. ScriptBinding.check_syntax() will # temporarily redirect the stream to the shell window to display warnings when @@ -55,20 +52,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) @@ -81,18 +79,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: - 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(): @@ -205,18 +202,26 @@ class PyShellEditorWindow(EditorWindow): breaks = self.breakpoints filename = self.io.filename try: - lines = open(self.breakpointPath,"r").readlines() + with open(self.breakpointPath, "r") as fp: + lines = fp.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 @@ -224,7 +229,8 @@ class PyShellEditorWindow(EditorWindow): if filename is None: return if os.path.isfile(self.breakpointPath): - lines = open(self.breakpointPath,"r").readlines() + with open(self.breakpointPath, "r") as fp: + lines = fp.readlines() for line in lines: if line.startswith(filename + '='): breakpoint_linenumbers = eval(line[len(filename)+1:]) @@ -342,15 +348,15 @@ class ModifiedInterpreter(InteractiveInterpreter): self.restarting = False self.subprocess_arglist = None self.port = PORT + self.original_compiler_flags = self.compile.compiler.flags rpcclt = None - rpcpid = None + rpcsubproc = 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) + self.rpcsubproc = subprocess.Popen(self.subprocess_arglist) def build_subprocess_arglist(self): assert (self.port!=0), ( @@ -365,12 +371,7 @@ class ModifiedInterpreter(InteractiveInterpreter): command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,) else: command = "__import__('run').main(%r)" % (del_exitf,) - if sys.platform[:3] == 'win' and ' ' in sys.executable: - # handle embedded space in path by quoting the argument - decorated_exec = '"%s"' % sys.executable - else: - decorated_exec = sys.executable - return [decorated_exec] + w + ["-c", command, str(self.port)] + return [sys.executable] + w + ["-c", command, str(self.port)] def start_subprocess(self): addr = (HOST, self.port) @@ -410,11 +411,11 @@ class ModifiedInterpreter(InteractiveInterpreter): self.rpcclt.register("flist", self.tkconsole.flist) self.rpcclt.register("linecache", linecache) self.rpcclt.register("interp", self) - self.transfer_path() + self.transfer_path(with_cwd=True) self.poll_subprocess() return self.rpcclt - def restart_subprocess(self): + def restart_subprocess(self, with_cwd=False): if self.restarting: return self.rpcclt self.restarting = True @@ -428,7 +429,7 @@ class ModifiedInterpreter(InteractiveInterpreter): pass # Kill subprocess, spawn a new one, accept connection. self.rpcclt.close() - self.unix_terminate() + self.terminate_subprocess() console = self.tkconsole was_executing = console.executing console.executing = False @@ -438,7 +439,7 @@ class ModifiedInterpreter(InteractiveInterpreter): except socket.timeout as err: self.display_no_subprocess_error() return None - self.transfer_path() + self.transfer_path(with_cwd=with_cwd) # annotate restart in shell window and mark it console.text.delete("iomark", "end-1c") if was_executing: @@ -455,6 +456,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 @@ -469,30 +471,35 @@ class ModifiedInterpreter(InteractiveInterpreter): self.rpcclt.close() except AttributeError: # no socket pass - self.unix_terminate() + self.terminate_subprocess() self.tkconsole.executing = False self.rpcclt = None - def unix_terminate(self): - "UNIX: make sure subprocess is terminated and collect status" - if hasattr(os, 'kill'): + def terminate_subprocess(self): + "Make sure subprocess is terminated" + try: + self.rpcsubproc.kill() + except OSError: + # process already terminated + return + else: try: - os.kill(self.rpcpid, SIGTERM) + self.rpcsubproc.wait() except OSError: - # process already terminated: return - else: - try: - os.waitpid(self.rpcpid, 0) - except OSError: - return - def transfer_path(self): + def transfer_path(self, with_cwd=False): + if with_cwd: # Issue 13506 + path = [''] # include Current Working Directory + path.extend(sys.path) + else: + path = sys.path + self.runcommand("""if 1: import sys as _sys _sys.path = %r del _sys - \n""" % (sys.path,)) + \n""" % (path,)) active_seq = None @@ -582,7 +589,8 @@ class ModifiedInterpreter(InteractiveInterpreter): def execfile(self, filename, source=None): "Execute an existing file" if source is None: - source = open(filename, "r").read() + with tokenize.open(filename) as fp: + source = fp.read() try: code = compile(source, filename, "exec") except (OverflowError, SyntaxError): @@ -651,9 +659,9 @@ class ModifiedInterpreter(InteractiveInterpreter): text = tkconsole.text text.tag_remove("ERROR", "1.0", "end") type, value, tb = sys.exc_info() - msg = value.msg or "<no detail available>" - lineno = value.lineno or 1 - offset = value.offset or 0 + msg = getattr(value, 'msg', '') or value or "<no detail available>" + lineno = getattr(value, 'lineno', '') or 1 + offset = getattr(value, 'offset', '') or 0 if offset == 0: lineno += 1 #mark end of offending line if lineno == 1: @@ -1185,7 +1193,8 @@ class PyShell(OutputWindow): self.text.see("restart") def restart_shell(self, event=None): - self.interp.restart_subprocess() + "Callback for Run/Restart Shell Cntl-F6" + self.interp.restart_subprocess(with_cwd=True) def showprompt(self): self.resetoutput() @@ -1417,6 +1426,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() diff --git a/Lib/idlelib/ScriptBinding.py b/Lib/idlelib/ScriptBinding.py index 41e6a59e6d..18ce9650ae 100644 --- a/Lib/idlelib/ScriptBinding.py +++ b/Lib/idlelib/ScriptBinding.py @@ -27,6 +27,7 @@ from idlelib.EditorWindow import EditorWindow from idlelib import PyShell, IOBinding from idlelib.configHandler import idleConf +from idlelib import macosxSupport indent_message = """Error: Inconsistent indentation detected! @@ -52,6 +53,9 @@ class ScriptBinding: self.flist = self.editwin.flist self.root = self.editwin.root + if macosxSupport.runningAsOSXApp(): + self.editwin.text_frame.bind('<<run-module-event-2>>', self._run_module_event) + def check_module_event(self, event): filename = self.getfilename() if not filename: @@ -63,25 +67,20 @@ class ScriptBinding: def tabnanny(self, filename): # XXX: tabnanny should work on binary files as well - with open(filename, 'r', encoding='iso-8859-1') as f: - two_lines = f.readline() + f.readline() - encoding = IOBinding.coding_spec(two_lines) - if not encoding: - encoding = 'utf-8' - f = open(filename, 'r', encoding=encoding) - try: - tabnanny.process_tokens(tokenize.generate_tokens(f.readline)) - except tokenize.TokenError as msg: - msgtxt, (lineno, start) = msg - self.editwin.gotoline(lineno) - self.errorbox("Tabnanny Tokenizing Error", - "Token Error: %s" % msgtxt) - return False - except tabnanny.NannyNag as nag: - # The error messages from tabnanny are too confusing... - self.editwin.gotoline(nag.get_lineno()) - self.errorbox("Tab/space error", indent_message) - return False + with tokenize.open(filename) as f: + try: + tabnanny.process_tokens(tokenize.generate_tokens(f.readline)) + except tokenize.TokenError as msg: + msgtxt, (lineno, start) = msg + self.editwin.gotoline(lineno) + self.errorbox("Tabnanny Tokenizing Error", + "Token Error: %s" % msgtxt) + return False + except tabnanny.NannyNag as nag: + # The error messages from tabnanny are too confusing... + self.editwin.gotoline(nag.get_lineno()) + self.errorbox("Tab/space error", indent_message) + return False return True def checksyntax(self, filename): @@ -102,10 +101,10 @@ class ScriptBinding: try: # If successful, return the compiled code return compile(source, filename, "exec") - except (SyntaxError, OverflowError) as value: - msg = value.msg or "<no detail available>" - lineno = value.lineno or 1 - offset = value.offset or 0 + except (SyntaxError, OverflowError, ValueError) as value: + msg = getattr(value, 'msg', '') or value or "<no detail available>" + lineno = getattr(value, 'lineno', '') or 1 + offset = getattr(value, 'offset', '') or 0 if offset == 0: lineno += 1 #mark end of offending line pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1) @@ -116,14 +115,27 @@ class ScriptBinding: shell.set_warning_stream(saved_stream) def run_module_event(self, event): + if macosxSupport.runningAsOSXApp(): + # Tk-Cocoa in MacOSX is broken until at least + # Tk 8.5.9, and without this rather + # crude workaround IDLE would hang when a user + # tries to run a module using the keyboard shortcut + # (the menu item works fine). + self.editwin.text_frame.after(200, + lambda: self.editwin.text_frame.event_generate('<<run-module-event-2>>')) + return 'break' + else: + return self._run_module_event(event) + + def _run_module_event(self, event): """Run the module after setting up the environment. First check the syntax. If OK, make sure the shell is active and then transfer the arguments, set the run environment's working directory to the directory of the module being executed and also add that directory to its sys.path if not already included. - """ + filename = self.getfilename() if not filename: return 'break' @@ -132,10 +144,9 @@ class ScriptBinding: return 'break' if not self.tabnanny(filename): return 'break' - shell = self.shell - interp = shell.interp + interp = self.shell.interp if PyShell.use_subprocess: - shell.restart_shell() + interp.restart_subprocess(with_cwd=False) dirname = os.path.dirname(filename) # XXX Too often this discards arguments the user just set... interp.runcommand("""if 1: diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def index 5ddd098de1..9546e2bf12 100644 --- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -46,8 +46,8 @@ [General] editor-on-startup= 0 autosave= 0 -print-command-posix=lpr %s -print-command-win=start /min notepad /p %s +print-command-posix=lpr %%s +print-command-win=start /min notepad /p %%s delete-exitfunc= 1 [EditorWindow] diff --git a/Lib/idlelib/configDialog.py b/Lib/idlelib/configDialog.py index 6482119efe..d09fb5eed4 100644 --- a/Lib/idlelib/configDialog.py +++ b/Lib/idlelib/configDialog.py @@ -199,7 +199,7 @@ class ConfigDialog(Toplevel): ("'string'",'string'),('\n var1 = ','normal'),("'selected'",'hilite'), ('\n var2 = ','normal'),("'found'",'hit'), ('\n var3 = ','normal'),('list', 'builtin'), ('(','normal'), - ('None', 'builtin'),(')\n\n','normal'), + ('None', 'keyword'),(')\n\n','normal'), (' error ','error'),(' ','normal'),('cursor |','cursor'), ('\n ','normal'),('shell','console'),(' ','normal'),('stdout','stdout'), (' ','normal'),('stderr','stderr'),('\n','normal')) diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py index 049da68871..cbcda05fdb 100644 --- a/Lib/idlelib/idlever.py +++ b/Lib/idlelib/idlever.py @@ -1 +1 @@ -IDLE_VERSION = "3.1.5rc1" +IDLE_VERSION = "3.2.3rc1" diff --git a/Lib/idlelib/macosxSupport.py b/Lib/idlelib/macosxSupport.py index da519f7a0f..f93ef118db 100644 --- a/Lib/idlelib/macosxSupport.py +++ b/Lib/idlelib/macosxSupport.py @@ -4,6 +4,10 @@ GUI application (as opposed to an X11 application). """ import sys import tkinter +from os import path + + +_appbundle = None def runningAsOSXApp(): """ @@ -11,7 +15,41 @@ def runningAsOSXApp(): If so, assume that Python was built with Aqua Tcl/Tk rather than X11 Tcl/Tk. """ - return (sys.platform == 'darwin' and '.app' in sys.executable) + global _appbundle + if _appbundle is None: + _appbundle = (sys.platform == 'darwin' and '.app' in sys.executable) + return _appbundle + +_carbonaquatk = None + +def isCarbonAquaTk(root): + """ + Returns True if IDLE is using a Carbon Aqua Tk (instead of the + newer Cocoa Aqua Tk). + """ + global _carbonaquatk + if _carbonaquatk is None: + _carbonaquatk = (runningAsOSXApp() and + 'aqua' in root.tk.call('tk', 'windowingsystem') and + 'AppKit' not in root.tk.call('winfo', 'server', '.')) + return _carbonaquatk + +def tkVersionWarning(root): + """ + Returns a string warning message if the Tk version in use appears to + be one known to cause problems with IDLE. The Apple Cocoa-based Tk 8.5 + that was shipped with Mac OS X 10.6. + """ + + if (runningAsOSXApp() and + ('AppKit' in root.tk.call('winfo', 'server', '.')) and + (root.tk.call('info', 'patchlevel') == '8.5.7') ): + return (r"WARNING: The version of Tcl/Tk (8.5.7) in use may" + r" be unstable.\n" + r"Visit http://www.python.org/download/mac/tcltk/" + r" for current information.") + else: + return False def addOpenEventSupport(root, flist): """ @@ -73,9 +111,6 @@ def overrideRootMenu(root, flist): WindowList.add_windows_to_menu(menu) WindowList.register_callback(postwindowsmenu) - menudict['application'] = menu = Menu(menubar, name='apple') - menubar.add_cascade(label='IDLE', menu=menu) - def about_dialog(event=None): from idlelib import aboutDialog aboutDialog.AboutDialog(root, 'About IDLE') @@ -91,9 +126,14 @@ def overrideRootMenu(root, flist): root.instance_dict = flist.inversedict configDialog.ConfigDialog(root, 'Settings') + def help_dialog(event=None): + from idlelib import textView + fn = path.join(path.abspath(path.dirname(__file__)), 'help.txt') + textView.view_file(root, 'Help', fn) root.bind('<<about-idle>>', about_dialog) root.bind('<<open-config-dialog>>', config_dialog) + root.createcommand('::tk::mac::ShowPreferences', config_dialog) if flist: root.bind('<<close-all-windows>>', flist.close_all_callback) @@ -102,35 +142,29 @@ def overrideRootMenu(root, flist): # right thing for now. root.createcommand('exit', flist.close_all_callback) - - ###check if Tk version >= 8.4.14; if so, use hard-coded showprefs binding - tkversion = root.tk.eval('info patchlevel') - # Note: we cannot check if the string tkversion >= '8.4.14', because - # the string '8.4.7' is greater than the string '8.4.14'. - if tuple(map(int, tkversion.split('.'))) >= (8, 4, 14): - Bindings.menudefs[0] = ('application', [ + if isCarbonAquaTk(root): + # for Carbon AquaTk, replace the default Tk apple menu + menudict['application'] = menu = Menu(menubar, name='apple') + menubar.add_cascade(label='IDLE', menu=menu) + Bindings.menudefs.insert(0, + ('application', [ ('About IDLE', '<<about-idle>>'), - None, - ]) - root.createcommand('::tk::mac::ShowPreferences', config_dialog) + None, + ])) + tkversion = root.tk.eval('info patchlevel') + if tuple(map(int, tkversion.split('.'))) < (8, 4, 14): + # for earlier AquaTk versions, supply a Preferences menu item + Bindings.menudefs[0][1].append( + ('_Preferences....', '<<open-config-dialog>>'), + ) else: - for mname, entrylist in Bindings.menudefs: - menu = menudict.get(mname) - if not menu: - continue - else: - for entry in entrylist: - if not entry: - menu.add_separator() - else: - label, eventname = entry - underline, label = prepstr(label) - accelerator = get_accelerator(Bindings.default_keydefs, - eventname) - def command(text=root, eventname=eventname): - text.event_generate(eventname) - menu.add_command(label=label, underline=underline, - command=command, accelerator=accelerator) + # assume Cocoa AquaTk + # replace default About dialog with About IDLE one + root.createcommand('tkAboutDialog', about_dialog) + # replace default "Help" item in Help menu + root.createcommand('::tk::mac::ShowHelp', help_dialog) + # remove redundant "IDLE Help" from menu + del Bindings.menudefs[-1][1][0] def setupApp(root, flist): """ diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py index 0c56ccdcfe..def43945ae 100644 --- a/Lib/idlelib/rpc.py +++ b/Lib/idlelib/rpc.py @@ -570,7 +570,7 @@ def _getmethods(obj, methods): # Adds names to dictionary argument 'methods' for name in dir(obj): attr = getattr(obj, name) - if hasattr(attr, '__call__'): + if callable(attr): methods[name] = 1 if isinstance(obj, type): for super in obj.__bases__: @@ -579,7 +579,7 @@ def _getmethods(obj, methods): def _getattributes(obj, attributes): for name in dir(obj): attr = getattr(obj, name) - if not hasattr(attr, '__call__'): + if not callable(attr): attributes[name] = 1 class MethodProxy(object): diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index fd2cc09042..25338ffaa6 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -25,12 +25,13 @@ except ImportError: pass else: def idle_formatwarning_subproc(message, category, filename, lineno, - file=None, line=None): + 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) diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textView.py index e5c551ad83..1eaa4649ed 100644 --- a/Lib/idlelib/textView.py +++ b/Lib/idlelib/textView.py @@ -9,7 +9,7 @@ class TextViewer(Toplevel): """A simple text viewer dialog for IDLE """ - def __init__(self, parent, title, text): + def __init__(self, parent, title, text, modal=True): """Show the given text in a scrollable window with a 'close' button """ @@ -24,8 +24,6 @@ class TextViewer(Toplevel): self.CreateWidgets() self.title(title) - self.transient(parent) - self.grab_set() self.protocol("WM_DELETE_WINDOW", self.Ok) self.parent = parent self.textView.focus_set() @@ -34,7 +32,11 @@ class TextViewer(Toplevel): self.bind('<Escape>',self.Ok) #dismiss dialog self.textView.insert(0.0, text) self.textView.config(state=DISABLED) - self.wait_window() + + if modal: + self.transient(parent) + self.grab_set() + self.wait_window() def CreateWidgets(self): frameText = Frame(self, relief=SUNKEN, height=700) @@ -57,19 +59,20 @@ class TextViewer(Toplevel): self.destroy() -def view_text(parent, title, text): - TextViewer(parent, title, text) +def view_text(parent, title, text, modal=True): + return TextViewer(parent, title, text, modal) -def view_file(parent, title, filename, encoding=None): +def view_file(parent, title, filename, encoding=None, modal=True): try: - textFile = open(filename, 'r', encoding=encoding) + with open(filename, 'r', encoding=encoding) as file: + contents = file.read() except IOError: import tkinter.messagebox as tkMessageBox tkMessageBox.showerror(title='File Load Error', message='Unable to load file %r .' % filename, parent=parent) else: - return view_text(parent, title, textFile.read()) + return view_text(parent, title, contents, modal) if __name__ == '__main__': @@ -79,11 +82,15 @@ if __name__ == '__main__': filename = './textView.py' text = file(filename, 'r').read() btn1 = Button(root, text='view_text', - command=lambda:view_text(root, 'view_text', text)) + command=lambda:view_text(root, 'view_text', text)) btn1.pack(side=LEFT) btn2 = Button(root, text='view_file', command=lambda:view_file(root, 'view_file', filename)) btn2.pack(side=LEFT) + btn3 = Button(root, text='nonmodal view_text', + command=lambda:view_text(root, 'nonmodal view_text', text, + modal=False)) + btn3.pack(side=LEFT) close = Button(root, text='Close', command=root.destroy) close.pack(side=RIGHT) root.mainloop() |