Index: __init__.py =================================================================== --- __init__.py (revision 85719) +++ __init__.py (working copy) @@ -1 +1,4 @@ # Dummy file to make this a package. +# Imports are relative (e.g. from . import PyShell), but +# in PyShell.py there is a specific reference to idlelib (or vidle) +# in connection with using __import__ (constructed import). Index: aboutDialog.py =================================================================== --- aboutDialog.py (revision 85719) +++ aboutDialog.py (working copy) @@ -5,8 +5,8 @@ from Tkinter import * import os -from idlelib import textView -from idlelib import idlever +from . import textView +from . import idlever class AboutDialog(Toplevel): """Modal about dialog for idle @@ -144,7 +144,7 @@ # test the dialog root = Tk() def run(): - from idlelib import aboutDialog + from . import aboutDialog aboutDialog.AboutDialog(root, 'About') Button(root, text='Dialog', command=run).pack() root.mainloop() Index: AutoComplete.py =================================================================== --- AutoComplete.py (revision 85719) +++ AutoComplete.py (working copy) @@ -7,7 +7,7 @@ import sys import string -from idlelib.configHandler import idleConf +from .configHandler import idleConf # This string includes all chars that may be in a file name (without a path # separator) @@ -18,8 +18,8 @@ # These constants represent the two different types of completions COMPLETE_ATTRIBUTES, COMPLETE_FILES = range(1, 2+1) -from idlelib import AutoCompleteWindow -from idlelib.HyperParser import HyperParser +from . import AutoCompleteWindow +from . HyperParser import HyperParser import __main__ Index: AutoCompleteWindow.py =================================================================== --- AutoCompleteWindow.py (revision 85719) +++ AutoCompleteWindow.py (working copy) @@ -2,8 +2,8 @@ An auto-completion window for IDLE, used by the AutoComplete extension """ from Tkinter import * -from idlelib.MultiCall import MC_SHIFT -from idlelib.AutoComplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES +from .MultiCall import MC_SHIFT +from .AutoComplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES HIDE_VIRTUAL_EVENT_NAME = "<>" HIDE_SEQUENCES = ("", "") Index: Bindings.py =================================================================== --- Bindings.py (revision 85719) +++ Bindings.py (working copy) @@ -9,8 +9,8 @@ """ import sys -from idlelib.configHandler import idleConf -from idlelib import macosxSupport +from .configHandler import idleConf +from . import macosxSupport menudefs = [ # underscore prefixes character to underscore Index: CallTips.py =================================================================== --- CallTips.py (revision 85719) +++ CallTips.py (working copy) @@ -8,9 +8,10 @@ import re import sys import types +import inspect -from idlelib import CallTipWindow -from idlelib.HyperParser import HyperParser +from . import CallTipWindow +from .HyperParser import HyperParser import __main__ @@ -25,28 +26,26 @@ def __init__(self, editwin=None): if editwin is None: # subprocess and test self.editwin = None - return - self.editwin = editwin - self.text = editwin.text - self.calltip = None - self._make_calltip_window = self._make_tk_calltip_window + else: + self.editwin = editwin + self.text = editwin.text + self.active_calltip = None + self._calltip_window = self._make_tk_calltip_window def close(self): - self._make_calltip_window = None + self._calltip_window = None def _make_tk_calltip_window(self): # See __init__ for usage return CallTipWindow.CallTip(self.text) def _remove_calltip_window(self, event=None): - if self.calltip: - self.calltip.hidetip() - self.calltip = None + if self.active_calltip: + self.active_calltip.hidetip() + self.active_calltip = None def force_open_calltip_event(self, event): - """Happens when the user really wants to open a CallTip, even if a - function call is needed. - """ + "The user selected the menu entry or hotkey, open the tip." self.open_calltip(True) def try_open_calltip_event(self, event): @@ -57,10 +56,7 @@ self.open_calltip(False) def refresh_calltip_event(self, event): - """If there is already a calltip window, check if it is still needed, - and if so, reload it. - """ - if self.calltip and self.calltip.is_active(): + if self.active_calltip and self.active_calltip.is_active(): self.open_calltip(False) def open_calltip(self, evalfuncs): @@ -72,20 +68,22 @@ return hp.set_index(sur_paren[0]) name = hp.get_expression() - if not name or (not evalfuncs and name.find('(') != -1): + if not name: return - arg_text = self.fetch_tip(name) - if not arg_text: + if not evalfuncs and (name.find('(') != -1): return - self.calltip = self._make_calltip_window() - self.calltip.showtip(arg_text, sur_paren[0], sur_paren[1]) + argspec = self.fetch_tip(name) + if not argspec: + return + self.active_calltip = self._calltip_window() + self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1]) def fetch_tip(self, name): - """Return the argument list and docstring of a function or class + """Return the argument list and docstring of a function or class. If there is a Python subprocess, get the calltip there. Otherwise, - either fetch_tip() is running in the subprocess itself or it was called - in an IDLE EditorWindow before any script had been run. + either this fetch_tip() is running in the subprocess or it was + called in an IDLE running without the subprocess. The subprocess environment is that of the most recently run script. If two unrelated modules are being edited some calltips in the current @@ -103,10 +101,10 @@ (name,), {}) else: entity = self.get_entity(name) - return get_arg_text(entity) + return get_argspec(entity) def get_entity(self, name): - "Lookup name in a namespace spanning sys.modules and __main.dict__" + "Lookup name in a namespace spanning sys.modules and __main.dict__." if name: namespace = sys.modules.copy() namespace.update(__main__.__dict__) @@ -116,106 +114,90 @@ return None def _find_constructor(class_ob): - # Given a class object, return a function object used for the - # constructor (ie, __init__() ) or None if we can't find one. + "Find the nearest __init__() in the class tree." try: - return class_ob.__init__.im_func + return class_ob.__init__.__func__ except AttributeError: for base in class_ob.__bases__: - rc = _find_constructor(base) - if rc is not None: return rc - return None + init = _find_constructor(base) + if init: + return init + return None -def get_arg_text(ob): - """Get a string describing the arguments for the given object""" - arg_text = "" +def get_argspec(ob): + """Get a string describing the arguments for the given object.""" + argspec = "" if ob is not None: - arg_offset = 0 - if type(ob) in (types.ClassType, types.TypeType): - # Look for the highest __init__ in the class chain. + if isinstance(ob, type): fob = _find_constructor(ob) if fob is None: fob = lambda: None - else: - arg_offset = 1 - elif type(ob)==types.MethodType: - # bit of a hack for methods - turn it into a function - # but we drop the "self" param. - fob = ob.im_func - arg_offset = 1 + elif isinstance(ob, types.MethodType): + fob = ob.__func__ else: fob = ob - # Try to build one for Python defined functions - if type(fob) in [types.FunctionType, types.LambdaType]: - argcount = fob.func_code.co_argcount - real_args = fob.func_code.co_varnames[arg_offset:argcount] - defaults = fob.func_defaults or [] - defaults = list(map(lambda name: "=%s" % repr(name), defaults)) - defaults = [""] * (len(real_args) - len(defaults)) + defaults - items = map(lambda arg, dflt: arg + dflt, real_args, defaults) - if fob.func_code.co_flags & 0x4: - items.append("...") - if fob.func_code.co_flags & 0x8: - items.append("***") - arg_text = ", ".join(items) - arg_text = "(%s)" % re.sub("\.\d+", "", arg_text) - # See if we can use the docstring + if isinstance(fob, (types.FunctionType, types.LambdaType)): + argspec = inspect.formatargspec(*inspect.getfullargspec(fob)) + pat = re.compile('self\,?\s*') + argspec = pat.sub("", argspec) doc = getattr(ob, "__doc__", "") if doc: doc = doc.lstrip() pos = doc.find("\n") if pos < 0 or pos > 70: pos = 70 - if arg_text: - arg_text += "\n" - arg_text += doc[:pos] - return arg_text + if argspec: + argspec += "\n" + argspec += doc[:pos] + return argspec ################################################# # # Test code # -if __name__=='__main__': - +def main(): def t1(): "()" def t2(a, b=None): "(a, b=None)" - def t3(a, *args): "(a, ...)" - def t4(*args): "(...)" - def t5(a, *args): "(a, ...)" - def t6(a, b=None, *args, **kw): "(a, b=None, ..., ***)" - def t7((a, b), c, (d, e)): "(, c, )" + def t3(a, *args): "(a, *args)" + def t4(*args): "(*args)" + def t5(a, *args): "(a, *args)" + def t6(a, b=None, *args, **kw): "(a, b=None, *args, **kw)" class TC(object): - "(ai=None, ...)" - def __init__(self, ai=None, *b): "(ai=None, ...)" + "(ai=None, *b)" + def __init__(self, ai=None, *b): "(ai=None, *b)" def t1(self): "()" def t2(self, ai, b=None): "(ai, b=None)" - def t3(self, ai, *args): "(ai, ...)" - def t4(self, *args): "(...)" - def t5(self, ai, *args): "(ai, ...)" - def t6(self, ai, b=None, *args, **kw): "(ai, b=None, ..., ***)" - def t7(self, (ai, b), c, (d, e)): "(, c, )" + def t3(self, ai, *args): "(ai, *args)" + def t4(self, *args): "(*args)" + def t5(self, ai, *args): "(ai, *args)" + def t6(self, ai, b=None, *args, **kw): "(ai, b=None, *args, **kw)" + __main__.__dict__.update(locals()) + def test(tests): ct = CallTips() failed=[] for t in tests: expected = t.__doc__ + "\n" + t.__doc__ name = t.__name__ - # exercise fetch_tip(), not just get_arg_text() + # exercise fetch_tip(), not just get_argspec() try: - qualified_name = "%s.%s" % (t.im_class.__name__, name) + qualified_name = "%s.%s" % (t.__self__.__class__.__name__, name) except AttributeError: qualified_name = name - arg_text = ct.fetch_tip(qualified_name) - if arg_text != expected: + argspec = ct.fetch_tip(qualified_name) + if argspec != expected: failed.append(t) fmt = "%s - expected %s, but got %s" - print fmt % (t.__name__, expected, get_arg_text(t)) - print "%d of %d tests failed" % (len(failed), len(tests)) + print(fmt % (t.__name__, expected, get_argspec(t))) + print("%d of %d tests failed" % (len(failed), len(tests))) tc = TC() - tests = (t1, t2, t3, t4, t5, t6, t7, - TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6, tc.t7) + tests = (t1, t2, t3, t4, t5, t6, + TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6) test(tests) + +if __name__ == '__main__': + main() Index: ClassBrowser.py =================================================================== --- ClassBrowser.py (revision 85719) +++ ClassBrowser.py (working copy) @@ -10,15 +10,38 @@ - add base classes to class browser tree """ +__all__ = ['ClassBrowser'] + import os import sys import pyclbr -from idlelib import PyShell -from idlelib.WindowList import ListedToplevel -from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas -from idlelib.configHandler import idleConf +from . import PyShell +from .WindowList import ListedToplevel +from .TreeWidget import TreeNode, TreeItem, ScrolledCanvas +from .configHandler import idleConf +def collect_objects(d, name=None): + items = [] + objects = {} + for key, cl in list(d.items()): + if name is None or cl.module == name: + s = key + if hasattr(cl, 'super') and cl.super: + supers = [] + for sup in cl.super: + if type(sup) is type(''): + sname = sup + else: + sname = sup.name + if sup.module != cl.module: + sname = "%s.%s" % (sup.module, sname) + supers.append(sname) + s = s + "(%s)" % ", ".join(supers) + items.append((cl.lineno, s)) + objects[s] = cl + return objects, items + class ClassBrowser: def __init__(self, flist, name, path): @@ -72,8 +95,8 @@ def GetSubList(self): sublist = [] - for name in self.listclasses(): - item = ClassBrowserTreeItem(name, self.classes, self.file) + for name in self.listobjects(): + item = ObjectBrowserTreeItem(name, self.classes, self.file) sublist.append(item) return sublist @@ -87,40 +110,20 @@ def IsExpandable(self): return os.path.normcase(self.file[-3:]) == ".py" - def listclasses(self): + def listobjects(self): dir, file = os.path.split(self.file) name, ext = os.path.splitext(file) if os.path.normcase(ext) != ".py": return [] try: dict = pyclbr.readmodule_ex(name, [dir] + sys.path) - except ImportError, msg: + except ImportError as msg: return [] - items = [] - self.classes = {} - for key, cl in dict.items(): - if cl.module == name: - s = key - if hasattr(cl, 'super') and cl.super: - supers = [] - for sup in cl.super: - if type(sup) is type(''): - sname = sup - else: - sname = sup.name - if sup.module != cl.module: - sname = "%s.%s" % (sup.module, sname) - supers.append(sname) - s = s + "(%s)" % ", ".join(supers) - items.append((cl.lineno, s)) - self.classes[s] = cl + self.classes, items = collect_objects(dict, name) items.sort() - list = [] - for item, s in items: - list.append(s) - return list + return [s for item, s in items] -class ClassBrowserTreeItem(TreeItem): +class ObjectBrowserTreeItem(TreeItem): def __init__(self, name, classes, file): self.name = name @@ -147,7 +150,7 @@ def IsExpandable(self): if self.cl: try: - return not not self.cl.methods + return not not self.cl.objects except AttributeError: return False @@ -155,8 +158,9 @@ if not self.cl: return [] sublist = [] - for name in self.listmethods(): - item = MethodBrowserTreeItem(name, self.cl, self.file) + for obj in self.listobjects(): + classes, item_name = obj + item = ObjectBrowserTreeItem(item_name, classes, self.file) sublist.append(item) return sublist @@ -168,40 +172,16 @@ lineno = self.cl.lineno edit.gotoline(lineno) - def listmethods(self): + def listobjects(self): if not self.cl: return [] - items = [] - for name, lineno in self.cl.methods.items(): - items.append((lineno, name)) - items.sort() - list = [] - for item, name in items: - list.append(name) - return list + result = [] + for name, ob in list(self.cl.objects.items()): + classes, items = collect_objects({name:ob}) + result.append((ob.lineno, classes, items[0][1])) + result.sort() + return [item[1:] for item in result] -class MethodBrowserTreeItem(TreeItem): - - def __init__(self, name, cl, file): - self.name = name - self.cl = cl - self.file = file - - def GetText(self): - return "def " + self.name + "(...)" - - def GetIconName(self): - return "python" # XXX - - def IsExpandable(self): - return 0 - - def OnDoubleClick(self): - if not os.path.exists(self.file): - return - edit = PyShell.flist.open(self.file) - edit.gotoline(self.cl.methods[self.name]) - def main(): try: file = __file__ Index: CodeContext.py =================================================================== --- CodeContext.py (revision 85719) +++ CodeContext.py (working copy) @@ -12,8 +12,8 @@ import Tkinter from Tkconstants import TOP, LEFT, X, W, SUNKEN import re -from sys import maxint as INFINITY -from idlelib.configHandler import idleConf +from sys import maxsize as INFINITY +from .configHandler import idleConf BLOCKOPENERS = set(["class", "def", "elif", "else", "except", "finally", "for", "if", "try", "while", "with"]) @@ -117,7 +117,7 @@ lastindent = INFINITY # For a line to be interesting, it must begin with a block opening # keyword, and have less indentation than lastindent. - for linenum in xrange(new_topvisible, stopline-1, -1): + for linenum in range(new_topvisible, stopline-1, -1): indent, text, opener = self.get_line_info(linenum) if indent < lastindent: lastindent = indent Index: ColorDelegator.py =================================================================== --- ColorDelegator.py (revision 85719) +++ ColorDelegator.py (working copy) @@ -3,8 +3,8 @@ import keyword import __builtin__ from Tkinter import * -from idlelib.Delegator import Delegator -from idlelib.configHandler import idleConf +from .Delegator import Delegator +from .configHandler import idleConf DEBUG = False @@ -16,7 +16,7 @@ kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b" builtinlist = [str(name) for name in dir(__builtin__) if not name.startswith('_')] - # self.file = file("file") : + # 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]*"]) @@ -72,7 +72,7 @@ "hit": idleConf.GetHighlight(theme, "hit"), } - if DEBUG: print 'tagdefs',self.tagdefs + if DEBUG: print('tagdefs',self.tagdefs) def insert(self, index, chars, tags=None): index = self.index(index) @@ -91,13 +91,13 @@ def notify_range(self, index1, index2=None): self.tag_add("TODO", index1, index2) if self.after_id: - if DEBUG: print "colorizing already scheduled" + if DEBUG: print("colorizing already scheduled") return if self.colorizing: self.stop_colorizing = True - if DEBUG: print "stop colorizing" + if DEBUG: print("stop colorizing") if self.allow_colorizing: - if DEBUG: print "schedule colorizing" + if DEBUG: print("schedule colorizing") self.after_id = self.after(1, self.recolorize) close_when_done = None # Window to be closed when done colorizing @@ -106,7 +106,7 @@ if self.after_id: after_id = self.after_id self.after_id = None - if DEBUG: print "cancel scheduled recolorizer" + if DEBUG: print("cancel scheduled recolorizer") self.after_cancel(after_id) self.allow_colorizing = False self.stop_colorizing = True @@ -120,42 +120,42 @@ if self.after_id: after_id = self.after_id self.after_id = None - if DEBUG: print "cancel scheduled recolorizer" + if DEBUG: print("cancel scheduled recolorizer") self.after_cancel(after_id) if self.allow_colorizing and self.colorizing: - if DEBUG: print "stop colorizing" + if DEBUG: print("stop colorizing") self.stop_colorizing = True self.allow_colorizing = not self.allow_colorizing if self.allow_colorizing and not self.colorizing: self.after_id = self.after(1, self.recolorize) if DEBUG: - print "auto colorizing turned",\ - self.allow_colorizing and "on" or "off" + print("auto colorizing turned",\ + self.allow_colorizing and "on" or "off") return "break" def recolorize(self): self.after_id = None if not self.delegate: - if DEBUG: print "no delegate" + if DEBUG: print("no delegate") return if not self.allow_colorizing: - if DEBUG: print "auto colorizing is off" + if DEBUG: print("auto colorizing is off") return if self.colorizing: - if DEBUG: print "already colorizing" + if DEBUG: print("already colorizing") return try: self.stop_colorizing = False self.colorizing = True - if DEBUG: print "colorizing..." + if DEBUG: print("colorizing...") t0 = time.clock() self.recolorize_main() t1 = time.clock() - if DEBUG: print "%.3f seconds" % (t1-t0) + if DEBUG: print("%.3f seconds" % (t1-t0)) finally: self.colorizing = False if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"): - if DEBUG: print "reschedule colorizing" + if DEBUG: print("reschedule colorizing") self.after_id = self.after(1, self.recolorize) if self.close_when_done: top = self.close_when_done @@ -190,7 +190,7 @@ ##print head, "get", mark, next, "->", repr(line) if not line: return - for tag in self.tagdefs.keys(): + for tag in self.tagdefs: self.tag_remove(tag, mark, next) chars = chars + line m = self.prog.search(chars) @@ -240,15 +240,15 @@ self.tag_add("TODO", next) self.update() if self.stop_colorizing: - if DEBUG: print "colorizing stopped" + if DEBUG: print("colorizing stopped") return def removecolors(self): - for tag in self.tagdefs.keys(): + for tag in self.tagdefs: self.tag_remove(tag, "1.0", "end") def main(): - from idlelib.Percolator import Percolator + from .Percolator import Percolator root = Tk() root.wm_protocol("WM_DELETE_WINDOW", root.quit) text = Text(background="white") Index: config-extensions.def =================================================================== --- config-extensions.def (revision 85719) +++ config-extensions.def (working copy) @@ -29,6 +29,12 @@ # See config-keys.def for notes on specifying keys and extend.txt for # information on creating IDLE extensions. +[FileRevert] +enable=1 +enable_shell=0 +[FileRevert_cfgBindings] +revert-file= + [FormatParagraph] enable=1 [FormatParagraph_cfgBindings] @@ -46,6 +52,7 @@ [ScriptBinding] enable=1 +enable_shell=0 [ScriptBinding_cfgBindings] run-module= check-module= Index: config-main.def =================================================================== --- config-main.def (revision 85719) +++ config-main.def (working copy) @@ -18,6 +18,9 @@ # ~/.idlerc/config-highlight.cfg the user highlighting config file # ~/.idlerc/config-keys.cfg the user keybinding config file # +# On Windows 7 the .idlerc directory is at +# C:\Users\username\.idlerc +# # On Windows2000 and Windows XP the .idlerc directory is at # Documents and Settings\\.idlerc # @@ -46,6 +49,8 @@ [General] editor-on-startup= 0 autosave= 0 +save-before-run= 1 +signal-first-error= 1 print-command-posix=lpr %s print-command-win=start /min notepad /p %s delete-exitfunc= 1 Index: configDialog.py =================================================================== --- configDialog.py (revision 85719) +++ configDialog.py (working copy) @@ -10,16 +10,17 @@ """ from Tkinter import * -import tkMessageBox, tkColorChooser, tkFont -import string +import tkMessageBox +import tkColorChooser +import tkFont -from idlelib.configHandler import idleConf -from idlelib.dynOptionMenuWidget import DynOptionMenu -from idlelib.tabbedpages import TabbedPageSet -from idlelib.keybindingDialog import GetKeysDialog -from idlelib.configSectionNameDialog import GetCfgSectionNameDialog -from idlelib.configHelpSourceEdit import GetHelpSourceDialog -from idlelib import macosxSupport +from .configHandler import idleConf +from .dynOptionMenuWidget import DynOptionMenu +from .tabbedpages import TabbedPageSet +from .keybindingDialog import GetKeysDialog +from .configSectionNameDialog import GetCfgSectionNameDialog +from .configHelpSourceEdit import GetHelpSourceDialog +from . import macosxSupport class ConfigDialog(Toplevel): @@ -71,25 +72,27 @@ page_names=['Fonts/Tabs','Highlighting','Keys','General']) frameActionButtons = Frame(self,pady=2) #action buttons + if macosxSupport.runningAsOSXApp(): - # Changing the default padding on OSX results in unreadable - # text in the buttons - paddingArgs={} + # Surpress the padx and pady arguments when + # running as IDLE.app, otherwise the text + # on these buttons will not be readable. + extraKwds={} else: - paddingArgs={'padx':6, 'pady':3} + extraKwds=dict(padx=6, pady=3) self.buttonHelp = Button(frameActionButtons,text='Help', command=self.Help,takefocus=FALSE, - **paddingArgs) + **extraKwds) self.buttonOk = Button(frameActionButtons,text='Ok', command=self.Ok,takefocus=FALSE, - **paddingArgs) + **extraKwds) self.buttonApply = Button(frameActionButtons,text='Apply', command=self.Apply,takefocus=FALSE, - **paddingArgs) + **extraKwds) self.buttonCancel = Button(frameActionButtons,text='Cancel', command=self.Cancel,takefocus=FALSE, - **paddingArgs) + **extraKwds) self.CreatePageFontTab() self.CreatePageHighlight() self.CreatePageKeys() @@ -201,7 +204,7 @@ (' ','normal'),('stderr','stderr'),('\n','normal')) for txTa in textAndTags: text.insert(END,txTa[0],txTa[1]) - for element in self.themeElements.keys(): + for element in self.themeElements: text.tag_bind(self.themeElements[element][0],'', lambda event,elem=element: event.widget.winfo_toplevel() .highlightTarget.set(elem)) @@ -330,6 +333,8 @@ self.paraWidth=StringVar(self) self.startupEdit=IntVar(self) self.autoSave=IntVar(self) + self.saveBeforeRun=BooleanVar(self) + self.signalOnErr = BooleanVar(self) self.encoding=StringVar(self) self.userHelpBrowser=BooleanVar(self) self.helpBrowser=StringVar(self) @@ -337,27 +342,38 @@ #body frame=self.tabPages.pages['General'].frame #body section frames + frameStartup=LabelFrame(frame,borderwidth=2,relief=GROOVE, + text=' Startup Preferences ') frameRun=LabelFrame(frame,borderwidth=2,relief=GROOVE, - text=' Startup Preferences ') - frameSave=LabelFrame(frame,borderwidth=2,relief=GROOVE, - text=' Autosave Preferences ') + text=' Run (F5) Preferences ') frameWinSize=Frame(frame,borderwidth=2,relief=GROOVE) frameParaSize=Frame(frame,borderwidth=2,relief=GROOVE) - frameEncoding=Frame(frame,borderwidth=2,relief=GROOVE) frameHelp=LabelFrame(frame,borderwidth=2,relief=GROOVE, text=' Additional Help Sources ') - #frameRun - labelRunChoiceTitle=Label(frameRun,text='At Startup') - radioStartupEdit=Radiobutton(frameRun,variable=self.startupEdit, + #frameStartup + labelRunChoiceTitle=Label(frameStartup,text='At Startup') + radioStartupEdit=Radiobutton(frameStartup,variable=self.startupEdit, value=1,command=self.SetKeysType,text="Open Edit Window") - radioStartupShell=Radiobutton(frameRun,variable=self.startupEdit, + radioStartupShell=Radiobutton(frameStartup,variable=self.startupEdit, value=0,command=self.SetKeysType,text='Open Shell Window') - #frameSave - labelRunSaveTitle=Label(frameSave,text='At Start of Run (F5) ') - radioSaveAsk=Radiobutton(frameSave,variable=self.autoSave, - value=0,command=self.SetKeysType,text="Prompt to Save") - radioSaveAuto=Radiobutton(frameSave,variable=self.autoSave, - value=1,command=self.SetKeysType,text='No Prompt') + #frameRun + labelSaveBeforeRun=Label(frameRun, + text='If file has never been saved ') + radioSaveBefore=Radiobutton(frameRun,variable=self.saveBeforeRun, + value=1,text="Prompt to Save") + radioSaveToTemp=Radiobutton(frameRun,variable=self.saveBeforeRun, + value=0,text="No prompt") + labelAutoSave=Label(frameRun, text='If file has been saved before ') + radioSaveAsk=Radiobutton(frameRun,variable=self.autoSave, + value=0,text="Prompt to Save") + radioSaveAuto=Radiobutton(frameRun,variable=self.autoSave, + value=1,text="No prompt") + labelSignalOnErr = Label(frameRun, + text="On first error") + signalErr = Radiobutton(frameRun, variable=self.signalOnErr, + value=1, text="Bring shell forward") + noSignalErr = Radiobutton(frameRun, variable=self.signalOnErr, + value=0, text="Do nothing") #frameWinSize labelWinSizeTitle=Label(frameWinSize,text='Initial Window Size'+ ' (in characters)') @@ -372,14 +388,6 @@ ' width (in characters)') entryParaWidth=Entry(frameParaSize,textvariable=self.paraWidth, width=3) - #frameEncoding - labelEncodingTitle=Label(frameEncoding,text="Default Source Encoding") - radioEncLocale=Radiobutton(frameEncoding,variable=self.encoding, - value="locale",text="Locale-defined") - radioEncUTF8=Radiobutton(frameEncoding,variable=self.encoding, - value="utf-8",text="UTF-8") - radioEncNone=Radiobutton(frameEncoding,variable=self.encoding, - value="none",text="None") #frameHelp frameHelpList=Frame(frameHelp) frameHelpListButtons=Frame(frameHelpList) @@ -397,20 +405,26 @@ state=DISABLED,width=8,command=self.HelpListItemRemove) #widget packing #body + frameStartup.pack(side=TOP,padx=5,pady=5,fill=X) frameRun.pack(side=TOP,padx=5,pady=5,fill=X) - frameSave.pack(side=TOP,padx=5,pady=5,fill=X) frameWinSize.pack(side=TOP,padx=5,pady=5,fill=X) frameParaSize.pack(side=TOP,padx=5,pady=5,fill=X) - frameEncoding.pack(side=TOP,padx=5,pady=5,fill=X) frameHelp.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH) - #frameRun + #frameStartup labelRunChoiceTitle.pack(side=LEFT,anchor=W,padx=5,pady=5) radioStartupShell.pack(side=RIGHT,anchor=W,padx=5,pady=5) radioStartupEdit.pack(side=RIGHT,anchor=W,padx=5,pady=5) - #frameSave - labelRunSaveTitle.pack(side=LEFT,anchor=W,padx=5,pady=5) - radioSaveAuto.pack(side=RIGHT,anchor=W,padx=5,pady=5) - radioSaveAsk.pack(side=RIGHT,anchor=W,padx=5,pady=5) + #frameRun + commonOpts = {'sticky': W, 'padx': 5, 'pady': 5} + labelSaveBeforeRun.grid(row=0, column=0, **commonOpts) + radioSaveBefore.grid(row=0, column=1, **commonOpts) + radioSaveToTemp.grid(row=0, column=2, **commonOpts) + labelAutoSave.grid(row=1, column=0, **commonOpts) + radioSaveAsk.grid(row=1, column=1, **commonOpts) + radioSaveAuto.grid(row=1, column=2, **commonOpts) + labelSignalOnErr.grid(row=2, column=0, **commonOpts) + signalErr.grid(row=2, column=1, **commonOpts) + noSignalErr.grid(row=2, column=2, **commonOpts) #frameWinSize labelWinSizeTitle.pack(side=LEFT,anchor=W,padx=5,pady=5) entryWinHeight.pack(side=RIGHT,anchor=E,padx=10,pady=5) @@ -420,11 +434,6 @@ #paragraphFormatWidth labelParaWidthTitle.pack(side=LEFT,anchor=W,padx=5,pady=5) entryParaWidth.pack(side=RIGHT,anchor=E,padx=10,pady=5) - #frameEncoding - labelEncodingTitle.pack(side=LEFT,anchor=W,padx=5,pady=5) - radioEncNone.pack(side=RIGHT,anchor=E,pady=5) - radioEncUTF8.pack(side=RIGHT,anchor=E,pady=5) - radioEncLocale.pack(side=RIGHT,anchor=E,pady=5) #frameHelp frameHelpListButtons.pack(side=RIGHT,padx=5,pady=5,fill=Y) frameHelpList.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH) @@ -455,6 +464,8 @@ self.startupEdit.trace_variable('w',self.VarChanged_startupEdit) self.autoSave.trace_variable('w',self.VarChanged_autoSave) self.encoding.trace_variable('w',self.VarChanged_encoding) + self.saveBeforeRun.trace_variable('w', self.VarChanged_saveBeforeRun) + self.signalOnErr.trace_variable('w', self.VarChanged_signalOnErr) def VarChanged_fontSize(self,*params): value=self.fontSize.get() @@ -552,6 +563,14 @@ value=self.encoding.get() self.AddChangedItem('main','EditorWindow','encoding',value) + def VarChanged_saveBeforeRun(self,*params): + value = self.saveBeforeRun.get() + self.AddChangedItem('main','General','save-before-run',value) + + def VarChanged_signalOnErr(self,*params): + value = self.signalOnErr.get() + self.AddChangedItem('main','General','signal-first-error',value) + def ResetChangedItems(self): #When any config item is changed in this dialog, an entry #should be made in the relevant section (config type) of this @@ -568,7 +587,7 @@ def GetDefaultItems(self): dItems={'main':{},'highlight':{},'keys':{},'extensions':{}} - for configType in dItems.keys(): + for configType in dItems: sections=idleConf.GetSectionList('default',configType) for section in sections: dItems[configType][section]={} @@ -609,11 +628,11 @@ else: currentKeySetName=self.customKeys.get() currentBindings=idleConf.GetCurrentKeySet() - if currentKeySetName in self.changedItems['keys'].keys(): #unsaved changes + if currentKeySetName in self.changedItems['keys']: #unsaved changes keySetChanges=self.changedItems['keys'][currentKeySetName] - for event in keySetChanges.keys(): + for event in keySetChanges: currentBindings[event]=keySetChanges[event].split() - currentKeySequences=currentBindings.values() + currentKeySequences = list(currentBindings.values()) newKeys=GetKeysDialog(self,'Get New Keys',bindName, currentKeySequences).result if newKeys: #new keys were specified @@ -660,14 +679,14 @@ prevKeySetName=self.customKeys.get() prevKeys=idleConf.GetCoreKeys(prevKeySetName) newKeys={} - for event in prevKeys.keys(): #add key set to changed items + for event in prevKeys: #add key set to changed items eventName=event[2:-2] #trim off the angle brackets - binding=string.join(prevKeys[event]) + binding=' '.join(prevKeys[event]) newKeys[eventName]=binding #handle any unsaved changes to prev key set - if prevKeySetName in self.changedItems['keys'].keys(): + if prevKeySetName in self.changedItems['keys']: keySetChanges=self.changedItems['keys'][prevKeySetName] - for event in keySetChanges.keys(): + for event in keySetChanges: newKeys[event]=keySetChanges[event] #save the new theme self.SaveNewKeySet(newKeySetName,newKeys) @@ -685,15 +704,15 @@ reselect=1 listIndex=self.listBindings.index(ANCHOR) keySet=idleConf.GetKeySet(keySetName) - bindNames=keySet.keys() + bindNames = list(keySet.keys()) bindNames.sort() self.listBindings.delete(0,END) for bindName in bindNames: - key=string.join(keySet[bindName]) #make key(s) into a string + key=' '.join(keySet[bindName]) #make key(s) into a string bindName=bindName[2:-2] #trim off the angle brackets - if keySetName in self.changedItems['keys'].keys(): + if keySetName in self.changedItems['keys']: #handle any unsaved changes to this key set - if bindName in self.changedItems['keys'][keySetName].keys(): + if bindName in self.changedItems['keys'][keySetName]: key=self.changedItems['keys'][keySetName][bindName] self.listBindings.insert(END, bindName+' - '+key) if reselect: @@ -707,6 +726,12 @@ 'to delete the key set %r ?' % (keySetName), parent=self): return + #revert to default key set + self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys','default')) + self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys','name')) + #user can't back out of these changes, they must be applied now + self.Apply() + self.SetKeysType() #remove key set from config idleConf.userCfg['keys'].remove_section(keySetName) if keySetName in self.changedItems['keys']: @@ -721,12 +746,6 @@ self.optMenuKeysCustom.SetMenu(itemList,'- no custom keys -') else: self.optMenuKeysCustom.SetMenu(itemList,itemList[0]) - #revert to default key set - self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys','default')) - self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys','name')) - #user can't back out of these changes, they must be applied now - self.Apply() - self.SetKeysType() def DeleteCustomTheme(self): themeName=self.customTheme.get() @@ -808,9 +827,9 @@ themeName=self.customTheme.get() newTheme=idleConf.GetThemeDict(themeType,themeName) #apply any of the old theme's unsaved changes to the new theme - if themeName in self.changedItems['highlight'].keys(): + if themeName in self.changedItems['highlight']: themeChanges=self.changedItems['highlight'][themeName] - for element in themeChanges.keys(): + for element in themeChanges: newTheme[element]=themeChanges[element] #save the new theme self.SaveNewTheme(newThemeName,newTheme) @@ -862,14 +881,14 @@ theme=self.builtinTheme.get() else: #a user theme theme=self.customTheme.get() - for elementTitle in self.themeElements.keys(): + for elementTitle in self.themeElements: element=self.themeElements[elementTitle][0] colours=idleConf.GetHighlight(theme,element) if element=='cursor': #cursor sample needs special painting colours['background']=idleConf.GetHighlight(theme, 'normal', fgBg='bg') #handle any unsaved changes to this theme - if theme in self.changedItems['highlight'].keys(): + if theme in self.changedItems['highlight']: themeDict=self.changedItems['highlight'][theme] if element+'-foreground' in themeDict: colours['foreground']=themeDict[element+'-foreground'] @@ -926,7 +945,7 @@ self.changedItems['main']['HelpFiles'] = {} for num in range(1,len(self.userHelpList)+1): self.AddChangedItem('main','HelpFiles',str(num), - string.join(self.userHelpList[num-1][:2],';')) + ';'.join(self.userHelpList[num-1][:2])) def LoadFontCfg(self): ##base editor font selection list @@ -988,7 +1007,7 @@ self.optMenuThemeBuiltin.SetMenu(itemList,itemList[0]) self.SetThemeType() ##load theme element option menu - themeNames=self.themeElements.keys() + themeNames = list(self.themeElements.keys()) themeNames.sort(key=lambda x: self.themeElements[x][1]) self.optMenuHighlightTarget.SetMenu(themeNames,themeNames[0]) self.PaintThemeSample() @@ -1031,6 +1050,12 @@ #autosave state self.autoSave.set(idleConf.GetOption('main', 'General', 'autosave', default=0, type='bool')) + # save before run + self.saveBeforeRun.set(idleConf.GetOption('main', 'General', + 'save-before-run', default=1, type='bool')) + # bring shell forward on first error + self.signalOnErr.set(idleConf.GetOption('main', 'General', + 'signal-first-error', default=1, type='bool')) #initial window size self.winWidth.set(idleConf.GetOption('main','EditorWindow','width')) self.winHeight.set(idleConf.GetOption('main','EditorWindow','height')) @@ -1068,7 +1093,7 @@ """ if not idleConf.userCfg['keys'].has_section(keySetName): idleConf.userCfg['keys'].add_section(keySetName) - for event in keySet.keys(): + for event in keySet: value=keySet[event] idleConf.userCfg['keys'].SetOption(keySetName,event,value) @@ -1080,7 +1105,7 @@ """ if not idleConf.userCfg['highlight'].has_section(themeName): idleConf.userCfg['highlight'].add_section(themeName) - for element in theme.keys(): + for element in theme: value=theme[element] idleConf.userCfg['highlight'].SetOption(themeName,element,value) @@ -1095,14 +1120,14 @@ def SaveAllChangedConfigs(self): "Save configuration changes to the user config file." idleConf.userCfg['main'].Save() - for configType in self.changedItems.keys(): + for configType in self.changedItems: cfgTypeHasChanges = False - for section in self.changedItems[configType].keys(): + for section in self.changedItems[configType]: if section == 'HelpFiles': #this section gets completely replaced idleConf.userCfg['main'].remove_section('HelpFiles') cfgTypeHasChanges = True - for item in self.changedItems[configType][section].keys(): + for item in self.changedItems[configType][section]: value = self.changedItems[configType][section][item] if self.SetUserValue(configType,section,item,value): cfgTypeHasChanges = True @@ -1116,13 +1141,13 @@ def DeactivateCurrentConfig(self): #Before a config is saved, some cleanup of current #config must be done - remove the previous keybindings - winInstances=self.parent.instance_dict.keys() + winInstances = self.parent.instance_dict.keys() for instance in winInstances: instance.RemoveKeybindings() def ActivateConfigChanges(self): "Dynamically apply configuration changes" - winInstances=self.parent.instance_dict.keys() + winInstances = self.parent.instance_dict.keys() for instance in winInstances: instance.ResetColorizer() instance.ResetFont() Index: configHandler.py =================================================================== --- configHandler.py (revision 85719) +++ configHandler.py (working copy) @@ -19,8 +19,8 @@ """ import os import sys -import string -from idlelib import macosxSupport + +from . import macosxSupport from ConfigParser import ConfigParser, NoOptionError, NoSectionError class InvalidConfigType(Exception): pass @@ -270,13 +270,13 @@ configType must be one of ('main','extensions','highlight','keys') """ if not (configType in ('main','extensions','highlight','keys')): - raise InvalidConfigType, 'Invalid configType specified' + raise InvalidConfigType('Invalid configType specified') if configSet == 'user': cfgParser=self.userCfg[configType] elif configSet == 'default': cfgParser=self.defaultCfg[configType] else: - raise InvalidConfigSet, 'Invalid configSet specified' + raise InvalidConfigSet('Invalid configSet specified') return cfgParser.sections() def GetHighlight(self, theme, element, fgBg=None): @@ -304,7 +304,7 @@ if fgBg == 'bg': return highlight["background"] else: - raise InvalidFgBg, 'Invalid fgBg specified' + raise InvalidFgBg('Invalid fgBg specified') def GetThemeDict(self,type,themeName): """ @@ -320,7 +320,7 @@ elif type == 'default': cfgParser=self.defaultCfg['highlight'] else: - raise InvalidTheme, 'Invalid theme type specified' + raise InvalidTheme('Invalid theme type specified') #foreground and background values are provded for each theme element #(apart from cursor) even though all these values are not yet used #by idle, to allow for their use in the future. Default values are @@ -354,7 +354,7 @@ 'stderr-background':'#ffffff', 'console-foreground':'#000000', 'console-background':'#ffffff' } - for element in theme.keys(): + for element in theme: if not cfgParser.has_option(themeName,element): #we are going to return a default, print warning warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict' @@ -438,7 +438,7 @@ extName=None vEvent='<<'+virtualEvent+'>>' for extn in self.GetExtensions(active_only=0): - for event in self.GetExtensionKeys(extn).keys(): + for event in self.GetExtensionKeys(extn): if event == vEvent: extName=extn return extName @@ -533,7 +533,7 @@ for extn in activeExtns: extKeys=self.__GetRawExtensionKeys(extn) if extKeys: #the extension defines keybindings - for event in extKeys.keys(): + for event in extKeys: if extKeys[event] in keySet.values(): #the binding is already in use extKeys[event]='' #disable this binding @@ -546,7 +546,7 @@ virtualEvent - string, name of the virtual event to test for, without the enclosing '<< >>' """ - return ('<<'+virtualEvent+'>>') in self.GetCoreKeys().keys() + return ('<<'+virtualEvent+'>>') in self.GetCoreKeys() def GetCoreKeys(self, keySetName=None): """ @@ -609,7 +609,7 @@ '<>': [''] } if keySetName: - for event in keyBindings.keys(): + for event in keyBindings: binding=self.GetKeyBinding(keySetName,event) if binding: keyBindings[event]=binding @@ -641,7 +641,7 @@ elif configSet=='default': cfgParser=self.defaultCfg['main'] else: - raise InvalidConfigSet, 'Invalid configSet specified' + raise InvalidConfigSet('Invalid configSet specified') options=cfgParser.GetOptionList('HelpFiles') for option in options: value=cfgParser.Get('HelpFiles',option,default=';') @@ -649,12 +649,12 @@ menuItem='' #make these empty helpPath='' #so value won't be added to list else: #config entry contains ';' as expected - value=string.split(value,';') + value=value.split(';') menuItem=value[0].strip() helpPath=value[1].strip() if menuItem and helpPath: #neither are empty strings helpSources.append( (menuItem,helpPath,option) ) - helpSources.sort(key=lambda x: int(x[2])) + helpSources.sort(key=lambda x: x[2]) return helpSources def GetAllExtraHelpSourcesList(self): @@ -671,7 +671,7 @@ """ load all configuration files. """ - for key in self.defaultCfg.keys(): + for key in self.defaultCfg: self.defaultCfg[key].Load() self.userCfg[key].Load() #same keys @@ -679,7 +679,7 @@ """ write all loaded user configuration files back to disk """ - for key in self.userCfg.keys(): + for key in self.userCfg: self.userCfg[key].Save() idleConf=IdleConf() @@ -687,18 +687,18 @@ ### module test if __name__ == '__main__': def dumpCfg(cfg): - print '\n',cfg,'\n' - for key in cfg.keys(): + print('\n',cfg,'\n') + for key in cfg: sections=cfg[key].sections() - print key - print sections + print(key) + print(sections) for section in sections: options=cfg[key].options(section) - print section - print options + print(section) + print(options) for option in options: - print option, '=', cfg[key].Get(section,option) + print(option, '=', cfg[key].Get(section,option)) dumpCfg(idleConf.defaultCfg) dumpCfg(idleConf.userCfg) - print idleConf.userCfg['main'].Get('Theme','name') + print(idleConf.userCfg['main'].Get('Theme','name')) #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal') Index: configHelpSourceEdit.py =================================================================== --- configHelpSourceEdit.py (revision 85719) +++ configHelpSourceEdit.py (working copy) @@ -164,6 +164,6 @@ def run(): keySeq = '' dlg = GetHelpSourceDialog(root, 'Get Help Source') - print dlg.result + print(dlg.result) Button(root,text='Dialog', command=run).pack() root.mainloop() Index: configSectionNameDialog.py =================================================================== --- configSectionNameDialog.py (revision 85719) +++ configSectionNameDialog.py (working copy) @@ -92,6 +92,6 @@ keySeq='' dlg=GetCfgSectionNameDialog(root,'Get Name', 'The information here should need to be word wrapped. Test.') - print dlg.result + print(dlg.result) Button(root,text='Dialog',command=run).pack() root.mainloop() Index: Debugger.py =================================================================== --- Debugger.py (revision 85719) +++ Debugger.py (working copy) @@ -2,11 +2,10 @@ import bdb import types from Tkinter import * -from idlelib.WindowList import ListedToplevel -from idlelib.ScrolledList import ScrolledList -from idlelib import macosxSupport +from .WindowList import ListedToplevel +from .ScrolledList import ScrolledList +from . import macosxSupport - class Idb(bdb.Bdb): def __init__(self, gui): @@ -253,7 +252,8 @@ if self.vsource.get(): self.sync_source_line() - def show_frame(self, (frame, lineno)): + def show_frame(self, stackitem): + frame, lineno = stackitem self.frame = frame self.show_variables() @@ -311,8 +311,7 @@ def load_breakpoints(self): "Load PyShellEditorWindow breakpoints into subprocess debugger" - pyshell_edit_windows = self.pyshell.flist.inversedict.keys() - for editwin in pyshell_edit_windows: + for editwin in self.pyshell.flist.inversedict: filename = editwin.io.filename try: for lineno in editwin.breakpoints: @@ -348,8 +347,7 @@ funcname = code.co_name import linecache sourceline = linecache.getline(filename, lineno) - import string - sourceline = string.strip(sourceline) + sourceline = sourceline.strip() if funcname in ("?", "", None): item = "%s, line %d: %s" % (modname, lineno, sourceline) else: @@ -413,8 +411,8 @@ height = 20*len(dict) # XXX 20 == observed height of Entry widget self.master = master self.title = title - import repr - self.repr = repr.Repr() + import reprlib + self.repr = reprlib.Repr() self.repr.maxstring = 60 self.repr.maxother = 60 self.frame = frame = Frame(master) @@ -440,15 +438,27 @@ return subframe = self.subframe frame = self.frame - for c in subframe.children.values(): + for c in list(subframe.children.values()): c.destroy() self.dict = None if not dict: l = Label(subframe, text="None") l.grid(row=0, column=0) else: - names = dict.keys() - names.sort() + #names = sorted(dict) + ### + # Because of (temporary) limitations on the dict_keys type (not yet + # public or pickleable), have the subprocess to send a list of + # keys, not a dict_keys object. sorted() will take a dict_keys + # (no subprocess) or a list. + # + # There is also an obscure bug in sorted(dict) where the + # interpreter gets into a loop requesting non-existing dict[0], + # dict[1], dict[2], etc from the RemoteDebugger.DictProxy. + ### + keys_list = dict.keys() + names = sorted(keys_list) + ### row = 0 for name in names: value = dict[name] Index: Delegator.py =================================================================== --- Delegator.py (revision 85719) +++ Delegator.py (working copy) @@ -13,7 +13,7 @@ return attr def resetcache(self): - for key in self.__cache.keys(): + for key in self.__cache: try: delattr(self, key) except AttributeError: @@ -21,9 +21,9 @@ self.__cache.clear() def cachereport(self): - keys = self.__cache.keys() + keys = list(self.__cache.keys()) keys.sort() - print keys + print(keys) def setdelegate(self, delegate): self.resetcache() Index: dynOptionMenuWidget.py =================================================================== --- dynOptionMenuWidget.py (revision 85719) +++ dynOptionMenuWidget.py (working copy) @@ -13,7 +13,7 @@ def __init__(self, master, variable, value, *values, **kwargs): #get a copy of kwargs before OptionMenu.__init__ munges them kwargsCopy=copy.copy(kwargs) - if 'highlightthickness' in kwargs.keys(): + if 'highlightthickness' in list(kwargs.keys()): del(kwargs['highlightthickness']) OptionMenu.__init__(self, master, variable, value, *values, **kwargs) self.config(highlightthickness=kwargsCopy.get('highlightthickness')) Index: EditorWindow.py =================================================================== --- EditorWindow.py (revision 85719) +++ EditorWindow.py (working copy) @@ -1,22 +1,25 @@ import sys import os import re +import string import imp +from itertools import count from Tkinter import * import tkSimpleDialog import tkMessageBox +import traceback import webbrowser -from idlelib.MultiCall import MultiCallCreator -from idlelib import idlever -from idlelib import WindowList -from idlelib import SearchDialog -from idlelib import GrepDialog -from idlelib import ReplaceDialog -from idlelib import PyParse -from idlelib.configHandler import idleConf -from idlelib import aboutDialog, textView, configDialog -from idlelib import macosxSupport +from .MultiCall import MultiCallCreator +from . import idlever +from . import WindowList +from . import SearchDialog +from . import GrepDialog +from . import ReplaceDialog +from . import PyParse +from .configHandler import idleConf +from . import aboutDialog, textView, configDialog +from . import macosxSupport # The default tab setting for a Text widget, in average-width characters. TK_TABWIDTH_DEFAULT = 8 @@ -47,17 +50,44 @@ try: path = module.__path__ except AttributeError: - raise ImportError, 'No source for module ' + module.__name__ + raise ImportError('No source for module ' + module.__name__) return file, filename, descr +class _singledialog(object): + """Just a given dialog should be executing at any time. Trying to + create a new one results in bringing to front the running dialog.""" + + def __init__(self, meth): + self.meth = meth + self.dlg = None + + def __get__(self, instance, owner): + self.instance = instance + return self + + def __call__(self, *args): + if self.dlg: # dialog is already running + # bring it to front + self.dlg.withdraw() + self.dlg.deiconify() + self.dlg.lift() + + else: # dialog not running, start it and save the instance + self.dlg = self.meth(self.instance, *args) + self.dlg.bind('', self._clear_dlg) + + def _clear_dlg(self, *args): + """Dialog is being destroyed. A new dialog instance can be created.""" + self.dlg = None + class EditorWindow(object): - from idlelib.Percolator import Percolator - from idlelib.ColorDelegator import ColorDelegator - from idlelib.UndoDelegator import UndoDelegator - from idlelib.IOBinding import IOBinding, filesystemencoding, encoding - from idlelib import Bindings + from .Percolator import Percolator + from .ColorDelegator import ColorDelegator + from .UndoDelegator import UndoDelegator + from .IOBinding import IOBinding, filesystemencoding, encoding + from . import Bindings from Tkinter import Toplevel - from idlelib.MultiStatusBar import MultiStatusBar + from .MultiStatusBar import MultiStatusBar help_url = None @@ -101,12 +131,12 @@ self.menubar = Menu(root) self.top = top = WindowList.ListedToplevel(root, menu=self.menubar) if flist: - self.tkinter_vars = flist.vars + 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 = flist.inversedict else: - self.tkinter_vars = {} # keys: Tkinter event names + self.Tkinter_vars = {} # keys: Tkinter event names # values: Tkinter variable instances self.top.instance_dict = {} self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(), @@ -230,39 +260,33 @@ # conceivable file). # Making the initial values larger slows things down more often. self.num_context_lines = 50, 500, 5000000 - self.per = per = self.Percolator(text) - self.undo = undo = self.UndoDelegator() per.insertfilter(undo) text.undo_block_start = undo.undo_block_start text.undo_block_stop = undo.undo_block_stop undo.set_saved_change_hook(self.saved_change_hook) - # IOBinding implements file I/O and printing functionality self.io = io = self.IOBinding(self) io.set_filename_change_hook(self.filename_change_hook) - - # Create the recent files submenu - self.recent_files_menu = Menu(self.menubar) - self.menudict['file'].insert_cascade(3, label='Recent Files', - underline=0, - menu=self.recent_files_menu) - self.update_recent_files_list() - + self.good_load = False + self.set_indentation_params(False) self.color = None # initialized below in self.ResetColorizer if filename: if os.path.exists(filename) and not os.path.isdir(filename): - io.loadfile(filename) + if io.loadfile(filename): + self.good_load = True + is_py_src = self.ispythonsource(filename) + self.set_indentation_params(is_py_src) + if is_py_src: + self.color = color = self.ColorDelegator() + per.insertfilter(color) else: io.set_filename(filename) self.ResetColorizer() self.saved_change_hook() - - self.set_indentation_params(self.ispythonsource(filename)) - + self.update_recent_files_list() self.load_extensions() - menu = self.menudict.get('windows') if menu: end = menu.index("end") @@ -281,7 +305,7 @@ def _filename_to_unicode(self, filename): """convert filename to unicode in order to display it in Tk""" - if isinstance(filename, unicode) or not filename: + if isinstance(filename, str) or not filename: return filename else: try: @@ -310,7 +334,7 @@ insertpt = int(self.text.index("iomark").split(".")[1]) else: line = self.text.get("insert linestart", "insert lineend") - for insertpt in xrange(len(line)): + for insertpt in range(len(line)): if line[insertpt] not in (' ','\t'): break else: @@ -384,13 +408,15 @@ underline, label = prepstr(label) menudict[name] = menu = Menu(mbar, name=name) mbar.add_cascade(label=label, menu=menu, underline=underline) - if macosxSupport.runningAsOSXApp(): # Insert the application menu menudict['application'] = menu = Menu(mbar, name='apple') mbar.add_cascade(label='IDLE', menu=menu) - self.fill_menus() + self.recent_files_menu = Menu(self.menubar) + self.menudict['file'].insert_cascade(3, label='Recent Files', + underline=0, + menu=self.recent_files_menu) self.base_helpmenu_length = self.menudict['help'].index(END) self.reset_help_menu_entries() @@ -407,7 +433,6 @@ rmenu = None def right_menu_event(self, event): - self.text.tag_remove("sel", "1.0", "end") self.text.mark_set("insert", "@%d,%d" % (event.x, event.y)) if not self.rmenu: self.make_rmenu() @@ -416,32 +441,73 @@ iswin = sys.platform[:3] == 'win' if iswin: self.text.config(cursor="arrow") + + for label, eventname, verify_state in self.rmenu_specs: + if verify_state is None: + continue + state = getattr(self, verify_state)() + rmenu.entryconfigure(label, state=state) + rmenu.tk_popup(event.x_root, event.y_root) if iswin: self.text.config(cursor="ibeam") rmenu_specs = [ - # ("Label", "<>"), ... - ("Close", "<>"), # Example + # ("Label", "<>", "statefuncname"), ... + ("Close", "<>", None), # Example ] def make_rmenu(self): rmenu = Menu(self.text, tearoff=0) - for label, eventname in self.rmenu_specs: - def command(text=self.text, eventname=eventname): - text.event_generate(eventname) - rmenu.add_command(label=label, command=command) + for label, eventname, _ in self.rmenu_specs: + if label is not None: + def command(text=self.text, eventname=eventname): + text.event_generate(eventname) + rmenu.add_command(label=label, command=command) + else: + rmenu.add_separator() self.rmenu = rmenu + def rmenu_check_cut(self): + if getattr(self, 'interp', None) is None: + return self.rmenu_check_copy() + + text = self.text + try: + indx = text.index('sel.first') + except TclError: + return 'disabled' + else: + if indx and text.compare(indx, '>=', text.index('iomark')): + return 'normal' + return 'disabled' + + def rmenu_check_copy(self): + try: + indx = self.text.index('sel.first') + except TclError: + return 'disabled' + else: + return 'normal' if indx else 'disabled' + + def rmenu_check_paste(self): + try: + self.text.tk.call('tk::GetSelection', self.text, 'CLIPBOARD') + except TclError: + return 'disabled' + else: + return 'normal' + def about_dialog(self, event=None): aboutDialog.AboutDialog(self.top,'About IDLE') def config_dialog(self, event=None): configDialog.ConfigDialog(self.top,'Settings') + @_singledialog 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) + return textView.view_file(self.top, 'Help', fn, modal=False) def python_docs(self, event=None): if sys.platform[:3] == 'win': @@ -537,7 +603,7 @@ text.see("insert") def open_module(self, event=None): - # XXX Shouldn't this be in IOBinding or in FileList? + # XXX Shouldn't this be in IOBinding? try: name = self.text.get("sel.first", "sel.last") except TclError: @@ -555,7 +621,7 @@ # XXX Ought to insert current file's directory in front of path try: (f, file, (suffix, mode, type)) = _find_module(name) - except (NameError, ImportError), msg: + except (NameError, ImportError) as msg: tkMessageBox.showerror("Import error", str(msg), parent=self.text) return if type != imp.PY_SOURCE: @@ -580,11 +646,11 @@ return None head, tail = os.path.split(filename) base, ext = os.path.splitext(tail) - from idlelib import ClassBrowser + from . import ClassBrowser ClassBrowser.ClassBrowser(self.flist, base, [head]) def open_path_browser(self, event=None): - from idlelib import PathBrowser + from . import PathBrowser PathBrowser.PathBrowser(self.flist) def gotoline(self, lineno): @@ -600,13 +666,8 @@ base, ext = os.path.splitext(os.path.basename(filename)) if os.path.normcase(ext) in (".py", ".pyw"): return True - try: - f = open(filename) - line = f.readline() - f.close() - except IOError: - return False - return line.startswith('#!') and line.find('python') >= 0 + line = self.text.get('1.0', '1.0 lineend') + return line.startswith('#!') and 'python' in line def close_hook(self): if self.flist: @@ -658,6 +719,19 @@ selectbackground=select_colors['background'], ) + IDENTCHARS = string.ascii_letters + string.digits + "_" + + def colorize_syntax_error(self, text, pos): + text.tag_add("ERROR", pos) + char = text.get(pos) + if char and char in self.IDENTCHARS: + text.tag_add("ERROR", pos + " wordstart", pos) + if '\n' == text.get(pos): # error at line end + text.mark_set("insert", pos) + else: + text.mark_set("insert", pos + "+1c") + text.see(pos) + def ResetFont(self): "Update the text widgets' font if it is changed" # Called from configDialog.py @@ -696,7 +770,7 @@ for item in menu[1]: if item: menuEventDict[menu[0]][prepstr(item[0])[1]] = item[1] - for menubarItem in self.menudict.keys(): + for menubarItem in self.menudict: menu = self.menudict[menubarItem] end = menu.index(END) + 1 for index in range(0, end): @@ -775,11 +849,11 @@ finally: rf_file.close() # for each edit window instance, construct the recent files menu - for instance in self.top.instance_dict.keys(): + for instance in self.top.instance_dict: menu = instance.recent_files_menu menu.delete(1, END) # clear, and rebuild: - for i, file_name in enumerate(rf_list): - file_name = file_name.rstrip() # zap \n + for i, file in zip(count(), rf_list): + file_name = file[0:-1] # 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) @@ -859,8 +933,7 @@ "Return (width, height, x, y)" geom = self.top.wm_geometry() m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom) - tuple = (map(int, m.groups())) - return tuple + return list(map(int, m.groups())) def close_event(self, event): self.close() @@ -892,7 +965,7 @@ self.color.close(False) self.color = None self.text = None - self.tkinter_vars = None + self.Tkinter_vars = None self.per.close() self.per = None self.top.destroy() @@ -905,7 +978,7 @@ self.load_standard_extensions() def unload_extensions(self): - for ins in self.extensions.values(): + for ins in list(self.extensions.values()): if hasattr(ins, "close"): ins.close() self.extensions = {} @@ -915,8 +988,7 @@ try: self.load_extension(name) except: - print "Failed to load extension", repr(name) - import traceback + print("Failed to load extension", repr(name)) traceback.print_exc() def get_standard_extension_names(self): @@ -926,8 +998,8 @@ try: mod = __import__(name, globals(), locals(), []) except ImportError: - print "\nFailed to import extension: ", name - return + print("\nFailed to import extension: ", name) + raise cls = getattr(mod, name) keydefs = idleConf.GetExtensionBindings(name) if hasattr(cls, "menudefs"): @@ -936,7 +1008,7 @@ self.extensions[name] = ins if keydefs: self.apply_bindings(keydefs) - for vevent in keydefs.keys(): + for vevent in keydefs: methodname = vevent.replace("-", "_") while methodname[:1] == '<': methodname = methodname[1:] @@ -998,20 +1070,20 @@ value = var.get() return value else: - raise NameError, name + raise NameError(name) def setvar(self, name, value, vartype=None): var = self.get_var_obj(name, vartype) if var: var.set(value) else: - raise NameError, name + raise NameError(name) def get_var_obj(self, name, vartype=None): - var = self.tkinter_vars.get(name) + var = self.Tkinter_vars.get(name) if not var and vartype: # create a Tkinter variable object with self.text as master: - self.tkinter_vars[name] = var = vartype(self.text) + self.Tkinter_vars[name] = var = vartype(self.text) return var # Tk implementations of "virtual text methods" -- each platform @@ -1046,34 +1118,31 @@ # Return the text widget's current view of what a tab stop means # (equivalent width in spaces). - def get_tabwidth(self): + def get_tk_tabwidth(self): current = self.text['tabs'] or TK_TABWIDTH_DEFAULT return int(current) # Set the text widget's current view of what a tab stop means. - def set_tabwidth(self, newtabwidth): + def set_tk_tabwidth(self, newtabwidth): text = self.text - if self.get_tabwidth() != newtabwidth: + if self.get_tk_tabwidth() != newtabwidth: + # Set text widget tab width pixels = text.tk.call("font", "measure", text["font"], "-displayof", text.master, "n" * newtabwidth) text.configure(tabs=pixels) - # If ispythonsource and guess are true, guess a good value for - # indentwidth based on file content (if possible), and if - # indentwidth != tabwidth set usetabs false. - # In any case, adjust the Text widget's view of what a tab - # character means. +### begin autoindent code ### (configuration was moved to beginning of class) - def set_indentation_params(self, ispythonsource, guess=True): - if guess and ispythonsource: + def set_indentation_params(self, is_py_src, guess=True): + if is_py_src and guess: i = self.guess_indent() if 2 <= i <= 8: self.indentwidth = i if self.indentwidth != self.tabwidth: self.usetabs = False - self.set_tabwidth(self.tabwidth) + self.set_tk_tabwidth(self.tabwidth) def smart_backspace_event(self, event): text = self.text @@ -1498,7 +1567,9 @@ _tokenize.tabsize = self.tabwidth try: try: - _tokenize.tokenize(self.readline, self.tokeneater) + tokens = _tokenize.generate_tokens(self.readline) + for token in tokens: + self.tokeneater(*token) except _tokenize.TokenError: # since we cut off the tokenizer early, we can trigger # spurious errors Index: FileList.py =================================================================== --- FileList.py (revision 85719) +++ FileList.py (working copy) @@ -2,11 +2,10 @@ from Tkinter import * import tkMessageBox - class FileList: # N.B. this import overridden in PyShellFileList. - from idlelib.EditorWindow import EditorWindow + from .EditorWindow import EditorWindow def __init__(self, root): self.root = root @@ -33,7 +32,12 @@ # Don't create window, perform 'action', e.g. open in same window return action(filename) else: - return self.EditorWindow(self, filename, key) + edit = self.EditorWindow(self, filename, key) + if edit.good_load: + return edit + else: + edit._close() + return None def gotofileline(self, filename, lineno=None): edit = self.open(filename) @@ -44,7 +48,7 @@ return self.EditorWindow(self, filename) def close_all_callback(self, event): - for edit in self.inversedict.keys(): + for edit in list(self.inversedict): reply = edit.close() if reply == "cancel": break @@ -54,7 +58,7 @@ try: key = self.inversedict[edit] except KeyError: - print "Don't know this EditorWindow object. (close)" + print("Don't know this EditorWindow object. (close)") return if key: del self.dict[key] @@ -67,7 +71,7 @@ try: key = self.inversedict[edit] except KeyError: - print "Don't know this EditorWindow object. (rename)" + print("Don't know this EditorWindow object. (rename)") return filename = edit.io.filename if not filename: @@ -106,7 +110,7 @@ def _test(): - from idlelib.EditorWindow import fixwordbreaks + from .EditorWindow import fixwordbreaks import sys root = Tk() fixwordbreaks(root) Index: FormatParagraph.py =================================================================== --- FormatParagraph.py (revision 85719) +++ FormatParagraph.py (working copy) @@ -15,7 +15,7 @@ # * Fancy comments, like this bulleted list, arent handled :-) import re -from idlelib.configHandler import idleConf +from .configHandler import idleConf class FormatParagraph: Index: GrepDialog.py =================================================================== --- GrepDialog.py (revision 85719) +++ GrepDialog.py (working copy) @@ -2,8 +2,8 @@ import fnmatch import sys from Tkinter import * -from idlelib import SearchEngine -from idlelib.SearchDialogBase import SearchDialogBase +from . import SearchEngine +from .SearchDialogBase import SearchDialogBase def grep(text, io=None, flist=None): root = text._root() @@ -63,7 +63,7 @@ if not path: self.top.bell() return - from idlelib.OutputWindow import OutputWindow + from .OutputWindow import OutputWindow save = sys.stdout try: sys.stdout = OutputWindow(self.flist) @@ -77,13 +77,13 @@ list.sort() self.close() pat = self.engine.getpat() - print "Searching %r in %s ..." % (pat, path) + print("Searching %r in %s ..." % (pat, path)) hits = 0 for fn in list: try: f = open(fn) - except IOError, msg: - print msg + except IOError as msg: + print(msg) continue lineno = 0 while 1: @@ -102,16 +102,16 @@ s = "" else: s = "s" - print "Found", hits, "hit%s." % s - print "(Hint: right-click to open locations.)" + print("Found", hits, "hit%s." % s) + print("(Hint: right-click to open locations.)") else: - print "No hits." + print("No hits.") def findfiles(self, dir, base, rec): try: names = os.listdir(dir or os.curdir) - except os.error, msg: - print msg + except os.error as msg: + print(msg) return [] list = [] subdirs = [] Index: help.txt =================================================================== --- help.txt (revision 85719) +++ help.txt (working copy) @@ -25,6 +25,7 @@ --- Close -- Close current window (asks to save if unsaved) Exit -- Close all windows, quit (asks to save if unsaved) + Revert -- Discard all changes since the last save Edit Menu: @@ -220,6 +221,9 @@ Command history: + F6 adjusts the view in the shell window to display the start of the + most recent non-empty shell restart. + Alt-p retrieves previous command matching what you have typed. Alt-n retrieves next. (These are Control-p, Control-n on the Mac) Index: HyperParser.py =================================================================== --- HyperParser.py (revision 85719) +++ HyperParser.py (working copy) @@ -10,7 +10,7 @@ import string import keyword -from idlelib import PyParse +from . import PyParse class HyperParser: Index: idle.py =================================================================== --- idle.py (revision 85719) +++ idle.py (working copy) @@ -2,10 +2,10 @@ 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 +# parent of this dir to sys.path. Otherwise, importing this module 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() +from idlelib import PyShell +PyShell.main() Index: IdleHistory.py =================================================================== --- IdleHistory.py (revision 85719) +++ IdleHistory.py (working copy) @@ -1,4 +1,4 @@ -from idlelib.configHandler import idleConf +from .configHandler import idleConf class History: Index: idlever.py =================================================================== --- idlever.py (revision 85719) +++ idlever.py (working copy) @@ -1 +1 @@ -IDLE_VERSION = "2.7.1a0" +IDLE_VERSION = "3.1.2" Index: IOBinding.py =================================================================== --- IOBinding.py (revision 85719) +++ IOBinding.py (working copy) @@ -1,12 +1,4 @@ -# changes by dscherer@cmu.edu -# - IOBinding.open() replaces the current window with the opened file, -# if the current window is both unmodified and unnamed -# - IOBinding.loadfile() interprets Windows, UNIX, and Macintosh -# end-of-line conventions, instead of relying on the standard library, -# which will only understand the local convention. - import os -import types import sys import codecs import tempfile @@ -16,13 +8,9 @@ from Tkinter import * from SimpleDialog import SimpleDialog -from idlelib.configHandler import idleConf +from .configHandler import idleConf -try: - from codecs import BOM_UTF8 -except ImportError: - # only available since Python 2.3 - BOM_UTF8 = '\xef\xbb\xbf' +from codecs import BOM_UTF8 # Try setting the locale, so that we can find out # what encoding to use @@ -33,15 +21,15 @@ pass # Encoding for file names -filesystemencoding = sys.getfilesystemencoding() +filesystemencoding = sys.getfilesystemencoding() ### currently unused -encoding = "ascii" +locale_encoding = 'ascii' if sys.platform == 'win32': # On Windows, we could use "mbcs". However, to give the user # a portable encoding name, we need to find the code page try: - encoding = locale.getdefaultlocale()[1] - codecs.lookup(encoding) + locale_encoding = locale.getdefaultlocale()[1] + codecs.lookup(locale_encoding) except LookupError: pass else: @@ -50,26 +38,29 @@ # loaded, it may not offer nl_langinfo, or CODESET, or the # resulting codeset may be unknown to Python. We ignore all # these problems, falling back to ASCII - encoding = locale.nl_langinfo(locale.CODESET) - if encoding is None or encoding is '': + locale_encoding = locale.nl_langinfo(locale.CODESET) + if locale_encoding is None or locale_encoding is '': # situation occurs on Mac OS X - encoding = 'ascii' - codecs.lookup(encoding) + locale_encoding = 'ascii' + codecs.lookup(locale_encoding) except (NameError, AttributeError, LookupError): - # Try getdefaultlocale well: it parses environment variables, + # Try getdefaultlocale: it parses environment variables, # which may give a clue. Unfortunately, getdefaultlocale has # bugs that can cause ValueError. try: - encoding = locale.getdefaultlocale()[1] - if encoding is None or encoding is '': + locale_encoding = locale.getdefaultlocale()[1] + if locale_encoding is None or locale_encoding is '': # situation occurs on Mac OS X - encoding = 'ascii' - codecs.lookup(encoding) + locale_encoding = 'ascii' + codecs.lookup(locale_encoding) except (ValueError, LookupError): pass -encoding = encoding.lower() +locale_encoding = locale_encoding.lower() +encoding = locale_encoding ### KBK 07Sep07 This is used all over IDLE, check! + ### 'encoding' is used below in encode(), check! + coding_re = re.compile("coding[:=]\s*([-\w_.]+)") class EncodingMessage(SimpleDialog): @@ -140,7 +131,6 @@ raise LookupError, "Unknown encoding "+name return name - class IOBinding: def __init__(self, editwin): @@ -182,14 +172,20 @@ self.filename_change_hook = hook filename = None + file_timestamp = None dirname = None def set_filename(self, filename): if filename and os.path.isdir(filename): self.filename = None + self.file_timestamp = None self.dirname = filename else: self.filename = filename + if filename is not None: + self.file_timestamp = os.stat(filename).st_mtime + else: + self.file_timestamp = None self.dirname = None self.set_saved(1) if self.filename_change_hook: @@ -237,7 +233,7 @@ eol = r"(\r\n)|\n|\r" # \r\n (Windows), \n (UNIX), or \r (Mac) eol_re = re.compile(eol) - eol_convention = os.linesep # Default + eol_convention = os.linesep # default def loadfile(self, filename): try: @@ -259,14 +255,17 @@ # Make sure it is an ASCII string self.eol_convention = self.eol_convention.encode("ascii") chars = self.eol_re.sub(r"\n", chars) - + self.text.delete("1.0", "end") self.set_filename(None) self.text.insert("1.0", chars) self.reset_undo() self.set_filename(filename) self.text.mark_set("insert", "1.0") - self.text.see("insert") +## The following has been commented out because on Windows +## it places the first line above the edit display, which +## is confusing. It apparently has no effect on Mac or Linux. +## self.text.see("insert") self.updaterecentfileslist(filename) return True @@ -338,7 +337,24 @@ if not self.filename: self.save_as(event) else: + # Check the time of most recent content modification so the + # user doesn't accidentally overwrite a newer version of the file. + if self.file_timestamp != os.stat(self.filename).st_mtime: + dlg = Tkinter.messagebox.Message( + master=self.text, + title="File has changed", + message=( + "The file has changed since reading it!\n\n" + "Do you really want to overwrite it?"), + default=Tkinter.messagebox.NO, + icon=Tkinter.messagebox.WARNING, + type=Tkinter.messagebox.YESNO) + res = dlg.show() + if not self.text.tk.getboolean(str(res)): + return + if self.writefile(self.filename): + self.file_timestamp = os.stat(self.filename).st_mtime self.set_saved(1) try: self.editwin.store_file_breaks() @@ -369,18 +385,28 @@ self.updaterecentfileslist(filename) return "break" + def save_as_temp(self, prefix='IDLE_tmp_'): + """Save the current text content to a temp file.""" + tfd, tempfilename = tempfile.mkstemp(prefix=prefix) + os.close(tfd) + if not self.writefile(tempfilename): + os.unlink(tempfilename) + return None + return tempfilename + def writefile(self, filename): self.fixlastline() - chars = self.encode(self.text.get("1.0", "end-1c")) + text = self.text.get("1.0", "end-1c") if self.eol_convention != "\n": - chars = chars.replace("\n", self.eol_convention) + text = text.replace("\n", self.eol_convention) + chars = self.encode(text) try: f = open(filename, "wb") f.write(chars) f.flush() f.close() return True - except IOError, msg: + except IOError as msg: tkMessageBox.showerror("I/O Error", str(msg), master=self.text) return False @@ -482,11 +508,8 @@ filename = self.filename # shell undo is reset after every prompt, looks saved, probably isn't if not saved or filename is None: - (tfd, tempfilename) = tempfile.mkstemp(prefix='IDLE_tmp_') - filename = tempfilename - os.close(tfd) - if not self.writefile(tempfilename): - os.unlink(tempfilename) + tempfilename = self.save_as_temp() + if tempfilename is None: return "break" platform=os.name printPlatform=1 @@ -526,14 +549,14 @@ ("All files", "*"), ] + defaultextension = '.py' if sys.platform[:3] == 'win' else '' + def askopenfile(self): dir, base = self.defaultfilename("open") if not self.opendialog: self.opendialog = tkFileDialog.Open(master=self.text, filetypes=self.filetypes) filename = self.opendialog.show(initialdir=dir, initialfile=base) - if isinstance(filename, unicode): - filename = filename.encode(filesystemencoding) return filename def defaultfilename(self, mode="open"): @@ -551,16 +574,17 @@ def asksavefile(self): dir, base = self.defaultfilename("save") if not self.savedialog: - self.savedialog = tkFileDialog.SaveAs(master=self.text, - filetypes=self.filetypes) + self.savedialog = tkFileDialog.SaveAs( + master=self.text, + filetypes=self.filetypes, + defaultextension=self.defaultextension) filename = self.savedialog.show(initialdir=dir, initialfile=base) - if isinstance(filename, unicode): - filename = filename.encode(filesystemencoding) return filename def updaterecentfileslist(self,filename): "Update recent file list on all editor windows" - self.editwin.update_recent_files_list(filename) + if self.editwin.flist: + self.editwin.update_recent_files_list(filename) def test(): root = Tk() Index: keybindingDialog.py =================================================================== --- keybindingDialog.py (revision 85719) +++ keybindingDialog.py (working copy) @@ -4,6 +4,7 @@ from Tkinter import * import tkMessageBox import string +from . import macosxSupport class GetKeysDialog(Toplevel): def __init__(self,parent,title,action,currentKeySequences): @@ -132,7 +133,7 @@ order is also important: key binding equality depends on it, so config-keys.def must use the same ordering. """ - from idlelib import macosxSupport + import sys if macosxSupport.runningAsOSXApp(): self.modifiers = ['Shift', 'Control', 'Option', 'Command'] else: @@ -163,7 +164,7 @@ if finalKey: finalKey = self.TranslateKey(finalKey, modifiers) keyList.append(finalKey) - self.keyString.set('<' + string.join(keyList,'-') + '>') + self.keyString.set('<' + '-'.join(keyList) + '>') def GetModifiers(self): modList = [variable.get() for variable in self.modifier_vars] @@ -203,7 +204,7 @@ '/':'slash','?':'question','Page Up':'Prior','Page Down':'Next', 'Left Arrow':'Left','Right Arrow':'Right','Up Arrow':'Up', 'Down Arrow': 'Down', 'Tab':'Tab'} - if key in translateDict.keys(): + if key in translateDict: key = translateDict[key] if 'Shift' in modifiers and key in string.ascii_lowercase: key = key.upper() @@ -263,6 +264,6 @@ def run(): keySeq='' dlg=GetKeysDialog(root,'Get Keys','find-again',[]) - print dlg.result + print(dlg.result) Button(root,text='Dialog',command=run).pack() root.mainloop() Index: macosxSupport.py =================================================================== --- macosxSupport.py (revision 85719) +++ macosxSupport.py (working copy) @@ -5,19 +5,13 @@ import sys import Tkinter - -_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 Tcl/Tk. """ - global _appbundle - if _appbundle is None: - _appbundle = (sys.platform == 'darwin' and '.app' in sys.executable) - return _appbundle + return (sys.platform == 'darwin' and '.app' in sys.executable) def addOpenEventSupport(root, flist): """ @@ -26,7 +20,7 @@ """ def doOpenFile(*args): for fn in args: - flist.open(fn) + flist.open(str(fn)) # The command below is a hook in aquatk that is called whenever the app # receives a file open event. The callback can have multiple arguments, @@ -57,10 +51,10 @@ # Due to a (mis-)feature of TkAqua the user will also see an empty Help # menu. from Tkinter import Menu, Text, Text - from idlelib.EditorWindow import prepstr, get_accelerator - from idlelib import Bindings - from idlelib import WindowList - from idlelib.MultiCall import MultiCallCreator + from .EditorWindow import prepstr, get_accelerator + from . import Bindings + from . import WindowList + from .MultiCall import MultiCallCreator menubar = Menu(root) root.configure(menu=menubar) @@ -83,12 +77,18 @@ menubar.add_cascade(label='IDLE', menu=menu) def about_dialog(event=None): - from idlelib import aboutDialog + from . import aboutDialog aboutDialog.AboutDialog(root, 'About IDLE') def config_dialog(event=None): - from idlelib import configDialog + from . import configDialog + + # Ensure that the root object has an instance_dict attribute, + # mirrors code in EditorWindow (although that sets the attribute + # on an EditorWindow instance that is then passed as the first + # argument to ConfigDialog) root.instance_dict = flist.inversedict + root.instance_dict = flist.inversedict configDialog.ConfigDialog(root, 'Settings') Index: MultiCall.py =================================================================== --- MultiCall.py (revision 85719) +++ MultiCall.py (working copy) @@ -30,10 +30,9 @@ """ import sys -import string import re import Tkinter -from idlelib import macosxSupport +from . import macosxSupport # the event type constants, which define the meaning of mc_type MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3; @@ -258,19 +257,16 @@ """ if not sequence or sequence[0] != '<' or sequence[-1] != '>': return None - words = string.split(sequence[1:-1], '-') - + words = sequence[1:-1].split('-') modifiers = 0 while words and words[0] in _modifier_names: modifiers |= 1 << _modifier_names[words[0]] del words[0] - if words and words[0] in _type_names: type = _type_names[words[0]] del words[0] else: return None - if _binder_classes[type] is _SimpleBinder: if modifiers or words: return None @@ -321,7 +317,8 @@ for i in range(len(_types))] def bind(self, sequence=None, func=None, add=None): - #print "bind(%s, %s, %s) called." % (sequence, func, add) + #print("bind(%s, %s, %s)" % (sequence, func, add), + # file=sys.__stderr__) if type(sequence) is str and len(sequence) > 2 and \ sequence[:2] == "<<" and sequence[-2:] == ">>": if sequence in self.__eventinfo: @@ -349,7 +346,8 @@ return widget.unbind(self, sequence, funcid) def event_add(self, virtual, *sequences): - #print "event_add(%s,%s) was called"%(repr(virtual),repr(sequences)) + #print("event_add(%s, %s)" % (repr(virtual), repr(sequences)), + # file=sys.__stderr__) if virtual not in self.__eventinfo: self.__eventinfo[virtual] = [None, []] @@ -357,7 +355,7 @@ for seq in sequences: triplet = _parse_sequence(seq) if triplet is None: - #print >> sys.stderr, "Seq. %s was added by Tkinter."%seq + #print("Tkinter event_add(%s)" % seq, file=sys.__stderr__) widget.event_add(self, virtual, seq) else: if func is not None: @@ -371,7 +369,7 @@ for seq in sequences: triplet = _parse_sequence(seq) if triplet is None: - #print >> sys.stderr, "Seq. %s was deleted by Tkinter."%seq + #print("Tkinter event_delete: %s" % seq, file=sys.__stderr__) widget.event_delete(self, virtual, seq) else: if func is not None: @@ -404,7 +402,7 @@ text.pack() def bindseq(seq, n=[0]): def handler(event): - print seq + print(seq) text.bind("<>"%n[0], handler) text.event_add("<>"%n[0], seq) n[0] += 1 Index: NEWS.txt =================================================================== --- NEWS.txt (revision 85719) +++ NEWS.txt (working copy) @@ -1,8 +1,80 @@ -What's New in IDLE 2.7? +What's New in IDLE 2.7 post-release of Python 2.7? + ======================= -*Release date: 07-03-2010* +*Release date: xx-xx-xx* +merged VIDLE into standard IDLE 2.7 + +What is VIDLE? +========================= + +In December 2008 David Scherer created an alternative version of IDLE to fix +some long-standing problems. In the 2009 Google Summer of Code Guilherme Polo +continued this work, assisted by Bruce Sherwood. Important new features +include a configuration preference that permits writing and running test +programs from the editor without having to save the file, and bringing the +shell window forward in case of an error (because novices often failed to +realize why their program had stopped). + +Polo submitted patches at the end of the summer of 2009, found here: +http://code.google.com/p/google-summer-of-code-2009-python/downloads/list +This was for the Python 2.X series. + +Because the mechanism was not clear for getting these patches into the +version of IDLE distributed with Python, the patched version has been +distributed with VPython (vpython.org) with the name VIDLE to attempt to +avoid confusion with IDLE. It is installed into site-packages. + +In September 2010 Bruce Sherwood manually applied Polo's patches to the +version of IDLE distributed with Python 3.1.2, using relative addressing +of modules (e.g. from . import PyShell). Except for having to change one +absolute address in the use of __import__ in PyShell.py, and the module +names idlelib or vidle in idle.py and idle.pyw, this version can work either +in Python31/Lib/idlelib or in Python31/Lib/site-packages/vidle. + +This Python 3 VIDLE is available at vpython.org and will be included in the +Windows and Mac installers for VPython for Python 3. + +At Guido's request, Bruce Sherwood carried out the same update to the IDLE +distributed with Python 2.7. Guido was concerned that with significant +problems reported for IDLE 2.7, there should be an update despite Python 2.7 +itself being essentially frozen. The basic structure for IDLE 3.2 was +carried over. The main changes that had to be made were the change in module +names between Python 2.7 and Python 3.2 (e.g. Tkinter in 2.7 is tkinter in 3.2). + +What's New in IDLE 3.1? +========================= + +*Release date: 27-Jun-09* + +- Use of 'filter' in keybindingDialog.py was causing custom key assignment to + fail. Patch 5707 amaury.forgeotdarc. + + +What's New in IDLE 3.1a1? +========================= + +*Release date: 07-Mar-09* + +- Issue #4815: Offer conversion to UTF-8 if source files have + no encoding declaration and are not encoded in UTF-8. + +- Issue #4008: Fix problems with non-ASCII source files. + +- Issue #4323: Always encode source as UTF-8 without asking + the user (unless a different encoding is declared); remove + user configuration of source encoding; all according to + PEP 3120. + +- Issue #2665: On Windows, an IDLE installation upgraded from an old version + would not start if a custom theme was defined. + +What's New in IDLE 2.7? (UNRELEASED, but merged into 3.1 releases above.) +======================= + +*Release date: XX-XXX-2010* + - idle.py modified and simplified to better support developing experimental versions of IDLE which are not installed in the standard location. @@ -20,37 +92,90 @@ extract port from command line when warnings are present. - Tk 8.5 Text widget requires 'wordprocessor' tabstyle attr to handle - mixed space/tab properly. Issue 5129, patch by Guilherme Polo. - + mixed space/tab properly. Issue 5120, patch by Guilherme Polo. + - Issue #3549: On MacOS the preferences menu was not present +What's New in IDLE 3.0 final? +============================= -What's New in IDLE 2.6? -======================= +*Release date: 03-Dec-2008* -*Release date: 01-Oct-2008* +- IDLE would print a "Unhandled server exception!" message when internal + debugging is enabled. +- Issue #4455: IDLE failed to display the windows list when two windows have + the same title. + +- Issue #4383: When IDLE cannot make the connection to its subprocess, it would + fail to properly display the error message. + + +What's New in IDLE 3.0a3? +========================= + +*Release date: 29-Feb-2008* + +- help() was not paging to the shell. Issue1650. + +- CodeContext was not importing. + +- Corrected two 3.0 compatibility errors reported by Mark Summerfield: + http://mail.python.org/pipermail/python-3000/2007-December/011491.html + +- Shell was not colorizing due to bug introduced at r57998, Bug 1586. + +- Issue #1585: IDLE uses non-existent xrange() function. + + +What's New in IDLE 3.0a2? +========================= + +*Release date: 06-Dec-2007* + +- Windows EOL sequence not converted correctly, encoding error. + Caused file save to fail. Bug 1130. + + +What's New in IDLE 3.0a1? +========================= + +*Release date: 31-Aug-2007* + +- IDLE converted to Python 3000 syntax. + +- Strings became Unicode. + +- CallTips module now uses the inspect module to produce the argspec. + +- IDLE modules now use absolute import instead of implied relative import. + +- atexit call replaces sys.exitfunc. The functionality of delete-exitfunc flag + in config-main.cfg remains unchanged: if set, registered exit functions will + be cleared before IDLE exits. + + +What's New in IDLE 2.6 final? +============================= + +*Release date: 01-Oct-2008*, merged into 3.0 releases detailed above (3.0rc2) + - Issue #2665: On Windows, an IDLE installation upgraded from an old version would not start if a custom theme was defined. - Home / Control-A toggles between left margin and end of leading white - space. Patch 1196903 Jeff Shute. + space. issue1196903, patch by Jeff Shute. -- Improved AutoCompleteWindow logic. Patch 2062 Tal Einat. +- Improved AutoCompleteWindow logic. issue2062, patch by Tal Einat. - Autocompletion of filenames now support alternate separators, e.g. the - '/' char on Windows. Patch 2061 Tal Einat. + '/' char on Windows. issue2061 Patch by Tal Einat. -What's New in IDLE 2.6a1? -========================= - -*Release date: 29-Feb-2008* - - Configured selection highlighting colors were ignored; updating highlighting 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. Index: ObjectBrowser.py =================================================================== --- ObjectBrowser.py (revision 85719) +++ ObjectBrowser.py (working copy) @@ -9,7 +9,7 @@ # XXX TO DO: # - for classes/modules, add "open source" to object browser -from idlelib.TreeWidget import TreeItem, TreeNode, ScrolledCanvas +from .TreeWidget import TreeItem, TreeNode, ScrolledCanvas from repr import Repr @@ -57,15 +57,6 @@ sublist.append(item) return sublist -class InstanceTreeItem(ObjectTreeItem): - def IsExpandable(self): - return True - def GetSubList(self): - sublist = ObjectTreeItem.GetSubList(self) - sublist.insert(0, - make_objecttreeitem("__class__ =", self.object.__class__)) - return sublist - class ClassTreeItem(ObjectTreeItem): def IsExpandable(self): return True @@ -103,25 +94,21 @@ class DictTreeItem(SequenceTreeItem): def keys(self): - keys = self.object.keys() + keys = list(self.object.keys()) try: keys.sort() except: pass return keys -from types import * - dispatch = { - IntType: AtomicObjectTreeItem, - LongType: AtomicObjectTreeItem, - FloatType: AtomicObjectTreeItem, - StringType: AtomicObjectTreeItem, - TupleType: SequenceTreeItem, - ListType: SequenceTreeItem, - DictType: DictTreeItem, - InstanceType: InstanceTreeItem, - ClassType: ClassTreeItem, + int: AtomicObjectTreeItem, + float: AtomicObjectTreeItem, + str: AtomicObjectTreeItem, + tuple: SequenceTreeItem, + list: SequenceTreeItem, + dict: DictTreeItem, + type: ClassTreeItem, } def make_objecttreeitem(labeltext, object, setfunction=None): Index: OutputWindow.py =================================================================== --- OutputWindow.py (revision 85719) +++ OutputWindow.py (working copy) @@ -1,8 +1,8 @@ from Tkinter import * -from idlelib.EditorWindow import EditorWindow +from .EditorWindow import EditorWindow import re import tkMessageBox -from idlelib import IOBinding +from . import IOBinding class OutputWindow(EditorWindow): @@ -35,14 +35,8 @@ # Act as output file def write(self, s, tags=(), mark="insert"): - # Tk assumes that byte strings are Latin-1; - # we assume that they are in the locale's encoding - if isinstance(s, str): - try: - s = unicode(s, IOBinding.encoding) - except UnicodeError: - # some other encoding; let Tcl deal with it - pass + if isinstance(s, (bytes, bytes)): + s = s.decode(IOBinding.encoding, "replace") self.text.insert(mark, s, tags) self.text.see(mark) self.text.update() @@ -57,7 +51,11 @@ # Our own right-button menu rmenu_specs = [ - ("Go to file/line", "<>"), + ("Cut", "<>", "rmenu_check_cut"), + ("Copy", "<>", "rmenu_check_copy"), + ("Paste", "<>", "rmenu_check_paste"), + (None, None, None), + ("Go to file/line", "<>", None) ] file_line_pats = [ Index: ParenMatch.py =================================================================== --- ParenMatch.py (revision 85719) +++ ParenMatch.py (working copy) @@ -5,8 +5,8 @@ parentheses, square brackets, and curly braces. """ -from idlelib.HyperParser import HyperParser -from idlelib.configHandler import idleConf +from .HyperParser import HyperParser +from .configHandler import idleConf _openers = {')':'(',']':'[','}':'{'} CHECK_DELAY = 100 # miliseconds Index: PathBrowser.py =================================================================== --- PathBrowser.py (revision 85719) +++ PathBrowser.py (working copy) @@ -2,8 +2,8 @@ import sys import imp -from idlelib.TreeWidget import TreeItem -from idlelib.ClassBrowser import ClassBrowser, ModuleBrowserTreeItem +from .TreeWidget import TreeItem +from .ClassBrowser import ClassBrowser, ModuleBrowserTreeItem class PathBrowser(ClassBrowser): @@ -86,7 +86,7 @@ return sorted def main(): - from idlelib import PyShell + from . import PyShell PathBrowser(PyShell.flist) if sys.stdin is sys.__stdin__: mainloop() Index: Percolator.py =================================================================== --- Percolator.py (revision 85719) +++ Percolator.py (working copy) @@ -1,5 +1,5 @@ -from idlelib.WidgetRedirector import WidgetRedirector -from idlelib.Delegator import Delegator +from .WidgetRedirector import WidgetRedirector +from .Delegator import Delegator class Percolator: @@ -51,21 +51,21 @@ f.setdelegate(filter.delegate) filter.setdelegate(None) - def main(): + import Tkinter as Tk class Tracer(Delegator): def __init__(self, name): self.name = name Delegator.__init__(self, None) def insert(self, *args): - print self.name, ": insert", args + print(self.name, ": insert", args) self.delegate.insert(*args) def delete(self, *args): - print self.name, ": delete", args + print(self.name, ": delete", args) self.delegate.delete(*args) - root = Tk() + root = Tk.Tk() root.wm_protocol("WM_DELETE_WINDOW", root.quit) - text = Text() + text = Tk.Text() text.pack() text.focus_set() p = Percolator(text) @@ -73,7 +73,7 @@ t2 = Tracer("t2") p.insertfilter(t1) p.insertfilter(t2) - root.mainloop() + root.mainloop() # click close widget to continue... p.removefilter(t2) root.mainloop() p.insertfilter(t2) @@ -81,5 +81,4 @@ root.mainloop() if __name__ == "__main__": - from Tkinter import * main() Index: PyParse.py =================================================================== --- PyParse.py (revision 85719) +++ PyParse.py (working copy) @@ -94,43 +94,39 @@ # Build translation table to map uninteresting chars to "x", open # brackets to "(", and close brackets to ")". -_tran = ['x'] * 256 +_tran = {} +for i in range(256): + _tran[i] = 'x' for ch in "({[": _tran[ord(ch)] = '(' for ch in ")}]": _tran[ord(ch)] = ')' for ch in "\"'\\\n#": _tran[ord(ch)] = ch -_tran = ''.join(_tran) -del ch +del i, ch -try: - UnicodeType = type(unicode("")) -except NameError: - UnicodeType = None - class Parser: def __init__(self, indentwidth, tabwidth): self.indentwidth = indentwidth self.tabwidth = tabwidth - def set_str(self, str): - assert len(str) == 0 or str[-1] == '\n' - if type(str) is UnicodeType: + def set_str(self, s): + assert len(s) == 0 or s[-1] == '\n' + if isinstance(s, str): # The parse functions have no idea what to do with Unicode, so # replace all Unicode characters with "x". This is "safe" # so long as the only characters germane to parsing the structure # of Python are 7-bit ASCII. It's *necessary* because Unicode # strings don't have a .translate() method that supports # deletechars. - uniphooey = str - str = [] - push = str.append + uniphooey = s + s = [] + push = s.append for raw in map(ord, uniphooey): push(raw < 127 and chr(raw) or "x") - str = "".join(str) - self.str = str + s = "".join(s) + self.str = s self.study_level = 0 # Return index of a good place to begin parsing, as close to the Index: PyShell.py =================================================================== --- PyShell.py (revision 85719) +++ PyShell.py (working copy) @@ -3,47 +3,50 @@ import os import os.path import sys -import string import getopt import re import socket import time import threading import traceback +import subprocess import types import linecache from code import InteractiveInterpreter +################################ +#Work around compatibility issues between Python 2 and Python 3 + +def print_to_file(text, outputfile): + print >> outputfile, text + +################################ + try: from Tkinter import * except ImportError: - print>>sys.__stderr__, "** IDLE can't import Tkinter. " \ - "Your Python may not be configured for Tk. **" + print_to_file("** IDLE can't import Tkinter. " \ + "Your Python may not be configured for Tk. **", sys.__stderr__) sys.exit(1) import tkMessageBox -from idlelib.EditorWindow import EditorWindow, fixwordbreaks -from idlelib.FileList import FileList -from idlelib.ColorDelegator import ColorDelegator -from idlelib.UndoDelegator import UndoDelegator -from idlelib.OutputWindow import OutputWindow -from idlelib.configHandler import idleConf -from idlelib import idlever -from idlelib import rpc -from idlelib import Debugger -from idlelib import RemoteDebugger -from idlelib import macosxSupport +from .EditorWindow import EditorWindow, fixwordbreaks +from .FileList import FileList +from .ColorDelegator import ColorDelegator +from .UndoDelegator import UndoDelegator +from .OutputWindow import OutputWindow +from .configHandler import idleConf +from .utils import tb_print_list +from . import idlever +from . import rpc +from . import Debugger +from . import RemoteDebugger +from . import macosxSupport -IDENTCHARS = string.ascii_letters + string.digits + "_" 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 @@ -88,6 +91,7 @@ orig_checkcache is bound at definition time to the original method, allowing it to be patched. + """ cache = linecache.cache save = {} @@ -121,8 +125,14 @@ old_hook() self.io.set_filename_change_hook(filename_changed_hook) - rmenu_specs = [("Set Breakpoint", "<>"), - ("Clear Breakpoint", "<>")] + rmenu_specs = [ + ("Cut", "<>", "rmenu_check_cut"), + ("Copy", "<>", "rmenu_check_copy"), + ("Paste", "<>", "rmenu_check_paste"), + (None, None, None), + ("Set Breakpoint", "<>", None), + ("Clear Breakpoint", "<>", None) + ] def set_breakpoint(self, lineno): text = self.text @@ -241,10 +251,12 @@ self.breakpoints = linenumber_list def ranges_to_linenumbers(self, ranges): + """Convert a tuple of ranges returned by Text.tag_ranges to + line numbers.""" lines = [] for index in range(0, len(ranges), 2): - lineno = int(float(ranges[index])) - end = int(float(ranges[index+1])) + lineno = int(float(str(ranges[index]))) + end = int(float(str(ranges[index+1]))) while lineno < end: lines.append(lineno) lineno += 1 @@ -346,20 +358,18 @@ self.port = PORT rpcclt = None - rpcpid = None + rpcproc = 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.rpcproc = subprocess.Popen([sys.executable] + args[1:]) 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') # Maybe IDLE is installed and is being accessed via sys.path, # or maybe it's not installed and the idle.py script is being # run from the IDLE source directory. @@ -384,7 +394,7 @@ try: self.rpcclt = MyRPCClient(addr) break - except socket.error, err: + except socket.error as err: pass else: self.display_port_binding_error() @@ -405,7 +415,7 @@ self.rpcclt.listening_sock.settimeout(10) try: self.rpcclt.accept() - except socket.timeout, err: + except socket.timeout as err: self.display_no_subprocess_error() return None self.rpcclt.register("stdin", self.tkconsole) @@ -418,6 +428,10 @@ self.poll_subprocess() return self.rpcclt + def get_restart_line(self): + halfbar = ((int(self.tkconsole.width) - 16) // 2) * '=' + return halfbar + ' RESTART ' + halfbar + def restart_subprocess(self): if self.restarting: return self.rpcclt @@ -432,14 +446,14 @@ 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 self.spawn_subprocess() try: self.rpcclt.accept() - except socket.timeout, err: + except socket.timeout as err: self.display_no_subprocess_error() return None self.transfer_path() @@ -448,10 +462,8 @@ if was_executing: console.write('\n') console.showprompt() - halfbar = ((int(console.width) - 16) // 2) * '=' - console.write(halfbar + ' RESTART ' + halfbar) - console.text.mark_set("restart", "end-1c") - console.text.mark_gravity("restart", "left") + console.write(self.get_restart_line()) + console.text.tag_add("restart", "end - 1 line") console.showprompt() # restart subprocess debugger if debug: @@ -473,23 +485,20 @@ 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 and collect status." + if sys.platform[:3] == 'win': try: - os.kill(self.rpcpid, SIGTERM) - except OSError: - # process already terminated: - return - else: - try: - os.waitpid(self.rpcpid, 0) - except OSError: - return + self.rpcproc.kill() + except WindowsError: + pass + else: + self.rpcproc.kill() + self.rpcproc.wait() def transfer_path(self): self.runcommand("""if 1: @@ -520,14 +529,16 @@ console = self.tkconsole.console if how == "OK": if what is not None: - print >>console, repr(what) + print_to_file(repr(what), console) elif how == "EXCEPTION": if self.tkconsole.getvar("<>"): self.remote_stack_viewer() elif how == "ERROR": errmsg = "PyShell.ModifiedInterpreter: Subprocess ERROR:\n" - print >>sys.__stderr__, errmsg, what - print >>console, errmsg, what + print_to_file(errmsg, sys.__stderr__) + print_to_file(what, sys.__stderr__) + print_to_file(errmsg, console) + print_to_file(what, file=console) # we received a response to the currently active seq number: try: self.tkconsole.endexecuting() @@ -552,7 +563,7 @@ This method is called from the subprocess, and by returning from this method we allow the subprocess to unblock. After a bit the shell requests the subprocess to open the remote stack viewer which returns a - static object looking at the last exception. It is queried through + static object looking at the last exceptiopn. It is queried through the RPC mechanism. """ @@ -560,13 +571,13 @@ return def remote_stack_viewer(self): - from idlelib import RemoteObjectBrowser + from . import RemoteObjectBrowser oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {}) if oid is None: self.tkconsole.root.bell() return item = RemoteObjectBrowser.StubObjectTreeItem(self.rpcclt, oid) - from idlelib.TreeWidget import ScrolledCanvas, TreeNode + from .TreeWidget import ScrolledCanvas, TreeNode top = Toplevel(self.tkconsole.root) theme = idleConf.GetOption('main','Theme','name') background = idleConf.GetHighlight(theme, 'normal')['background'] @@ -592,8 +603,8 @@ except (OverflowError, SyntaxError): self.tkconsole.resetoutput() tkerr = self.tkconsole.stderr - print>>tkerr, '*** Error in script or command!\n' - print>>tkerr, 'Traceback (most recent call last):' + print_to_file('*** Error in script or command!\n', tkerr) + print_to_file('Traceback (most recent call last):', tkerr) InteractiveInterpreter.showsyntaxerror(self, filename) self.tkconsole.showprompt() else: @@ -605,14 +616,16 @@ self.more = 0 self.save_warnings_filters = warnings.filters[:] warnings.filterwarnings(action="error", category=SyntaxWarning) - if isinstance(source, types.UnicodeType): - from idlelib import IOBinding - try: - source = source.encode(IOBinding.encoding) - except UnicodeError: - self.tkconsole.resetoutput() - self.write("Unsupported characters in input\n") - return + # at the moment, InteractiveInterpreter expects str + assert isinstance(source, str) + #if isinstance(source, str): + # from . import IOBinding + # try: + # source = source.encode(IOBinding.encoding) + # except UnicodeError: + # self.tkconsole.resetoutput() + # self.write("Unsupported characters in input\n") + # return try: # InteractiveInterpreter.runsource() calls its runcode() method, # which is overridden (see below) @@ -643,59 +656,64 @@ \n""" % (filename,)) def showsyntaxerror(self, filename=None): - """Extend base class method: Add Colorizing + """Override Interactive Interpreter method: Use Colorizing Color the offending position instead of printing it and pointing at it with a caret. """ - text = self.tkconsole.text - stuff = self.unpackerror() - if stuff: - msg, lineno, offset, line = stuff - if lineno == 1: - pos = "iomark + %d chars" % (offset-1) - else: - pos = "iomark linestart + %d lines + %d chars" % \ - (lineno-1, offset-1) - text.tag_add("ERROR", pos) - text.see(pos) - char = text.get(pos) - if char and char in IDENTCHARS: - text.tag_add("ERROR", pos + " wordstart", pos) - self.tkconsole.resetoutput() - self.write("SyntaxError: %s\n" % str(msg)) - else: - self.tkconsole.resetoutput() - InteractiveInterpreter.showsyntaxerror(self, filename) - self.tkconsole.showprompt() - - def unpackerror(self): + tkconsole = self.tkconsole + text = tkconsole.text + text.tag_remove("ERROR", "1.0", "end") type, value, tb = sys.exc_info() - ok = type is SyntaxError - if ok: - try: - msg, (dummy_filename, lineno, offset, line) = value - if not offset: - offset = 0 - except: - ok = 0 - if ok: - return msg, lineno, offset, line + msg = value.msg or "" + lineno = value.lineno or 1 + offset = value.offset or 0 + if offset == 0: + lineno += 1 #mark end of offending line + if lineno == 1: + pos = "iomark + %d chars" % (offset-1) else: - return None + pos = "iomark linestart + %d lines + %d chars" % \ + (lineno-1, offset-1) + tkconsole.colorize_syntax_error(text, pos) + tkconsole.resetoutput() + self.write("SyntaxError: %s\n" % msg) + tkconsole.showprompt() - def showtraceback(self): - "Extend base class method to reset output properly" + def showtraceback(self, temp_filename=None): + """Extend base class method to reset output properly and print an + customized traceback.""" self.tkconsole.resetoutput() self.checklinecache() - InteractiveInterpreter.showtraceback(self) + + typ, value, tb = sys.exc_info() + sys.last_type = typ + sys.last_value = value + sys.last_traceback = tb + tblist = traceback.extract_tb(tb) + del tblist[:1] + sys.stderr.write('\nTraceback (most recent call last):\n') + if temp_filename is not None: + # Replace the name of the temporary file by 'Untitled' + main_fname = 'Untitled' + new_tb = [] + for t in tblist: + fname = main_fname if t[0] == temp_filename else t[0] + new_tb.append((fname, ) + t[1:]) + tblist = new_tb + else: + main_fname = tblist[0][0] + tb_print_list(tblist, main_fname, sys.stdout, sys.stderr) + lines = traceback.format_exception_only(typ, value) + list(map(sys.stderr.write, lines)) + if self.tkconsole.getvar("<>"): self.tkconsole.open_stack_viewer() def checklinecache(self): c = linecache.cache - for key in c.keys(): + for key in list(c.keys()): if key[:1] + key[-1:] != "<>": del c[key] @@ -708,10 +726,10 @@ if self.rpcclt: self.rpcclt.remotequeue("exec", "runcode", (code,), {}) else: - exec code in self.locals + exec(code, self.locals) return 1 - def runcode(self, code): + def runcode(self, code, tempname=None): "Override base class method" if self.tkconsole.executing: self.interp.restart_subprocess() @@ -724,11 +742,11 @@ self.tkconsole.beginexecuting() if not debugger and self.rpcclt is not None: self.active_seq = self.rpcclt.asyncqueue("exec", "runcode", - (code,), {}) + (code, tempname), {}) elif debugger: debugger.run(code, self.locals) else: - exec code in self.locals + exec(code, self.locals) except SystemExit: if not self.tkconsole.closing: if tkMessageBox.askyesno( @@ -743,16 +761,16 @@ raise except: if use_subprocess: - print >>self.tkconsole.stderr, \ - "IDLE internal error in runcode()" + print_to_file("IDLE internal error in runcode()", + self.tkconsole.stderr) self.showtraceback() self.tkconsole.endexecuting() else: if self.tkconsole.canceled: self.tkconsole.canceled = False - print >>self.tkconsole.stderr, "KeyboardInterrupt" + print_to_file("KeyboardInterrupt", self.tkconsole.stderr) else: - self.showtraceback() + self.showtraceback(tempname) finally: if not use_subprocess: try: @@ -815,7 +833,7 @@ # New classes - from idlelib.IdleHistory import History + from .IdleHistory import History def __init__(self, flist=None): if use_subprocess: @@ -839,6 +857,7 @@ # text = self.text text.configure(wrap="char") + text.bind("<>", self.paste_callback) text.bind("<>", self.enter_callback) text.bind("<>", self.linefeed_callback) text.bind("<>", self.cancel_callback) @@ -846,6 +865,8 @@ text.bind("<>", self.open_stack_viewer) text.bind("<>", self.toggle_debugger) text.bind("<>", self.toggle_jit_stack_viewer) + self.color = color = self.ColorDelegator() + self.per.insertfilter(color) if use_subprocess: text.bind("<>", self.view_restart_mark) text.bind("<>", self.restart_shell) @@ -853,19 +874,32 @@ self.save_stdout = sys.stdout self.save_stderr = sys.stderr self.save_stdin = sys.stdin - from idlelib import IOBinding + from . import IOBinding self.stdout = PseudoFile(self, "stdout", IOBinding.encoding) - self.stderr = PseudoFile(self, "stderr", IOBinding.encoding) + self.stderr = PseudoStderrFile(self, encoding=IOBinding.encoding) self.console = PseudoFile(self, "console", IOBinding.encoding) if not use_subprocess: sys.stdout = self.stdout sys.stderr = self.stderr sys.stdin = self + try: + # page help() text to shell. + import pydoc # import must be done here to capture i/o rebinding. + # XXX KBK 27Dec07 use a textView someday, but must work w/o subproc + pydoc.pager = pydoc.plainpager + except: + sys.stderr = sys.__stderr__ + raise # self.history = self.History(self.text) # self.pollinterval = 50 # millisec + # Cleanup functions to be called when endexecuting is called + self._cleanup_funcs = [] + def append_cleanup_func(self, func, *args, **kwargs): + self._cleanup_funcs.append((func, args, kwargs)) + def get_standard_extension_names(self): return idleConf.GetExtensions(shell_only=True) @@ -939,6 +973,10 @@ self.canceled = 0 self.showprompt() + for func, args, kwargs in self._cleanup_funcs: + func(*args, **kwargs) + self._cleanup_funcs = [] + def close(self): "Extend EditorWindow.close()" if self.executing: @@ -986,6 +1024,7 @@ 'Type "copyright", "credits" or "license()" for more information.' def begin(self): + self.text.mark_set("iomark", "insert") self.resetoutput() if use_subprocess: nosub = '' @@ -1012,12 +1051,6 @@ line = self.text.get("iomark", "end-1c") if len(line) == 0: # may be EOF if we quit our mainloop with Ctrl-C line = "\n" - if isinstance(line, unicode): - from idlelib import IOBinding - try: - line = line.encode(IOBinding.encoding) - except UnicodeError: - pass self.resetoutput() if self.canceled: self.canceled = 0 @@ -1030,7 +1063,40 @@ def isatty(self): return True + + def paste_callback(self, event): + text = self.text + tk = text.tk + try: + selection = tk.call('::tk::GetSelection', text, 'CLIPBOARD') + except TclError: + # No selection, probably. + return "break" + oldseparator = text.cget('autoseparators') + if oldseparator: + text.configure(autoseparators=0) + text.edit_separator() + if tk.call('tk', 'windowingsystem') != 'x11': + try: + text.delete('sel.first', 'sel.last') + except TclError: + # Nothing tagged with 'sel', probably. + pass + selection = selection.split('\n')[::-1] + while selection: + line = selection.pop() + if line == '': + text.event_generate('<>') + else: + text.insert('insert', line) + + if oldseparator: + text.edit_separator() + text.configure(autoseparators=1) + + return "break" + def cancel_callback(self, event=None): try: if self.text.compare("sel.first", "!=", "sel.last"): @@ -1135,7 +1201,7 @@ self.text.tag_add("stdin", "iomark", "end-1c") self.text.update_idletasks() if self.reading: - self.top.quit() # Break out of recursive mainloop() in raw_input() + self.top.quit() # Break out of recursive mainloop() else: self.runit() return "break" @@ -1191,14 +1257,28 @@ "(sys.last_traceback is not defined)", master=self.text) return - from idlelib.StackViewer import StackBrowser + from .StackViewer import StackBrowser sv = StackBrowser(self.root, self.flist) def view_restart_mark(self, event=None): - self.text.see("iomark") - self.text.see("restart") + text = self.text + text.see("iomark") + ranges = text.tag_ranges("restart") + if not ranges: + return + restart_line = self.interp.get_restart_line() + for indx in range(len(ranges), 0, -2): + lineno = '%s.0' % str(ranges[indx - 1]).split('.')[0] + start, end = ('%s +1 line' % lineno, '%s +2 lines +1c' % lineno) + content = text.get(start, end)[4:].rstrip() + if content and content[:-2] != restart_line: + break + + text.see(lineno) + def restart_shell(self, event=None): + self.stderr.signaled = False self.interp.restart_subprocess() def showprompt(self): @@ -1220,7 +1300,6 @@ self.text.insert("end-1c", "\n") self.text.mark_set("iomark", "end-1c") self.set_line_and_column() - sys.stdout.softspace = 0 def write(self, s, tags=()): try: @@ -1228,7 +1307,8 @@ OutputWindow.write(self, s, tags, "iomark") self.text.mark_gravity("iomark", "left") except: - pass + raise ###pass # ### 11Aug07 KBK if we are expecting exceptions + # let's find out what they are and be specific. if self.canceled: self.canceled = 0 if not use_subprocess: @@ -1239,7 +1319,6 @@ def __init__(self, shell, tags, encoding=None): self.shell = shell self.tags = tags - self.softspace = 0 self.encoding = encoding def write(self, s): @@ -1255,7 +1334,20 @@ def isatty(self): return True +class PseudoStderrFile(PseudoFile): + def __init__(self, shell, tags="stderr", encoding=None): + PseudoFile.__init__(self, shell, tags, encoding) + self.signaled = False + def write(self, s): + if not self.signaled: + signal_err = idleConf.GetOption('main', 'General', + 'signal-first-error', default=1, type='bool') + if signal_err: + self.shell.top.wakeup(anystate=True) + self.signaled = True + PseudoFile.write(self, s) + usage_msg = """\ USAGE: idle [-deins] [-t title] [file]* @@ -1295,7 +1387,7 @@ Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell window with the title "Baz". -idle -c "import sys; print sys.argv" "foo" +idle -c "import sys; print(sys.argv)" "foo" Open a shell window and run the command, passing "-c" in sys.argv[0] and "foo" in sys.argv[1]. @@ -1304,7 +1396,7 @@ run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in sys.argv[1]. -echo "import sys; print sys.argv" | idle - "foobar" +echo "import sys; print(sys.argv)" | idle - "foobar" Open a shell window, run the script piped in, passing '' in sys.argv[0] and "foobar" in sys.argv[1]. """ @@ -1313,7 +1405,7 @@ global flist, root, use_subprocess use_subprocess = True - enable_shell = True + enable_shell = False enable_edit = False debug = False cmd = None @@ -1321,7 +1413,7 @@ startup = False try: opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:") - except getopt.error, msg: + except getopt.error as msg: sys.stderr.write("Error: %s\n" % str(msg)) sys.stderr.write(usage_msg) sys.exit(2) @@ -1333,8 +1425,7 @@ debug = True enable_shell = True if o == '-e': - enable_edit = True - enable_shell = False + enable_edit = False if o == '-h': sys.stdout.write(usage_msg) sys.exit() @@ -1347,7 +1438,7 @@ if os.path.isfile(script): pass else: - print "No script file: ", script + ("No script file: ", script) sys.exit() enable_shell = True if o == '-s': @@ -1375,16 +1466,17 @@ pathx.append(os.path.dirname(filename)) for dir in pathx: dir = os.path.abspath(dir) - if dir not in sys.path: + if not dir in sys.path: sys.path.insert(0, dir) else: dir = os.getcwd() - if not dir in sys.path: + if dir not in sys.path: sys.path.insert(0, dir) # check the IDLE settings configuration (but command line overrides) 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") @@ -1395,8 +1487,10 @@ if enable_edit: if not (cmd or script): - for filename in args: - flist.open(filename) + for filename in args[:]: + if flist.open(filename) is None: + # filename is a directory actually, disconsider it + args.remove(filename) if not args: flist.new() if enable_shell: Index: README.txt =================================================================== --- README.txt (revision 85719) +++ README.txt (working copy) @@ -45,14 +45,11 @@ IDLE accepts command line arguments. Try idle -h to see the options. -If you find bugs or have suggestions, let us know about them by using the -Python Bug Tracker: +If you find bugs or have suggestions or patches, let us know about +them by using the Python issue tracker: -http://sourceforge.net/projects/python +http://bugs.python.org -Patches are always appreciated at the Python Patch Tracker, and change -requests should be posted to the RFE Tracker. - For further details and links, read the Help files and check the IDLE home page at Index: RemoteDebugger.py =================================================================== --- RemoteDebugger.py (revision 85719) +++ RemoteDebugger.py (working copy) @@ -21,8 +21,8 @@ """ import types -from idlelib import rpc -from idlelib import Debugger +from . import rpc +from . import Debugger debugging = 0 @@ -93,16 +93,13 @@ self.idb.set_return(frame) def get_stack(self, fid, tbid): - ##print >>sys.__stderr__, "get_stack(%r, %r)" % (fid, tbid) frame = frametable[fid] if tbid is None: tb = None else: tb = tracebacktable[tbid] stack, i = self.idb.get_stack(frame, tb) - ##print >>sys.__stderr__, "get_stack() ->", stack stack = [(wrap_frame(frame), k) for frame, k in stack] - ##print >>sys.__stderr__, "get_stack() ->", stack return stack, i def run(self, cmd): @@ -161,13 +158,20 @@ #----------called by a DictProxy---------- def dict_keys(self, did): + raise NotImplemented("dict_keys not public or pickleable") +## dict = dicttable[did] +## return dict.keys() + + ### Needed until dict_keys is type is finished and pickealable. + ### Will probably need to extend rpc.py:SocketIO._proxify at that time. + def dict_keys_list(self, did): dict = dicttable[did] - return dict.keys() + return list(dict.keys()) def dict_item(self, did, key): dict = dicttable[did] value = dict[key] - value = repr(value) + value = repr(value) ### can't pickle module 'builtins' return value #----------end class IdbAdapter---------- @@ -205,7 +209,7 @@ def __getattr__(self, name): if name[:1] == "_": - raise AttributeError, name + raise AttributeError(name) if name == "f_code": return self._get_f_code() if name == "f_globals": @@ -260,16 +264,21 @@ self._oid = oid self._did = did +## def keys(self): +## return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {}) + + # 'temporary' until dict_keys is a pickleable built-in type def keys(self): - return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {}) + return self._conn.remotecall(self._oid, + "dict_keys_list", (self._did,), {}) def __getitem__(self, key): return self._conn.remotecall(self._oid, "dict_item", (self._did, key), {}) def __getattr__(self, name): - ##print >>sys.__stderr__, "failed DictProxy.__getattr__:", name - raise AttributeError, name + ##print("*** Failed DictProxy.__getattr__:", name) + raise AttributeError(name) class GUIAdapter: @@ -279,7 +288,7 @@ self.gui = gui def interaction(self, message, fid, modified_info): - ##print "interaction: (%s, %s, %s)" % (message, fid, modified_info) + ##print("*** Interaction: (%s, %s, %s)" % (message, fid, modified_info)) frame = FrameProxy(self.conn, fid) self.gui.interaction(message, frame, modified_info) @@ -292,9 +301,9 @@ self.shell = shell def call(self, methodname, *args, **kwargs): - ##print "**IdbProxy.call %s %s %s" % (methodname, args, kwargs) + ##print("*** IdbProxy.call %s %s %s" % (methodname, args, kwargs)) value = self.conn.remotecall(self.oid, methodname, args, kwargs) - ##print "**IdbProxy.call %s returns %r" % (methodname, value) + ##print("*** IdbProxy.call %s returns %r" % (methodname, value)) return value def run(self, cmd, locals): Index: RemoteObjectBrowser.py =================================================================== --- RemoteObjectBrowser.py (revision 85719) +++ RemoteObjectBrowser.py (working copy) @@ -1,4 +1,4 @@ -from idlelib import rpc +from . import rpc def remote_object_tree_item(item): wrapper = WrappedObjectTreeItem(item) @@ -18,7 +18,7 @@ def _GetSubList(self): list = self.__item._GetSubList() - return map(remote_object_tree_item, list) + return list(map(remote_object_tree_item, list)) class StubObjectTreeItem: # Lives in IDLE process Index: ReplaceDialog.py =================================================================== --- ReplaceDialog.py (revision 85719) +++ ReplaceDialog.py (working copy) @@ -1,7 +1,7 @@ from Tkinter import * -from idlelib import SearchEngine -from idlelib.SearchDialogBase import SearchDialogBase +from . import SearchEngine +from .SearchDialogBase import SearchDialogBase def replace(text): root = text._root() Index: rpc.py =================================================================== --- rpc.py (revision 85719) +++ rpc.py (working copy) @@ -33,7 +33,7 @@ import select import SocketServer import struct -import cPickle as pickle +import pickle import threading import Queue import traceback @@ -41,7 +41,14 @@ import types import marshal +################################ +#Work around compatibility issues between Python 2 and Python 3 +def print_to_file(text, outputfile): + print >> outputfile, text + +################################ + def unpickle_code(ms): co = marshal.loads(ms) assert isinstance(co, types.CodeType) @@ -61,7 +68,7 @@ # return repr(fn) copy_reg.pickle(types.CodeType, pickle_code, unpickle_code) -# copy_reg.pickle(types.FunctionType, pickle_function, unpickle_function) +# copyreg.pickle(types.FunctionType, pickle_function, unpickle_function) BUFSIZE = 8*1024 LOCALHOST = '127.0.0.1' @@ -104,14 +111,14 @@ raise except: erf = sys.__stderr__ - print>>erf, '\n' + '-'*40 - print>>erf, 'Unhandled server exception!' - print>>erf, 'Thread: %s' % threading.currentThread().getName() - print>>erf, 'Client Address: ', client_address - print>>erf, 'Request: ', repr(request) + print_to_file('\n' + '-'*40, erf) + print_to_file('Unhandled server exception!', erf) + print_to_file('Thread: %s' % threading.current_thread().name, erf) + print_to_file('Client Address: ', client_address, erf) + print_to_file('Request: ', repr(request), erf) traceback.print_exc(file=erf) - print>>erf, '\n*** Unrecoverable, server exiting!' - print>>erf, '-'*40 + print_to_file('\n*** Unrecoverable, server exiting!', erf) + print_to_file('-'*40, erf) os._exit(0) #----------------- end class RPCServer -------------------- @@ -126,7 +133,7 @@ nextseq = 0 def __init__(self, sock, objtable=None, debugging=None): - self.sockthread = threading.currentThread() + self.sockthread = threading.current_thread() if debugging is not None: self.debugging = debugging self.sock = sock @@ -144,15 +151,15 @@ def exithook(self): "override for specific exit action" - os._exit() + os._exit(0) def debug(self, *args): if not self.debugging: return - s = self.location + " " + str(threading.currentThread().getName()) + s = self.location + " " + str(threading.current_thread().name) for a in args: s = s + " " + str(a) - print>>sys.__stderr__, s + print_to_file(s, sys.__stderr__) def register(self, oid, object): self.objtable[oid] = object @@ -201,7 +208,7 @@ except: msg = "*** Internal Error: rpc.py:SocketIO.localcall()\n\n"\ " Object: %s \n Method: %s \n Args: %s\n" - print>>sys.__stderr__, msg % (oid, method, args) + print_to_file(msg % (oid, method, args), sys.__stderr__) traceback.print_exc(file=sys.__stderr__) return ("EXCEPTION", None) @@ -218,7 +225,7 @@ def asynccall(self, oid, methodname, args, kwargs): request = ("CALL", (oid, methodname, args, kwargs)) seq = self.newseq() - if threading.currentThread() != self.sockthread: + if threading.current_thread() != self.sockthread: cvar = threading.Condition() self.cvars[seq] = cvar self.debug(("asynccall:%d:" % seq), oid, methodname, args, kwargs) @@ -228,7 +235,7 @@ def asyncqueue(self, oid, methodname, args, kwargs): request = ("QUEUE", (oid, methodname, args, kwargs)) seq = self.newseq() - if threading.currentThread() != self.sockthread: + if threading.current_thread() != self.sockthread: cvar = threading.Condition() self.cvars[seq] = cvar self.debug(("asyncqueue:%d:" % seq), oid, methodname, args, kwargs) @@ -256,8 +263,8 @@ return None if how == "ERROR": self.debug("decoderesponse: Internal ERROR:", what) - raise RuntimeError, what - raise SystemError, (how, what) + raise RuntimeError(what) + raise SystemError(how, what) def decode_interrupthook(self): "" @@ -287,14 +294,14 @@ def _proxify(self, obj): if isinstance(obj, RemoteProxy): return RPCProxy(self, obj.oid) - if isinstance(obj, types.ListType): - return map(self._proxify, obj) + if isinstance(obj, list): + return list(map(self._proxify, obj)) # XXX Check for other types -- not currently needed return obj def _getresponse(self, myseq, wait): self.debug("_getresponse:myseq:", myseq) - if threading.currentThread() is self.sockthread: + if threading.current_thread() is self.sockthread: # this thread does all reading of requests or responses while 1: response = self.pollresponse(myseq, wait) @@ -323,7 +330,7 @@ try: s = pickle.dumps(message) except pickle.PicklingError: - print >>sys.__stderr__, "Cannot pickle:", repr(message) + print_to_file("Cannot pickle:", repr(message), sys.__stderr__) raise s = struct.pack(" 0: @@ -331,19 +338,19 @@ r, w, x = select.select([], [self.sock], []) n = self.sock.send(s[:BUFSIZE]) except (AttributeError, TypeError): - raise IOError, "socket no longer exists" + raise IOError("socket no longer exists") except socket.error: raise else: s = s[n:] - buffer = "" + buff = b'' bufneed = 4 bufstate = 0 # meaning: 0 => reading count; 1 => reading data def pollpacket(self, wait): self._stage0() - if len(self.buffer) < self.bufneed: + if len(self.buff) < self.bufneed: r, w, x = select.select([self.sock.fileno()], [], [], wait) if len(r) == 0: return None @@ -353,21 +360,21 @@ raise EOFError if len(s) == 0: raise EOFError - self.buffer += s + self.buff += s self._stage0() return self._stage1() def _stage0(self): - if self.bufstate == 0 and len(self.buffer) >= 4: - s = self.buffer[:4] - self.buffer = self.buffer[4:] + if self.bufstate == 0 and len(self.buff) >= 4: + s = self.buff[:4] + self.buff = self.buff[4:] self.bufneed = struct.unpack("= self.bufneed: - packet = self.buffer[:self.bufneed] - self.buffer = self.buffer[self.bufneed:] + if self.bufstate == 1 and len(self.buff) >= self.bufneed: + packet = self.buff[:self.bufneed] + self.buff = self.buff[self.bufneed:] self.bufneed = 4 self.bufstate = 0 return packet @@ -379,10 +386,10 @@ try: message = pickle.loads(packet) except pickle.UnpicklingError: - print >>sys.__stderr__, "-----------------------" - print >>sys.__stderr__, "cannot unpickle packet:", repr(packet) + print_to_file("-----------------------", sys.__stderr__) + print_to_file("cannot unpickle packet:", repr(packet), sys.__stderr__) traceback.print_stack(file=sys.__stderr__) - print >>sys.__stderr__, "-----------------------" + print_to_file("-----------------------", sys.__stderr__) raise return message @@ -503,7 +510,7 @@ SocketServer.BaseRequestHandler.__init__(self, sock, addr, svr) def handle(self): - "handle() method required by SocketServer" + "handle() method required by socketserver" self.mainloop() def get_remote_proxy(self, oid): @@ -524,11 +531,11 @@ def accept(self): working_sock, address = self.listening_sock.accept() if self.debugging: - print>>sys.__stderr__, "****** Connection request from ", address + print_to_file("****** Connection request from ", address, sys.__stderr__) if address[0] == LOCALHOST: SocketIO.__init__(self, working_sock) else: - print>>sys.__stderr__, "** Invalid host: ", address + print_to_file("** Invalid host: ", address, sys.__stderr__) raise socket.error def get_remote_proxy(self, oid): @@ -555,7 +562,7 @@ (name,), {}) return value else: - raise AttributeError, name + raise AttributeError(name) def __getattributes(self): self.__attributes = self.sockio.remotecall(self.oid, @@ -572,9 +579,7 @@ attr = getattr(obj, name) if hasattr(attr, '__call__'): methods[name] = 1 - if type(obj) == types.InstanceType: - _getmethods(obj.__class__, methods) - if type(obj) == types.ClassType: + if isinstance(obj, type): for super in obj.__bases__: _getmethods(super, methods) Index: run.py =================================================================== --- run.py (revision 85719) +++ run.py (working copy) @@ -7,18 +7,27 @@ import threading import Queue -from idlelib import CallTips -from idlelib import AutoComplete +from . import CallTips +from . import AutoComplete +from .utils import tb_print_list -from idlelib import RemoteDebugger -from idlelib import RemoteObjectBrowser -from idlelib import StackViewer -from idlelib import rpc +from . import RemoteDebugger +from . import RemoteObjectBrowser +from . import StackViewer +from . import rpc import __main__ LOCALHOST = '127.0.0.1' +################################ +#Work around compatibility issues between Python 2 and Python 3 + +def print_to_file(text, outputfile): + print >> outputfile, text + +################################ + try: import warnings except ImportError: @@ -73,13 +82,13 @@ assert(len(sys.argv) > 1) port = int(sys.argv[-1]) except: - print>>sys.stderr, "IDLE Subprocess: no IP port passed in sys.argv." + print_to_file("IDLE Subprocess: no IP port passed in sys.argv.", sys.__stderr__) return sys.argv[:] = [""] sockthread = threading.Thread(target=manage_socket, name='SockThread', args=((LOCALHOST, port),)) - sockthread.setDaemon(True) + sockthread.daemon = True sockthread.start() while 1: try: @@ -120,13 +129,14 @@ try: server = MyRPCServer(address, MyHandler) break - except socket.error, err: - print>>sys.__stderr__,"IDLE Subprocess: socket error: "\ - + err.args[1] + ", retrying...." + except socket.error as err: + print_to_file("IDLE Subprocess: socket error: " + err.args[1] + + ", retrying....", sys.__stderr__) + socket_error = err else: - print>>sys.__stderr__, "IDLE Subprocess: Connection to "\ - "IDLE GUI failed, exiting." - show_socket_error(err, address) + print_to_file("IDLE Subprocess: Connection to " + "IDLE GUI failed, exiting.", sys.__stderr__) + show_socket_error(socket_error, address) global exit_now exit_now = True return @@ -134,7 +144,7 @@ def show_socket_error(err, address): import Tkinter - import tkMessageBox + import Tkinter.messagebox as tkMessageBox root = Tkinter.Tk() root.withdraw() if err.args[0] == 61: # connection refused @@ -148,7 +158,7 @@ "Socket Error: %s" % err.args[1]) root.destroy() -def print_exception(): +def print_exception(temp_filename=None): import linecache linecache.checkcache() flush_stdout() @@ -156,14 +166,24 @@ typ, val, tb = excinfo = sys.exc_info() sys.last_type, sys.last_value, sys.last_traceback = excinfo tbe = traceback.extract_tb(tb) - print>>efile, '\nTraceback (most recent call last):' - exclude = ("run.py", "rpc.py", "threading.py", "Queue.py", + print_to_file('Traceback (most recent call last):', efile) + exclude = ("run.py", "rpc.py", "threading.py", "queue.py", "RemoteDebugger.py", "bdb.py") cleanup_traceback(tbe, exclude) - traceback.print_list(tbe, file=efile) + if temp_filename is not None: + # Replace the name of the temporary file by 'Untitled' + main_fname = 'Untitled' + new_tbe = [] + for t in tbe: + fname = main_fname if t[0] == temp_filename else t[0] + new_tbe.append((fname, ) + t[1:]) + tbe = new_tbe + else: + main_fname = tbe[0][0] + tb_print_list(tbe, main_fname, sys.stdout, efile) lines = traceback.format_exception_only(typ, val) for line in lines: - print>>efile, line, + print_to_file(line+'', efile) def cleanup_traceback(tb, exclude): "Remove excluded traces from beginning/end of tb; get cached lines" @@ -185,7 +205,7 @@ if len(tb) == 0: # exception was in IDLE internals, don't prune! tb[:] = orig_tb[:] - print>>sys.stderr, "** IDLE Internal Exception: " + print_to_file("** IDLE Internal Exception: ", sys.stderr) rpchandler = rpc.objecttable['exec'].rpchandler for i in range(len(tb)): fn, ln, nm, line = tb[i] @@ -197,25 +217,19 @@ tb[i] = fn, ln, nm, line def flush_stdout(): - try: - if sys.stdout.softspace: - sys.stdout.softspace = 0 - sys.stdout.write("\n") - except (AttributeError, EOFError): - pass + """XXX How to do this now?""" def exit(): - """Exit subprocess, possibly after first deleting sys.exitfunc + """Exit subprocess, possibly after first clearing exit functions. If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any - sys.exitfunc will be removed before exiting. (VPython support) + functions registered with atexit will be removed before exiting. + (VPython support) """ if no_exitfunc: - try: - del sys.exitfunc - except AttributeError: - pass + import atexit + atexit._clear() sys.exit(0) class MyRPCServer(rpc.RPCServer): @@ -237,14 +251,14 @@ thread.interrupt_main() except: erf = sys.__stderr__ - print>>erf, '\n' + '-'*40 - print>>erf, 'Unhandled server exception!' - print>>erf, 'Thread: %s' % threading.currentThread().getName() - print>>erf, 'Client Address: ', client_address - print>>erf, 'Request: ', repr(request) + print_to_file('\n' + '-'*40, erf) + print_to_file('Unhandled server exception!', erf) + print_to_file('Thread: %s' % threading.current_thread().name, erf) + print_to_file('Client Address: ', client_address, erf) + print_to_file('Request: ', repr(request), erf) traceback.print_exc(file=erf) - print>>erf, '\n*** Unrecoverable, server exiting!' - print>>erf, '-'*40 + print_to_file('\n*** Unrecoverable, server exiting!', erf) + print_to_file('-'*40, erf) quitting = True thread.interrupt_main() @@ -258,7 +272,10 @@ sys.stdin = self.console = self.get_remote_proxy("stdin") sys.stdout = self.get_remote_proxy("stdout") sys.stderr = self.get_remote_proxy("stderr") - from idlelib import IOBinding + # page help() text to shell. + import pydoc # import must be done here to capture i/o binding + pydoc.pager = pydoc.plainpager + from . import IOBinding sys.stdin.encoding = sys.stdout.encoding = \ sys.stderr.encoding = IOBinding.encoding self.interp = self.get_remote_proxy("interp") @@ -289,13 +306,13 @@ self.calltip = CallTips.CallTips() self.autocomplete = AutoComplete.AutoComplete() - def runcode(self, code): + def runcode(self, code, temp_filename=None): global interruptable try: self.usr_exc_info = None interruptable = True try: - exec code in self.locals + exec(code, self.locals) finally: interruptable = False except: @@ -303,7 +320,7 @@ if quitting: exit() # even print a user code SystemExit exception, continue - print_exception() + print_exception(temp_filename) jit = self.rpchandler.console.getvar("<>") if jit: self.rpchandler.interp.open_remote_stack_viewer() Index: ScriptBinding.py =================================================================== --- ScriptBinding.py (revision 85719) +++ ScriptBinding.py (working copy) @@ -23,12 +23,11 @@ import tabnanny import tokenize import tkMessageBox -from idlelib import PyShell +from .EditorWindow import EditorWindow +from . import PyShell, IOBinding -from idlelib.configHandler import idleConf +from .configHandler import idleConf -IDENTCHARS = string.ascii_letters + string.digits + "_" - indent_message = """Error: Inconsistent indentation detected! 1) Your indentation is outright incorrect (easy to fix), OR @@ -53,15 +52,28 @@ self.flist = self.editwin.flist self.root = self.editwin.root + def _cleanup_temp(self, filename, is_temp=True): + if is_temp: + try: + os.unlink(filename) + except OSError: + pass + def check_module_event(self, event): - filename = self.getfilename() - if not filename: - return 'break' - if not self.checksyntax(filename): - return 'break' - if not self.tabnanny(filename): - return 'break' + filename, is_temp = self.getfilename() + ret = None + if filename: + if self.checksyntax(filename): + if not self.tabnanny(filename): + ret = 'break' + else: + ret = 'break' + self._cleanup_temp(filename, is_temp) + else: + ret = 'break' + return ret + def tabnanny(self, filename): f = open(filename, 'r') try: @@ -78,53 +90,38 @@ self.errorbox("Tab/space error", indent_message) return False return True - + def checksyntax(self, filename): self.shell = shell = self.flist.open_shell() saved_stream = shell.get_warning_stream() shell.set_warning_stream(shell.stderr) - f = open(filename, 'r') + f = open(filename, 'rb') source = f.read() f.close() - if '\r' in source: - source = re.sub(r"\r\n", "\n", source) - source = re.sub(r"\r", "\n", source) - if source and source[-1] != '\n': - source = source + '\n' - text = self.editwin.text + if b'\r' in source: + source = source.replace(b'\r\n', b'\n') + source = source.replace(b'\r', b'\n') + if source and source[-1] != ord(b'\n'): + source = source + b'\n' + editwin = self.editwin + text = editwin.text text.tag_remove("ERROR", "1.0", "end") try: - try: - # If successful, return the compiled code - return compile(source, filename, "exec") - except (SyntaxError, OverflowError), err: - try: - msg, (errorfilename, lineno, offset, line) = err - if not errorfilename: - err.args = msg, (filename, lineno, offset, line) - err.filename = filename - self.colorize_syntax_error(msg, lineno, offset) - except: - msg = "*** " + str(err) - self.errorbox("Syntax error", - "There's an error in your program:\n" + msg) - return False + # If successful, return the compiled code + return compile(source, filename, "exec") + except (SyntaxError, OverflowError) as value: + msg = value.msg or "" + lineno = value.lineno or 1 + offset = 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) + editwin.colorize_syntax_error(text, pos) + self.errorbox("SyntaxError", "%-20s" % msg) + return False finally: shell.set_warning_stream(saved_stream) - def colorize_syntax_error(self, msg, lineno, offset): - text = self.editwin.text - pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1) - text.tag_add("ERROR", pos) - char = text.get(pos) - if char and char in IDENTCHARS: - text.tag_add("ERROR", pos + " wordstart", pos) - if '\n' == text.get(pos): # error at line end - text.mark_set("insert", pos) - else: - text.mark_set("insert", pos + "+1c") - text.see(pos) - def run_module_event(self, event): """Run the module after setting up the environment. @@ -134,13 +131,15 @@ add that directory to its sys.path if not already included. """ - filename = self.getfilename() + filename, is_temp = self.getfilename() if not filename: return 'break' code = self.checksyntax(filename) if not code: + self._cleanup_temp(filename, is_temp) return 'break' if not self.tabnanny(filename): + self._cleanup_temp(filename, is_temp) return 'break' shell = self.shell interp = shell.interp @@ -163,7 +162,11 @@ # XXX KBK 03Jul04 When run w/o subprocess, runtime warnings still # go to __stderr__. With subprocess, they go to the shell. # Need to change streams in PyShell.ModifiedInterpreter. - interp.runcode(code) + if is_temp: + interp.runcode(code, filename) + interp.tkconsole.append_cleanup_func(self._cleanup_temp, filename) + else: + interp.runcode(code) return 'break' def getfilename(self): @@ -178,21 +181,38 @@ """ filename = self.editwin.io.filename + is_temp = False if not self.editwin.get_saved(): autosave = idleConf.GetOption('main', 'General', 'autosave', type='bool') - if autosave and filename: - self.editwin.io.save(None) - else: + save_before_run = idleConf.GetOption('main', 'General', + 'save-before-run', default=1, type='bool') + io = self.editwin.io + if filename and autosave: + # This has been saved before and IDLE is configured to not + # prompt before saving this file again. + io.save(None) + elif filename or save_before_run: + # This has been saved before and IDLE is configured to prompt + # before saving this file again, or, this has never been + # saved and the configuration tells it must be saved. reply = self.ask_save_dialog() self.editwin.text.focus_set() if reply == "ok": - self.editwin.io.save(None) + io.save(None) filename = self.editwin.io.filename else: filename = None - return filename + else: + # This has never been saved before but IDLE is configured + # to allow running the present code without explicitly + # saving. + if not save_before_run: + filename = io.save_as_temp(prefix='IDLE_rtmp_') + is_temp = True + return filename, is_temp + def ask_save_dialog(self): msg = "Source Must Be Saved\n" + 5*' ' + "OK to Save?" mb = tkMessageBox.Message(title="Save Before Run or Check", @@ -200,10 +220,10 @@ icon=tkMessageBox.QUESTION, type=tkMessageBox.OKCANCEL, default=tkMessageBox.OK, - master=self.editwin.text) + parent=self.editwin.text) return mb.show() def errorbox(self, title, message): # XXX This should really be a function of EditorWindow... - tkMessageBox.showerror(title, message, master=self.editwin.text) + tkMessageBox.showerror(title, message, parent=self.editwin.text) self.editwin.text.focus_set() Index: ScrolledList.py =================================================================== --- ScrolledList.py (revision 85719) +++ ScrolledList.py (working copy) @@ -124,8 +124,8 @@ root.protocol("WM_DELETE_WINDOW", root.destroy) class MyScrolledList(ScrolledList): def fill_menu(self): self.menu.add_command(label="pass") - def on_select(self, index): print "select", self.get(index) - def on_double(self, index): print "double", self.get(index) + def on_select(self, index): print("select", self.get(index)) + def on_double(self, index): print("double", self.get(index)) s = MyScrolledList(root) for i in range(30): s.append("item %02d" % i) Index: SearchDialog.py =================================================================== --- SearchDialog.py (revision 85719) +++ SearchDialog.py (working copy) @@ -1,7 +1,7 @@ from Tkinter import * -from idlelib import SearchEngine -from idlelib.SearchDialogBase import SearchDialogBase +from . import SearchEngine +from .SearchDialogBase import SearchDialogBase def _setup(text): root = text._root() Index: SearchEngine.py =================================================================== --- SearchEngine.py (revision 85719) +++ SearchEngine.py (working copy) @@ -66,7 +66,7 @@ flags = flags | re.IGNORECASE try: prog = re.compile(pat, flags) - except re.error, what: + except re.error as what: try: msg, col = what except: Index: StackViewer.py =================================================================== --- StackViewer.py (revision 85719) +++ StackViewer.py (working copy) @@ -2,8 +2,8 @@ import sys import linecache -from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas -from idlelib.ObjectBrowser import ObjectTreeItem, make_objecttreeitem +from .TreeWidget import TreeNode, TreeItem, ScrolledCanvas +from .ObjectBrowser import ObjectTreeItem, make_objecttreeitem def StackBrowser(root, flist=None, tb=None, top=None): if top is None: @@ -106,7 +106,7 @@ return len(self.object) > 0 def keys(self): - return self.object.keys() + return list(self.object.keys()) def GetSubList(self): sublist = [] @@ -120,18 +120,3 @@ item = make_objecttreeitem(key + " =", value, setfunction) sublist.append(item) return sublist - - -def _test(): - try: - import testcode - reload(testcode) - except: - sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info() - from Tkinter import Tk - root = Tk() - StackBrowser(None, top=root) - root.mainloop() - -if __name__ == "__main__": - _test() Index: tabbedpages.py =================================================================== --- tabbedpages.py (revision 85719) +++ tabbedpages.py (working copy) @@ -143,8 +143,8 @@ """ # remove all tabs and rows - for tab_name in self._tabs.keys(): - self._tabs.pop(tab_name).destroy() + while self._tabs: + self._tabs.popitem()[1].destroy() self._reset_tab_rows() if not self._tab_names: @@ -159,7 +159,7 @@ # not expanding the tabs with more than one row is very ugly expand_tabs = self.expand_tabs or n_rows > 1 i = 0 # index in self._tab_names - for row_index in xrange(n_rows): + for row_index in range(n_rows): # calculate required number of tabs in this row n_tabs = (len(self._tab_names) - i - 1) // (n_rows - row_index) + 1 tab_names = self._tab_names[i:i + n_tabs] Index: textView.py =================================================================== --- textView.py (revision 85719) +++ textView.py (working copy) @@ -9,7 +9,7 @@ """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 @@ 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,8 +32,12 @@ self.bind('',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) frameButtons = Frame(self) @@ -56,38 +58,46 @@ def Ok(self, event=None): self.destroy() +def view_text(parent, title, text, modal=True): + return TextViewer(parent, title, text, modal) -def view_text(parent, title, text): - TextViewer(parent, title, text) - -def view_file(parent, title, filename, encoding=None): +def view_file(parent, title, filename, encoding=None, modal=True): try: if encoding: import codecs - textFile = codecs.open(filename, 'r') + textFile = codecs.open(filename, 'r', encoding) else: textFile = open(filename, 'r') except IOError: - import tkMessageBox + 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, textFile.read(), modal) if __name__ == '__main__': #test the dialog + import os root=Tk() root.title('textView test') - filename = './textView.py' + filename = os.path.abspath(__file__) 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() Index: TreeWidget.py =================================================================== --- TreeWidget.py (revision 85719) +++ TreeWidget.py (working copy) @@ -18,8 +18,8 @@ from Tkinter import * import imp -from idlelib import ZoomHeight -from idlelib.configHandler import idleConf +from . import ZoomHeight +from .configHandler import idleConf ICONDIR = "Icons" @@ -31,7 +31,7 @@ if os.path.isdir(_icondir): ICONDIR = _icondir elif not os.path.isdir(ICONDIR): - raise RuntimeError, "can't find icon directory (%r)" % (ICONDIR,) + raise RuntimeError("can't find icon directory (%r)" % (ICONDIR,)) def listicons(icondir=ICONDIR): """Utility to display the available icons.""" @@ -452,7 +452,7 @@ # Testing functions def test(): - from idlelib import PyShell + from . import PyShell root = Toplevel(PyShell.root) root.configure(bd=0, bg="yellow") root.focus_set() Index: UndoDelegator.py =================================================================== --- UndoDelegator.py (revision 85719) +++ UndoDelegator.py (working copy) @@ -1,7 +1,7 @@ import string from Tkinter import * -from idlelib.Delegator import Delegator +from .Delegator import Delegator #$ event <> #$ win @@ -15,7 +15,6 @@ #$ win #$ unix - class UndoDelegator(Delegator): max_undo = 1000 @@ -38,9 +37,9 @@ def dump_event(self, event): from pprint import pprint pprint(self.undolist[:self.pointer]) - print "pointer:", self.pointer, - print "saved:", self.saved, - print "can_merge:", self.can_merge, + print "pointer:", self.pointer, ' ', + print "saved:", self.saved, ' ', + print "can_merge:", self.can_merge, ' ', print "get_saved():", self.get_saved() pprint(self.undolist[self.pointer:]) return "break" @@ -337,7 +336,7 @@ return self.depth def main(): - from idlelib.Percolator import Percolator + from .Percolator import Percolator root = Tk() root.wm_protocol("WM_DELETE_WINDOW", root.quit) text = Text() Index: WidgetRedirector.py =================================================================== --- WidgetRedirector.py (revision 85719) +++ WidgetRedirector.py (working copy) @@ -77,6 +77,7 @@ to *args to accomplish that. For an example, see ColorDelegator.py. ''' + operation = str(operation) m = self._operations.get(operation) try: if m: @@ -113,7 +114,7 @@ redir = WidgetRedirector(text) global previous_tcl_fcn def my_insert(*args): - print "insert", args + print("insert", args) previous_tcl_fcn(*args) previous_tcl_fcn = redir.register("insert", my_insert) root.mainloop() Index: WindowList.py =================================================================== --- WindowList.py (revision 85719) +++ WindowList.py (working copy) @@ -20,15 +20,15 @@ def add_windows_to_menu(self, menu): list = [] - for key in self.dict.keys(): + for key in self.dict: window = self.dict[key] try: title = window.get_title() except TclError: continue - list.append((title, window)) + list.append((title, key, window)) list.sort() - for title, window in list: + for title, key, window in list: menu.add_command(label=title, command=window.wakeup) def register_callback(self, callback): @@ -45,8 +45,8 @@ try: callback() except: - print "warning: callback failed in WindowList", \ - sys.exc_type, ":", sys.exc_value + t, v, tb = sys.exc_info() + print("warning: callback failed in WindowList", t, ":", v) registry = WindowList() @@ -77,9 +77,13 @@ # Subclass can override return self.wm_title() - def wakeup(self): + def wakeup(self, anystate=False): + """Signal the user about something important. + + If anystate is true the window will always come forward, otherwise, + this will only happen if the window has been minimized.""" try: - if self.wm_state() == "iconic": + if anystate or self.wm_state() == "iconic": self.wm_withdraw() self.wm_deiconify() self.tkraise() Index: ZoomHeight.py =================================================================== --- ZoomHeight.py (revision 85719) +++ ZoomHeight.py (working copy) @@ -3,7 +3,7 @@ import re import sys -from idlelib import macosxSupport +from . import macosxSupport class ZoomHeight: @@ -35,7 +35,7 @@ elif macosxSupport.runningAsOSXApp(): # The '88' below is a magic number that avoids placing the bottom # of the window below the panel on my machine. I don't know how - # to calculate the correct value for this with tkinter. + # to calculate the correct value for this with Tkinter. newy = 22 newheight = newheight - newy - 88