diff options
Diffstat (limited to 'Lib/idlelib')
30 files changed, 468 insertions, 266 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/CREDITS.txt b/Lib/idlelib/CREDITS.txt index 456079b8ca..5ff599dee1 100644 --- a/Lib/idlelib/CREDITS.txt +++ b/Lib/idlelib/CREDITS.txt @@ -25,8 +25,8 @@ integration, debugger integration and persistent breakpoints). Scott David Daniels, Tal Einat, Hernan Foffani, Christos Georgiou, Jim Jewett, Martin v. Löwis, Jason Orendorff, Guilherme Polo, Josh Robb, -Nigel Rowe, Bruce Sherwood, and Jeff Shute have submitted useful patches. -Thanks, guys! +Nigel Rowe, Bruce Sherwood, Jeff Shute, and Weeble have submitted useful +patches. Thanks, guys! For additional details refer to NEWS.txt and Changelog. diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py index 7cc69cbf00..fb05245dc3 100644 --- a/Lib/idlelib/EditorWindow.py +++ b/Lib/idlelib/EditorWindow.py @@ -2,7 +2,6 @@ import sys import os import re import imp -from itertools import count from Tkinter import * import tkSimpleDialog import tkMessageBox @@ -27,8 +26,10 @@ def _sphinx_version(): major, minor, micro, level, serial = sys.version_info release = '%s%s' % (major, minor) if micro: - release += '%s' % micro - if level != 'final': + release += '%s' % (micro,) + if level == 'candidate': + release += 'rc%s' % (serial,) + elif level != 'final': release += '%s%s' % (level[0], serial) return release @@ -47,8 +48,67 @@ 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 + base, ext = os.path.splitext(filename) + if ext == '.pyc': + ext = '.py' + filename = base + ext + descr = filename, 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 @@ -101,8 +161,8 @@ class EditorWindow(object): self.top = top = WindowList.ListedToplevel(root, menu=self.menubar) if flist: self.tkinter_vars = flist.vars - #self.top.instance_dict makes flist.inversedict avalable to - #configDialog.py so it can access all EditorWindow instaces + #self.top.instance_dict makes flist.inversedict available to + #configDialog.py so it can access all EditorWindow instances self.top.instance_dict = flist.inversedict else: self.tkinter_vars = {} # keys: Tkinter event names @@ -135,6 +195,14 @@ class EditorWindow(object): if macosxSupport.runningAsOSXApp(): # Command-W on editorwindows doesn't work without this. text.bind('<<close-window>>', self.close_event) + # Some OS X systems have only one mouse button, + # so use control-click for pulldown menus there. + # (Note, AquaTk defines <2> as the right button if + # present and the Tk Text widget already binds <2>.) + text.bind("<Control-Button-1>",self.right_menu_event) + else: + # Elsewhere, use right-click for pulldown menus. + text.bind("<3>",self.right_menu_event) text.bind("<<cut>>", self.cut) text.bind("<<copy>>", self.copy) text.bind("<<paste>>", self.paste) @@ -153,7 +221,6 @@ class EditorWindow(object): text.bind("<<find-selection>>", self.find_selection_event) text.bind("<<replace>>", self.replace_event) text.bind("<<goto-line>>", self.goto_line_event) - text.bind("<3>", self.right_menu_event) text.bind("<<smart-backspace>>",self.smart_backspace_event) text.bind("<<newline-and-indent>>",self.newline_and_indent_event) text.bind("<<smart-indent>>",self.smart_indent_event) @@ -299,13 +366,13 @@ class EditorWindow(object): return "break" def home_callback(self, event): - if (event.state & 12) != 0 and event.keysym == "Home": - # state&1==shift, state&4==control, state&8==alt - return # <Modifier-Home>; fall back to class binding - + if (event.state & 4) != 0 and event.keysym == "Home": + # state&4==Control. If <Control-Home>, use the Tk binding. + return if self.text.index("iomark") and \ self.text.compare("iomark", "<=", "insert lineend") and \ self.text.compare("insert linestart", "<=", "iomark"): + # In Shell on input line, go to just after prompt insertpt = int(self.text.index("iomark").split(".")[1]) else: line = self.text.get("insert linestart", "insert lineend") @@ -314,30 +381,27 @@ class EditorWindow(object): break else: insertpt=len(line) - lineat = int(self.text.index("insert").split('.')[1]) - if insertpt == lineat: insertpt = 0 - dest = "insert linestart+"+str(insertpt)+"c" - if (event.state&1) == 0: - # shift not pressed + # shift was not pressed self.text.tag_remove("sel", "1.0", "end") else: if not self.text.index("sel.first"): - self.text.mark_set("anchor","insert") - + self.text.mark_set("my_anchor", "insert") # there was no previous selection + else: + if self.text.compare(self.text.index("sel.first"), "<", self.text.index("insert")): + self.text.mark_set("my_anchor", "sel.first") # extend back + else: + self.text.mark_set("my_anchor", "sel.last") # extend forward first = self.text.index(dest) - last = self.text.index("anchor") - + last = self.text.index("my_anchor") if self.text.compare(first,">",last): first,last = last,first - self.text.tag_remove("sel", "1.0", "end") self.text.tag_add("sel", first, last) - self.text.mark_set("insert", dest) self.text.see("insert") return "break" @@ -384,7 +448,7 @@ class EditorWindow(object): 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) @@ -439,12 +503,19 @@ 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': - os.startfile(self.help_url) + try: + os.startfile(self.help_url) + except WindowsError as why: + tkMessageBox.showerror(title='Document Start Failure', + message=str(why), parent=self.text) else: webbrowser.open(self.help_url) return "break" @@ -704,8 +775,8 @@ class EditorWindow(object): if accel: itemName = menu.entrycget(index, 'label') event = '' - if menuEventDict.has_key(menubarItem): - if menuEventDict[menubarItem].has_key(itemName): + if menubarItem in menuEventDict: + if itemName in menuEventDict[menubarItem]: event = menuEventDict[menubarItem][itemName] if event: accel = get_accelerator(keydefs, event) @@ -739,9 +810,13 @@ class EditorWindow(object): "Create a callback with the helpfile value frozen at definition time" def display_extra_help(helpfile=helpfile): if not helpfile.startswith(('www', 'http')): - url = os.path.normpath(helpfile) + helpfile = os.path.normpath(helpfile) if sys.platform[:3] == 'win': - os.startfile(helpfile) + try: + os.startfile(helpfile) + except WindowsError as why: + tkMessageBox.showerror(title='Document Start Failure', + message=str(why), parent=self.text) else: webbrowser.open(helpfile) return display_extra_help @@ -768,17 +843,22 @@ 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') try: - rf_file.writelines(rf_list) - finally: - rf_file.close() + with open(self.recent_files_path, 'w') 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.keys(): 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) @@ -1102,7 +1182,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: @@ -1525,7 +1608,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/FileList.py b/Lib/idlelib/FileList.py index 5622805524..8318ff17b2 100644 --- a/Lib/idlelib/FileList.py +++ b/Lib/idlelib/FileList.py @@ -25,7 +25,7 @@ class FileList: master=self.root) return None key = os.path.normcase(filename) - if self.dict.has_key(key): + if key in self.dict: edit = self.dict[key] edit.top.wakeup() return edit @@ -43,7 +43,7 @@ class FileList: def new(self, filename=None): return self.EditorWindow(self, filename) - def close_all_callback(self, event): + def close_all_callback(self, *args, **kwds): for edit in self.inversedict.keys(): reply = edit.close() if reply == "cancel": @@ -79,7 +79,7 @@ class FileList: newkey = os.path.normcase(filename) if newkey == key: return - if self.dict.has_key(newkey): + if newkey in self.dict: conflict = self.dict[newkey] self.inversedict[conflict] = None tkMessageBox.showerror( diff --git a/Lib/idlelib/FormatParagraph.py b/Lib/idlelib/FormatParagraph.py index 02f96d493a..6a5f9b5dd5 100644 --- a/Lib/idlelib/FormatParagraph.py +++ b/Lib/idlelib/FormatParagraph.py @@ -54,7 +54,7 @@ class FormatParagraph: # If the block ends in a \n, we dont want the comment # prefix inserted after it. (Im not sure it makes sense to # reformat a comment block that isnt made of complete - # lines, but whatever!) Can't think of a clean soltution, + # lines, but whatever!) Can't think of a clean solution, # so we hack away block_suffix = "" if not newdata[-1]: diff --git a/Lib/idlelib/HISTORY.txt b/Lib/idlelib/HISTORY.txt index c0faaad872..01d73ed2ba 100644 --- a/Lib/idlelib/HISTORY.txt +++ b/Lib/idlelib/HISTORY.txt @@ -13,7 +13,7 @@ What's New in IDLEfork 0.8.1? - New tarball released as a result of the 'revitalisation' of the IDLEfork project. -- This release requires python 2.1 or better. Compatability with earlier +- This release requires python 2.1 or better. Compatibility with earlier versions of python (especially ancient ones like 1.5x) is no longer a priority in IDLEfork development. diff --git a/Lib/idlelib/IOBinding.py b/Lib/idlelib/IOBinding.py index cbc1c3343e..c515432ddc 100644 --- a/Lib/idlelib/IOBinding.py +++ b/Lib/idlelib/IOBinding.py @@ -266,7 +266,7 @@ class IOBinding: self.reset_undo() self.set_filename(filename) self.text.mark_set("insert", "1.0") - self.text.see("insert") + self.text.yview("insert") self.updaterecentfileslist(filename) return True @@ -320,17 +320,20 @@ class IOBinding: return "yes" message = "Do you want to save %s before closing?" % ( self.filename or "this untitled document") - m = tkMessageBox.Message( - title="Save On Close", - message=message, - icon=tkMessageBox.QUESTION, - type=tkMessageBox.YESNOCANCEL, - master=self.text) - reply = m.show() - if reply == "yes": + confirm = tkMessageBox.askyesnocancel( + title="Save On Close", + message=message, + default=tkMessageBox.YES, + master=self.text) + if confirm: + reply = "yes" self.save(None) if not self.get_saved(): reply = "cancel" + elif confirm is None: + reply = "cancel" + else: + reply = "no" self.text.focus_set() return reply @@ -339,7 +342,7 @@ class IOBinding: self.save_as(event) else: if self.writefile(self.filename): - self.set_saved(1) + self.set_saved(True) try: self.editwin.store_file_breaks() except AttributeError: # may be a PyShell @@ -465,15 +468,12 @@ class IOBinding: self.text.insert("end-1c", "\n") def print_window(self, event): - m = tkMessageBox.Message( - title="Print", - message="Print to Default Printer", - icon=tkMessageBox.QUESTION, - type=tkMessageBox.OKCANCEL, - default=tkMessageBox.OK, - master=self.text) - reply = m.show() - if reply != tkMessageBox.OK: + confirm = tkMessageBox.askokcancel( + title="Print", + message="Print to Default Printer", + default=tkMessageBox.OK, + master=self.text) + if not confirm: self.text.focus_set() return "break" tempfilename = None @@ -488,8 +488,8 @@ class IOBinding: if not self.writefile(tempfilename): os.unlink(tempfilename) return "break" - platform=os.name - printPlatform=1 + platform = os.name + printPlatform = True if platform == 'posix': #posix platform command = idleConf.GetOption('main','General', 'print-command-posix') @@ -497,7 +497,7 @@ class IOBinding: elif platform == 'nt': #win32 platform command = idleConf.GetOption('main','General','print-command-win') else: #no printing for this platform - printPlatform=0 + printPlatform = False if printPlatform: #we can try to print for this platform command = command % filename pipe = os.popen(command, "r") @@ -511,7 +511,7 @@ class IOBinding: output = "Printing command: %s\n" % repr(command) + output tkMessageBox.showerror("Print status", output, master=self.text) else: #no printing for this platform - message="Printing is not enabled for this platform: %s" % platform + message = "Printing is not enabled for this platform: %s" % platform tkMessageBox.showinfo("Print status", message, master=self.text) if tempfilename: os.unlink(tempfilename) @@ -521,8 +521,8 @@ class IOBinding: savedialog = None filetypes = [ - ("Python and text files", "*.py *.pyw *.txt", "TEXT"), - ("All text files", "*", "TEXT"), + ("Python files", "*.py *.pyw", "TEXT"), + ("Text files", "*.txt", "TEXT"), ("All files", "*"), ] diff --git a/Lib/idlelib/MultiCall.py b/Lib/idlelib/MultiCall.py index 5b73481535..b81c5ed7d0 100644 --- a/Lib/idlelib/MultiCall.py +++ b/Lib/idlelib/MultiCall.py @@ -201,7 +201,7 @@ class _ComplexBinder: seq, handler))) def bind(self, triplet, func): - if not self.bindedfuncs.has_key(triplet[2]): + if triplet[2] not in self.bindedfuncs: self.bindedfuncs[triplet[2]] = [[] for s in _states] for s in _states: lists = [ self.bindedfuncs[detail][i] diff --git a/Lib/idlelib/MultiStatusBar.py b/Lib/idlelib/MultiStatusBar.py index 2d4c5473d4..8ee2d03d04 100644 --- a/Lib/idlelib/MultiStatusBar.py +++ b/Lib/idlelib/MultiStatusBar.py @@ -9,7 +9,7 @@ class MultiStatusBar(Frame): self.labels = {} def set_label(self, name, text='', side=LEFT): - if not self.labels.has_key(name): + if name not in self.labels: label = Label(self, bd=1, relief=SUNKEN, anchor=W) label.pack(side=side) self.labels[name] = label diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index bb4c1431fe..46ef3cb658 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,36 +1,45 @@ -What's New in Python 2.6.4rc1 -============================= +What's New in IDLE 2.7.2? +======================= -*Release date: 07-Oct-2009* +*Release date: 29-May-2011* -- OutputWindow/PyShell right click menu "Go to file/line" wasn't working with - file paths containing spaces. Bug 5559. +- Issue #6378: Further adjust idle.bat to start associated Python -What's New in Python 2.6.3 -========================== +- Issue #11896: Save on Close failed despite selecting "Yes" in dialog. -*Release date: 02-Oct-2009* +- <Home> toggle failing on Tk 8.5, causing IDLE exits and strange selection + behavior. Issue 4676. Improve selection extension behaviour. -What's New in IDLE 2.6.3rc1? -============================ +- <Home> toggle non-functional when NumLock set on Windows. Issue 3851. -*Release date: 28-Sep-2009* -- On OS X IDLE 2.6 shows two Preference menu items. Issue6951. +What's New in IDLE 2.7? +======================= + +*Release date: 07-03-2010* + +- idle.py modified and simplified to better support developing experimental + versions of IDLE which are not installed in the standard location. + +- OutputWindow/PyShell right click menu "Go to file/line" wasn't working with + file paths containing spaces. Bug 5559. - Windows: Version string for the .chm help file changed, file not being accessed Patch 5783 Guilherme Polo -- Tk 8.5 Text widget requires 'wordprocessor' tabstyle attr to handle - mixed space/tab properly. Issue 5129, patch by Guilherme Polo. +- Allow multiple IDLE GUI/subprocess pairs to exist simultaneously. Thanks to + David Scherer for suggesting the use of an ephemeral port for the GUI. + Patch 1529142 Weeble. -What's New in IDLE 2.6.2rc1? -============================ +- Remove port spec from run.py and fix bug where subprocess fails to + extract port from command line when warnings are present. -*Release date: 06-Apr-2009* +- Tk 8.5 Text widget requires 'wordprocessor' tabstyle attr to handle + mixed space/tab properly. Issue 5129, patch by Guilherme Polo. - Issue #3549: On MacOS the preferences menu was not present + What's New in IDLE 2.6? ======================= @@ -56,7 +65,7 @@ What's New in IDLE 2.6a1? in the config dialog would cause non-Python files to be colored as if they were Python source; improve use of ColorDelagator. Patch 1334. Tal Einat. -- ScriptBinding event handlers weren't returning 'break'. Patch 2050, Tal Einat +- ScriptBinding event handlers weren't returning 'break'. Patch 2050, Tal Einat. - There was an error on exit if no sys.exitfunc was defined. Issue 1647. diff --git a/Lib/idlelib/ObjectBrowser.py b/Lib/idlelib/ObjectBrowser.py index 83830812a2..7de698816f 100644 --- a/Lib/idlelib/ObjectBrowser.py +++ b/Lib/idlelib/ObjectBrowser.py @@ -126,7 +126,7 @@ dispatch = { def make_objecttreeitem(labeltext, object, setfunction=None): t = type(object) - if dispatch.has_key(t): + if t in dispatch: c = dispatch[t] else: c = ObjectTreeItem diff --git a/Lib/idlelib/PathBrowser.py b/Lib/idlelib/PathBrowser.py index 6b2c0712e6..d88a48e344 100644 --- a/Lib/idlelib/PathBrowser.py +++ b/Lib/idlelib/PathBrowser.py @@ -78,7 +78,7 @@ class DirBrowserTreeItem(TreeItem): normed_name = os.path.normcase(name) if normed_name[i:] == suff: mod_name = name[:i] - if not modules.has_key(mod_name): + if mod_name not in modules: modules[mod_name] = None sorted.append((normed_name, name)) allnames.remove(name) diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py index 6524dffa12..895d7dac09 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: @@ -400,11 +423,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 +451,7 @@ class ModifiedInterpreter(InteractiveInterpreter): except socket.timeout, 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: @@ -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 @@ -477,12 +501,18 @@ class ModifiedInterpreter(InteractiveInterpreter): 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 @@ -753,13 +783,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 +1001,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 +1011,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? @@ -1196,7 +1215,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() @@ -1310,7 +1330,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 +1351,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 +1402,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 +1449,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/RemoteDebugger.py b/Lib/idlelib/RemoteDebugger.py index ffeffcedc1..647285fe4e 100644 --- a/Lib/idlelib/RemoteDebugger.py +++ b/Lib/idlelib/RemoteDebugger.py @@ -230,7 +230,7 @@ class FrameProxy: return self._get_dict_proxy(did) def _get_dict_proxy(self, did): - if self._dictcache.has_key(did): + if did in self._dictcache: return self._dictcache[did] dp = DictProxy(self._conn, self._oid, did) self._dictcache[did] = dp diff --git a/Lib/idlelib/RstripExtension.py b/Lib/idlelib/RstripExtension.py new file mode 100644 index 0000000000..19e35d4d48 --- /dev/null +++ b/Lib/idlelib/RstripExtension.py @@ -0,0 +1,29 @@ +'Provides "Strip trailing whitespace" under the "Format" menu.' + +__author__ = "Roger D. Serwy <roger.serwy at gmail.com>" + +class RstripExtension: + + menudefs = [ + ('format', [None, + ('Strip trailing whitespace', '<<do-rstrip>>'), + ]),] + + def __init__(self, editwin): + self.editwin = editwin + self.editwin.text.bind("<<do-rstrip>>", self.do_rstrip) + + def do_rstrip(self, event=None): + + text = self.editwin.text + undo = self.editwin.undo + + undo.undo_block_start() + + end_line = int(float(text.index('end'))) + 1 + for cur in range(1, end_line): + txt = text.get('%i.0' % cur, '%i.0 lineend' % cur) + cut = len(txt.rstrip()) + text.delete('%i.%i' % (cur, cut), '%i.0 lineend' % cur) + + undo.undo_block_stop() diff --git a/Lib/idlelib/ScriptBinding.py b/Lib/idlelib/ScriptBinding.py index 3a441650a9..01ac474658 100644 --- a/Lib/idlelib/ScriptBinding.py +++ b/Lib/idlelib/ScriptBinding.py @@ -26,6 +26,7 @@ import tkMessageBox from idlelib import PyShell from idlelib.configHandler import idleConf +from idlelib import macosxSupport IDENTCHARS = string.ascii_letters + string.digits + "_" @@ -53,6 +54,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: @@ -97,7 +101,7 @@ class ScriptBinding: try: # If successful, return the compiled code return compile(source, filename, "exec") - except (SyntaxError, OverflowError), err: + except (SyntaxError, OverflowError, ValueError), err: try: msg, (errorfilename, lineno, offset, line) = err if not errorfilename: @@ -142,10 +146,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: @@ -166,6 +169,19 @@ class ScriptBinding: interp.runcode(code) return 'break' + 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). + _run_module_event = run_module_event + + def run_module_event(self, event): + self.editwin.text_frame.after(200, + lambda: self.editwin.text_frame.event_generate('<<run-module-event-2>>')) + return 'break' + def getfilename(self): """Get source filename. If not saved, offer to save (or create) file @@ -184,9 +200,9 @@ class ScriptBinding: if autosave and filename: self.editwin.io.save(None) else: - reply = self.ask_save_dialog() + confirm = self.ask_save_dialog() self.editwin.text.focus_set() - if reply == "ok": + if confirm: self.editwin.io.save(None) filename = self.editwin.io.filename else: @@ -195,13 +211,11 @@ class ScriptBinding: def ask_save_dialog(self): msg = "Source Must Be Saved\n" + 5*' ' + "OK to Save?" - mb = tkMessageBox.Message(title="Save Before Run or Check", - message=msg, - icon=tkMessageBox.QUESTION, - type=tkMessageBox.OKCANCEL, - default=tkMessageBox.OK, - master=self.editwin.text) - return mb.show() + confirm = tkMessageBox.askokcancel(title="Save Before Run or Check", + message=msg, + default=tkMessageBox.OK, + master=self.editwin.text) + return confirm def errorbox(self, title, message): # XXX This should really be a function of EditorWindow... diff --git a/Lib/idlelib/TreeWidget.py b/Lib/idlelib/TreeWidget.py index 7d962feb32..0feca0196d 100644 --- a/Lib/idlelib/TreeWidget.py +++ b/Lib/idlelib/TreeWidget.py @@ -409,7 +409,7 @@ class FileTreeItem(TreeItem): class ScrolledCanvas: def __init__(self, master, **opts): - if not opts.has_key('yscrollincrement'): + if 'yscrollincrement' not in opts: opts['yscrollincrement'] = 17 self.master = master self.frame = Frame(master) diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def index 2d5cf6822e..78b68f6b56 100644 --- a/Lib/idlelib/config-extensions.def +++ b/Lib/idlelib/config-extensions.def @@ -86,3 +86,9 @@ bgcolor=LightGray fgcolor=Black [CodeContext_bindings] toggle-code-context= + +[RstripExtension] +enable=1 +enable_shell=0 +enable_editor=1 + diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def index fb0aaf4dc1..fdc35ba7b5 100644 --- a/Lib/idlelib/config-keys.def +++ b/Lib/idlelib/config-keys.def @@ -176,7 +176,7 @@ comment-region = <Control-Key-3> redo = <Shift-Command-Key-Z> close-window = <Command-Key-w> restart-shell = <Control-Key-F6> -save-window-as-file = <Command-Key-S> +save-window-as-file = <Shift-Command-Key-S> close-all-windows = <Command-Key-q> view-restart = <Key-F6> tabify-region = <Control-Key-5> @@ -208,7 +208,7 @@ open-new-window = <Command-Key-n> open-module = <Command-Key-m> find-selection = <Shift-Command-Key-F3> python-context-help = <Shift-Key-F1> -save-copy-of-window-as-file = <Shift-Command-Key-s> +save-copy-of-window-as-file = <Option-Command-Key-s> open-window-from-file = <Command-Key-o> python-docs = <Key-F1> diff --git a/Lib/idlelib/configDialog.py b/Lib/idlelib/configDialog.py index 8d3a49dff7..dbaedc76c3 100644 --- a/Lib/idlelib/configDialog.py +++ b/Lib/idlelib/configDialog.py @@ -28,6 +28,7 @@ class ConfigDialog(Toplevel): self.wm_withdraw() self.configure(borderwidth=5) + self.title('IDLE Preferences') self.geometry("+%d+%d" % (parent.winfo_rootx()+20, parent.winfo_rooty()+30)) #Theme Elements. Each theme element key is its display name. @@ -561,7 +562,7 @@ class ConfigDialog(Toplevel): def AddChangedItem(self,type,section,item,value): value=str(value) #make sure we use a string - if not self.changedItems[type].has_key(section): + if section not in self.changedItems[type]: self.changedItems[type][section]={} self.changedItems[type][section][item]=value @@ -708,7 +709,7 @@ class ConfigDialog(Toplevel): return #remove key set from config idleConf.userCfg['keys'].remove_section(keySetName) - if self.changedItems['keys'].has_key(keySetName): + if keySetName in self.changedItems['keys']: del(self.changedItems['keys'][keySetName]) #write changes idleConf.userCfg['keys'].Save() @@ -735,7 +736,7 @@ class ConfigDialog(Toplevel): return #remove theme from config idleConf.userCfg['highlight'].remove_section(themeName) - if self.changedItems['highlight'].has_key(themeName): + if themeName in self.changedItems['highlight']: del(self.changedItems['highlight'][themeName]) #write changes idleConf.userCfg['highlight'].Save() @@ -870,9 +871,9 @@ class ConfigDialog(Toplevel): #handle any unsaved changes to this theme if theme in self.changedItems['highlight'].keys(): themeDict=self.changedItems['highlight'][theme] - if themeDict.has_key(element+'-foreground'): + if element+'-foreground' in themeDict: colours['foreground']=themeDict[element+'-foreground'] - if themeDict.has_key(element+'-background'): + if element+'-background' in themeDict: colours['background']=themeDict[element+'-background'] self.textHighlightSample.tag_config(element, **colours) self.SetColourSample() diff --git a/Lib/idlelib/configHandler.py b/Lib/idlelib/configHandler.py index b00bdcf39d..73487d56f3 100644 --- a/Lib/idlelib/configHandler.py +++ b/Lib/idlelib/configHandler.py @@ -246,7 +246,7 @@ class IdleConf: else: #returning default, print warning if warn_on_default: warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n' - ' problem retrieving configration option %r\n' + ' problem retrieving configuration option %r\n' ' from section %r.\n' ' returning default value: %r\n' % (option, section, default)) diff --git a/Lib/idlelib/extend.txt b/Lib/idlelib/extend.txt index f5fb3e0409..165e044b5d 100644 --- a/Lib/idlelib/extend.txt +++ b/Lib/idlelib/extend.txt @@ -18,7 +18,7 @@ window. An IDLE extension class is instantiated with a single argument, `editwin', an EditorWindow instance. The extension cannot assume much -about this argument, but it is guarateed to have the following instance +about this argument, but it is guaranteed to have the following instance variables: text a Text instance (a widget) diff --git a/Lib/idlelib/idle.bat b/Lib/idlelib/idle.bat index c1b5fd28ac..e77b96e9b5 100755 --- a/Lib/idlelib/idle.bat +++ b/Lib/idlelib/idle.bat @@ -1,3 +1,4 @@ @echo off -rem Working IDLE bat for Windows - uses start instead of absolute pathname -start idle.pyw %1 %2 %3 %4 %5 %6 %7 %8 %9 +rem Start IDLE using the appropriate Python interpreter +set CURRDIR=%~dp0 +start "IDLE" "%CURRDIR%..\..\pythonw.exe" "%CURRDIR%idle.pyw" %1 %2 %3 %4 %5 %6 %7 %8 %9 diff --git a/Lib/idlelib/idle.py b/Lib/idlelib/idle.py index 537dd5a9a7..a249557dd1 100644 --- a/Lib/idlelib/idle.py +++ b/Lib/idlelib/idle.py @@ -1,21 +1,11 @@ -try: - import idlelib.PyShell -except ImportError: - # IDLE is not installed, but maybe PyShell is on sys.path: - try: - import PyShell - except ImportError: - raise - else: - import os - idledir = os.path.dirname(os.path.abspath(PyShell.__file__)) - if idledir != os.getcwd(): - # We're not in the IDLE directory, help the subprocess find run.py - pypath = os.environ.get('PYTHONPATH', '') - if pypath: - os.environ['PYTHONPATH'] = pypath + ':' + idledir - else: - os.environ['PYTHONPATH'] = idledir - PyShell.main() -else: - idlelib.PyShell.main() +import os.path +import sys + +# If we are working on a development version of IDLE, we need to prepend the +# parent of this idlelib dir to sys.path. Otherwise, importing idlelib gets +# the version installed with the Python used to call this module: +idlelib_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(0, idlelib_dir) + +import idlelib.PyShell +idlelib.PyShell.main() diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py index 89352d7d3b..3f8bcffe99 100644 --- a/Lib/idlelib/idlever.py +++ b/Lib/idlelib/idlever.py @@ -1 +1 @@ -IDLE_VERSION = "2.6.7" +IDLE_VERSION = "2.7.2" diff --git a/Lib/idlelib/macosxSupport.py b/Lib/idlelib/macosxSupport.py index 7ef56f3440..4be60a3601 100644 --- a/Lib/idlelib/macosxSupport.py +++ b/Lib/idlelib/macosxSupport.py @@ -4,19 +4,57 @@ GUI application (as opposed to an X11 application). """ import sys import Tkinter +from os import path + + +_appbundle = None def runningAsOSXApp(): """ Returns True if Python is running from within an app on OSX. If so, assume that Python was built with Aqua Tcl/Tk rather than - X11 Tck/Tk. + X11 Tcl/Tk. + """ + 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). """ - return (sys.platform == 'darwin' and '.app' in sys.executable) + 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): """ - This ensures that the application will respont to open AppleEvents, which - makes is feaseable to use IDLE as the default application for python files. + This ensures that the application will respond to open AppleEvents, which + makes is feasible to use IDLE as the default application for python files. """ def doOpenFile(*args): for fn in args: @@ -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') @@ -85,41 +120,45 @@ 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) - - ###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', [ + # The binding above doesn't reliably work on all versions of Tk + # on MacOSX. Adding command definition below does seem to do the + # right thing for now. + root.createcommand('exit', flist.close_all_callback) + + 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 3bac6a30b3..13950589a0 100644 --- a/Lib/idlelib/rpc.py +++ b/Lib/idlelib/rpc.py @@ -169,7 +169,7 @@ class SocketIO(object): how, (oid, methodname, args, kwargs) = request except TypeError: return ("ERROR", "Bad request format") - if not self.objtable.has_key(oid): + if oid not in self.objtable: return ("ERROR", "Unknown object id: %r" % (oid,)) obj = self.objtable[oid] if methodname == "__methods__": @@ -304,7 +304,7 @@ class SocketIO(object): # wait for notification from socket handling thread cvar = self.cvars[myseq] cvar.acquire() - while not self.responses.has_key(myseq): + while myseq not in self.responses: cvar.wait() response = self.responses[myseq] self.debug("_getresponse:%s: thread woke up: response: %s" % @@ -518,8 +518,6 @@ class RPCClient(SocketIO): def __init__(self, address, family=socket.AF_INET, type=socket.SOCK_STREAM): self.listening_sock = socket.socket(family, type) - self.listening_sock.setsockopt(socket.SOL_SOCKET, - socket.SO_REUSEADDR, 1) self.listening_sock.bind(address) self.listening_sock.listen(1) @@ -552,7 +550,7 @@ class RPCProxy(object): return MethodProxy(self.sockio, self.oid, name) if self.__attributes is None: self.__getattributes() - if self.__attributes.has_key(name): + if name in self.__attributes: value = self.sockio.remotecall(self.oid, '__getattribute__', (name,), {}) return value @@ -572,7 +570,7 @@ def _getmethods(obj, methods): # Adds names to dictionary argument 'methods' for name in dir(obj): attr = getattr(obj, name) - if callable(attr): + if hasattr(attr, '__call__'): methods[name] = 1 if type(obj) == types.InstanceType: _getmethods(obj.__class__, methods) @@ -583,7 +581,7 @@ def _getmethods(obj, methods): def _getattributes(obj, attributes): for name in dir(obj): attr = getattr(obj, name) - if not callable(attr): + if not hasattr(attr, '__call__'): attributes[name] = 1 class MethodProxy(object): @@ -599,4 +597,4 @@ class MethodProxy(object): # XXX KBK 09Sep03 We need a proper unit test for this module. Previously -# existing test code was removed at Rev 1.27. +# existing test code was removed at Rev 1.27 (r34098). diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index 270ea15d6d..642b979d8b 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) @@ -67,10 +68,13 @@ def main(del_exitfunc=False): global quitting global no_exitfunc no_exitfunc = del_exitfunc - port = 8833 #time.sleep(15) # test subprocess not responding - if sys.argv[1:]: - port = int(sys.argv[1]) + try: + assert(len(sys.argv) > 1) + port = int(sys.argv[-1]) + except: + print>>sys.stderr, "IDLE Subprocess: no IP port passed in sys.argv." + return sys.argv[:] = [""] sockthread = threading.Thread(target=manage_socket, name='SockThread', diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textView.py index 0e7e663185..8937c17b6c 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,10 +59,10 @@ 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: if encoding: import codecs @@ -73,7 +75,7 @@ def view_file(parent, title, filename, encoding=None): message='Unable to load file %r .' % filename, parent=parent) else: - return view_text(parent, title, textFile.read()) + return view_text(parent, title, textFile.read(), modal) if __name__ == '__main__': @@ -83,11 +85,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() |