summaryrefslogtreecommitdiff
path: root/lib/python2.7/idlelib
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python2.7/idlelib')
-rw-r--r--lib/python2.7/idlelib/AutoComplete.py229
-rw-r--r--lib/python2.7/idlelib/AutoCompleteWindow.py407
-rw-r--r--lib/python2.7/idlelib/AutoExpand.py104
-rw-r--r--lib/python2.7/idlelib/Bindings.py91
-rw-r--r--lib/python2.7/idlelib/CREDITS.txt37
-rw-r--r--lib/python2.7/idlelib/CallTipWindow.py161
-rw-r--r--lib/python2.7/idlelib/CallTips.py219
-rw-r--r--lib/python2.7/idlelib/ChangeLog1591
-rw-r--r--lib/python2.7/idlelib/ClassBrowser.py236
-rw-r--r--lib/python2.7/idlelib/CodeContext.py176
-rw-r--r--lib/python2.7/idlelib/ColorDelegator.py258
-rw-r--r--lib/python2.7/idlelib/Debugger.py529
-rw-r--r--lib/python2.7/idlelib/Delegator.py25
-rw-r--r--lib/python2.7/idlelib/EditorWindow.py1704
-rw-r--r--lib/python2.7/idlelib/FileList.py124
-rw-r--r--lib/python2.7/idlelib/FormatParagraph.py195
-rw-r--r--lib/python2.7/idlelib/GrepDialog.py159
-rw-r--r--lib/python2.7/idlelib/HISTORY.txt296
-rw-r--r--lib/python2.7/idlelib/HyperParser.py255
-rw-r--r--lib/python2.7/idlelib/IOBinding.py610
-rw-r--r--lib/python2.7/idlelib/Icons/folder.gifbin0 -> 120 bytes
-rw-r--r--lib/python2.7/idlelib/Icons/idle.icnsbin0 -> 57435 bytes
-rw-r--r--lib/python2.7/idlelib/Icons/idle.icobin0 -> 19790 bytes
-rw-r--r--lib/python2.7/idlelib/Icons/idle_16.gifbin0 -> 1034 bytes
-rw-r--r--lib/python2.7/idlelib/Icons/idle_16.pngbin0 -> 1264 bytes
-rw-r--r--lib/python2.7/idlelib/Icons/idle_32.gifbin0 -> 1435 bytes
-rw-r--r--lib/python2.7/idlelib/Icons/idle_32.pngbin0 -> 2542 bytes
-rw-r--r--lib/python2.7/idlelib/Icons/idle_48.gifbin0 -> 1388 bytes
-rw-r--r--lib/python2.7/idlelib/Icons/idle_48.pngbin0 -> 4710 bytes
-rw-r--r--lib/python2.7/idlelib/Icons/minusnode.gifbin0 -> 96 bytes
-rw-r--r--lib/python2.7/idlelib/Icons/openfolder.gifbin0 -> 125 bytes
-rw-r--r--lib/python2.7/idlelib/Icons/plusnode.gifbin0 -> 79 bytes
-rw-r--r--lib/python2.7/idlelib/Icons/python.gifbin0 -> 585 bytes
-rw-r--r--lib/python2.7/idlelib/Icons/tk.gifbin0 -> 85 bytes
-rw-r--r--lib/python2.7/idlelib/IdleHistory.py104
-rw-r--r--lib/python2.7/idlelib/MultiCall.py430
-rw-r--r--lib/python2.7/idlelib/MultiStatusBar.py47
-rw-r--r--lib/python2.7/idlelib/NEWS.txt1165
-rw-r--r--lib/python2.7/idlelib/ObjectBrowser.py156
-rw-r--r--lib/python2.7/idlelib/OutputWindow.py149
-rw-r--r--lib/python2.7/idlelib/ParenMatch.py178
-rw-r--r--lib/python2.7/idlelib/PathBrowser.py105
-rw-r--r--lib/python2.7/idlelib/Percolator.py103
-rw-r--r--lib/python2.7/idlelib/PyParse.py594
-rw-r--r--lib/python2.7/idlelib/PyShell.py1641
-rw-r--r--lib/python2.7/idlelib/README.txt230
-rw-r--r--lib/python2.7/idlelib/RemoteDebugger.py379
-rw-r--r--lib/python2.7/idlelib/RemoteObjectBrowser.py36
-rw-r--r--lib/python2.7/idlelib/ReplaceDialog.py220
-rw-r--r--lib/python2.7/idlelib/RstripExtension.py33
-rw-r--r--lib/python2.7/idlelib/ScriptBinding.py222
-rw-r--r--lib/python2.7/idlelib/ScrolledList.py145
-rw-r--r--lib/python2.7/idlelib/SearchDialog.py89
-rw-r--r--lib/python2.7/idlelib/SearchDialogBase.py184
-rw-r--r--lib/python2.7/idlelib/SearchEngine.py233
-rw-r--r--lib/python2.7/idlelib/StackViewer.py151
-rw-r--r--lib/python2.7/idlelib/TODO.txt210
-rw-r--r--lib/python2.7/idlelib/ToolTip.py97
-rw-r--r--lib/python2.7/idlelib/TreeWidget.py467
-rw-r--r--lib/python2.7/idlelib/UndoDelegator.py365
-rw-r--r--lib/python2.7/idlelib/WidgetRedirector.py175
-rw-r--r--lib/python2.7/idlelib/WindowList.py90
-rw-r--r--lib/python2.7/idlelib/ZoomHeight.py51
-rw-r--r--lib/python2.7/idlelib/__init__.py8
-rw-r--r--lib/python2.7/idlelib/aboutDialog.py150
-rw-r--r--lib/python2.7/idlelib/config-extensions.def99
-rw-r--r--lib/python2.7/idlelib/config-highlight.def93
-rw-r--r--lib/python2.7/idlelib/config-keys.def214
-rw-r--r--lib/python2.7/idlelib/config-main.def78
-rw-r--r--lib/python2.7/idlelib/configDialog.py1455
-rw-r--r--lib/python2.7/idlelib/configHandler.py772
-rw-r--r--lib/python2.7/idlelib/configHelpSourceEdit.py166
-rw-r--r--lib/python2.7/idlelib/configSectionNameDialog.py92
-rw-r--r--lib/python2.7/idlelib/dynOptionMenuWidget.py57
-rw-r--r--lib/python2.7/idlelib/extend.txt83
-rw-r--r--lib/python2.7/idlelib/help.html715
-rw-r--r--lib/python2.7/idlelib/help.py277
-rw-r--r--lib/python2.7/idlelib/help.txt302
-rw-r--r--lib/python2.7/idlelib/idle.bat4
-rw-r--r--lib/python2.7/idlelib/idle.py13
-rw-r--r--lib/python2.7/idlelib/idle.pyw17
-rw-r--r--lib/python2.7/idlelib/idle_test/README.txt150
-rw-r--r--lib/python2.7/idlelib/idle_test/__init__.py15
-rw-r--r--lib/python2.7/idlelib/idle_test/htest.py403
-rw-r--r--lib/python2.7/idlelib/idle_test/mock_idle.py55
-rw-r--r--lib/python2.7/idlelib/idle_test/mock_tk.py298
-rw-r--r--lib/python2.7/idlelib/idle_test/test_autocomplete.py140
-rw-r--r--lib/python2.7/idlelib/idle_test/test_autoexpand.py141
-rw-r--r--lib/python2.7/idlelib/idle_test/test_calltips.py185
-rw-r--r--lib/python2.7/idlelib/idle_test/test_config_name.py75
-rw-r--r--lib/python2.7/idlelib/idle_test/test_configdialog.py33
-rw-r--r--lib/python2.7/idlelib/idle_test/test_delegator.py37
-rw-r--r--lib/python2.7/idlelib/idle_test/test_editmenu.py101
-rw-r--r--lib/python2.7/idlelib/idle_test/test_formatparagraph.py376
-rw-r--r--lib/python2.7/idlelib/idle_test/test_grep.py82
-rw-r--r--lib/python2.7/idlelib/idle_test/test_helpabout.py52
-rw-r--r--lib/python2.7/idlelib/idle_test/test_hyperparser.py192
-rw-r--r--lib/python2.7/idlelib/idle_test/test_idlehistory.py168
-rw-r--r--lib/python2.7/idlelib/idle_test/test_io.py267
-rw-r--r--lib/python2.7/idlelib/idle_test/test_parenmatch.py121
-rw-r--r--lib/python2.7/idlelib/idle_test/test_pathbrowser.py28
-rw-r--r--lib/python2.7/idlelib/idle_test/test_rstrip.py49
-rw-r--r--lib/python2.7/idlelib/idle_test/test_searchdialogbase.py164
-rw-r--r--lib/python2.7/idlelib/idle_test/test_searchengine.py329
-rw-r--r--lib/python2.7/idlelib/idle_test/test_text.py227
-rw-r--r--lib/python2.7/idlelib/idle_test/test_textview.py96
-rw-r--r--lib/python2.7/idlelib/idle_test/test_warning.py73
-rw-r--r--lib/python2.7/idlelib/idle_test/test_widgetredir.py124
-rw-r--r--lib/python2.7/idlelib/idlever.py12
-rw-r--r--lib/python2.7/idlelib/keybindingDialog.py266
-rw-r--r--lib/python2.7/idlelib/macosxSupport.py237
-rw-r--r--lib/python2.7/idlelib/rpc.py597
-rw-r--r--lib/python2.7/idlelib/run.py375
-rw-r--r--lib/python2.7/idlelib/tabbedpages.py498
-rw-r--r--lib/python2.7/idlelib/textView.py94
115 files changed, 26110 insertions, 0 deletions
diff --git a/lib/python2.7/idlelib/AutoComplete.py b/lib/python2.7/idlelib/AutoComplete.py
new file mode 100644
index 0000000..9381bda
--- /dev/null
+++ b/lib/python2.7/idlelib/AutoComplete.py
@@ -0,0 +1,229 @@
+"""AutoComplete.py - An IDLE extension for automatically completing names.
+
+This extension can complete either attribute names or file names. It can pop
+a window with all available names, for the user to select from.
+"""
+import os
+import sys
+import string
+
+from idlelib.configHandler import idleConf
+
+# This string includes all chars that may be in a file name (without a path
+# separator)
+FILENAME_CHARS = string.ascii_letters + string.digits + os.curdir + "._~#$:-"
+# This string includes all chars that may be in an identifier
+ID_CHARS = string.ascii_letters + string.digits + "_"
+
+# 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
+
+import __main__
+
+SEPS = os.sep
+if os.altsep: # e.g. '/' on Windows...
+ SEPS += os.altsep
+
+class AutoComplete:
+
+ menudefs = [
+ ('edit', [
+ ("Show Completions", "<<force-open-completions>>"),
+ ])
+ ]
+
+ popupwait = idleConf.GetOption("extensions", "AutoComplete",
+ "popupwait", type="int", default=0)
+
+ def __init__(self, editwin=None):
+ self.editwin = editwin
+ if editwin is None: # subprocess and test
+ return
+ self.text = editwin.text
+ self.autocompletewindow = None
+
+ # id of delayed call, and the index of the text insert when the delayed
+ # call was issued. If _delayed_completion_id is None, there is no
+ # delayed call.
+ self._delayed_completion_id = None
+ self._delayed_completion_index = None
+
+ def _make_autocomplete_window(self):
+ return AutoCompleteWindow.AutoCompleteWindow(self.text)
+
+ def _remove_autocomplete_window(self, event=None):
+ if self.autocompletewindow:
+ self.autocompletewindow.hide_window()
+ self.autocompletewindow = None
+
+ def force_open_completions_event(self, event):
+ """Happens when the user really wants to open a completion list, even
+ if a function call is needed.
+ """
+ self.open_completions(True, False, True)
+
+ def try_open_completions_event(self, event):
+ """Happens when it would be nice to open a completion list, but not
+ really necessary, for example after a dot, so function
+ calls won't be made.
+ """
+ lastchar = self.text.get("insert-1c")
+ if lastchar == ".":
+ self._open_completions_later(False, False, False,
+ COMPLETE_ATTRIBUTES)
+ elif lastchar in SEPS:
+ self._open_completions_later(False, False, False,
+ COMPLETE_FILES)
+
+ def autocomplete_event(self, event):
+ """Happens when the user wants to complete his word, and if necessary,
+ open a completion list after that (if there is more than one
+ completion)
+ """
+ if hasattr(event, "mc_state") and event.mc_state:
+ # A modifier was pressed along with the tab, continue as usual.
+ return
+ if self.autocompletewindow and self.autocompletewindow.is_active():
+ self.autocompletewindow.complete()
+ return "break"
+ else:
+ opened = self.open_completions(False, True, True)
+ if opened:
+ return "break"
+
+ def _open_completions_later(self, *args):
+ self._delayed_completion_index = self.text.index("insert")
+ if self._delayed_completion_id is not None:
+ self.text.after_cancel(self._delayed_completion_id)
+ self._delayed_completion_id = \
+ self.text.after(self.popupwait, self._delayed_open_completions,
+ *args)
+
+ def _delayed_open_completions(self, *args):
+ self._delayed_completion_id = None
+ if self.text.index("insert") != self._delayed_completion_index:
+ return
+ self.open_completions(*args)
+
+ def open_completions(self, evalfuncs, complete, userWantsWin, mode=None):
+ """Find the completions and create the AutoCompleteWindow.
+ Return True if successful (no syntax error or so found).
+ if complete is True, then if there's nothing to complete and no
+ start of completion, won't open completions and return False.
+ If mode is given, will open a completion list only in this mode.
+ """
+ # Cancel another delayed call, if it exists.
+ if self._delayed_completion_id is not None:
+ self.text.after_cancel(self._delayed_completion_id)
+ self._delayed_completion_id = None
+
+ hp = HyperParser(self.editwin, "insert")
+ curline = self.text.get("insert linestart", "insert")
+ i = j = len(curline)
+ if hp.is_in_string() and (not mode or mode==COMPLETE_FILES):
+ self._remove_autocomplete_window()
+ mode = COMPLETE_FILES
+ while i and curline[i-1] in FILENAME_CHARS:
+ i -= 1
+ comp_start = curline[i:j]
+ j = i
+ while i and curline[i-1] in FILENAME_CHARS + SEPS:
+ i -= 1
+ comp_what = curline[i:j]
+ elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES):
+ self._remove_autocomplete_window()
+ mode = COMPLETE_ATTRIBUTES
+ while i and curline[i-1] in ID_CHARS:
+ i -= 1
+ comp_start = curline[i:j]
+ if i and curline[i-1] == '.':
+ hp.set_index("insert-%dc" % (len(curline)-(i-1)))
+ comp_what = hp.get_expression()
+ if not comp_what or \
+ (not evalfuncs and comp_what.find('(') != -1):
+ return
+ else:
+ comp_what = ""
+ else:
+ return
+
+ if complete and not comp_what and not comp_start:
+ return
+ comp_lists = self.fetch_completions(comp_what, mode)
+ if not comp_lists[0]:
+ return
+ self.autocompletewindow = self._make_autocomplete_window()
+ return not self.autocompletewindow.show_window(
+ comp_lists, "insert-%dc" % len(comp_start),
+ complete, mode, userWantsWin)
+
+ def fetch_completions(self, what, mode):
+ """Return a pair of lists of completions for something. The first list
+ is a sublist of the second. Both are sorted.
+
+ If there is a Python subprocess, get the comp. list there. Otherwise,
+ either fetch_completions() is running in the subprocess itself or it
+ was called in an IDLE EditorWindow before any script had been run.
+
+ The subprocess environment is that of the most recently run script. If
+ two unrelated modules are being edited some calltips in the current
+ module may be inoperative if the module was not the last to run.
+ """
+ try:
+ rpcclt = self.editwin.flist.pyshell.interp.rpcclt
+ except:
+ rpcclt = None
+ if rpcclt:
+ return rpcclt.remotecall("exec", "get_the_completion_list",
+ (what, mode), {})
+ else:
+ if mode == COMPLETE_ATTRIBUTES:
+ if what == "":
+ namespace = __main__.__dict__.copy()
+ namespace.update(__main__.__builtins__.__dict__)
+ bigl = eval("dir()", namespace)
+ bigl.sort()
+ if "__all__" in bigl:
+ smalll = sorted(eval("__all__", namespace))
+ else:
+ smalll = [s for s in bigl if s[:1] != '_']
+ else:
+ try:
+ entity = self.get_entity(what)
+ bigl = dir(entity)
+ bigl.sort()
+ if "__all__" in bigl:
+ smalll = sorted(entity.__all__)
+ else:
+ smalll = [s for s in bigl if s[:1] != '_']
+ except:
+ return [], []
+
+ elif mode == COMPLETE_FILES:
+ if what == "":
+ what = "."
+ try:
+ expandedpath = os.path.expanduser(what)
+ bigl = os.listdir(expandedpath)
+ bigl.sort()
+ smalll = [s for s in bigl if s[:1] != '.']
+ except OSError:
+ return [], []
+
+ if not smalll:
+ smalll = bigl
+ return smalll, bigl
+
+ def get_entity(self, name):
+ """Lookup name in a namespace spanning sys.modules and __main.dict__"""
+ namespace = sys.modules.copy()
+ namespace.update(__main__.__dict__)
+ return eval(name, namespace)
+
+
+if __name__ == '__main__':
+ from unittest import main
+ main('idlelib.idle_test.test_autocomplete', verbosity=2)
diff --git a/lib/python2.7/idlelib/AutoCompleteWindow.py b/lib/python2.7/idlelib/AutoCompleteWindow.py
new file mode 100644
index 0000000..205a29b
--- /dev/null
+++ b/lib/python2.7/idlelib/AutoCompleteWindow.py
@@ -0,0 +1,407 @@
+"""
+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
+
+HIDE_VIRTUAL_EVENT_NAME = "<<autocompletewindow-hide>>"
+HIDE_SEQUENCES = ("<FocusOut>", "<ButtonPress>")
+KEYPRESS_VIRTUAL_EVENT_NAME = "<<autocompletewindow-keypress>>"
+# We need to bind event beyond <Key> so that the function will be called
+# before the default specific IDLE function
+KEYPRESS_SEQUENCES = ("<Key>", "<Key-BackSpace>", "<Key-Return>", "<Key-Tab>",
+ "<Key-Up>", "<Key-Down>", "<Key-Home>", "<Key-End>",
+ "<Key-Prior>", "<Key-Next>")
+KEYRELEASE_VIRTUAL_EVENT_NAME = "<<autocompletewindow-keyrelease>>"
+KEYRELEASE_SEQUENCE = "<KeyRelease>"
+LISTUPDATE_SEQUENCE = "<B1-ButtonRelease>"
+WINCONFIG_SEQUENCE = "<Configure>"
+DOUBLECLICK_SEQUENCE = "<B1-Double-ButtonRelease>"
+
+class AutoCompleteWindow:
+
+ def __init__(self, widget):
+ # The widget (Text) on which we place the AutoCompleteWindow
+ self.widget = widget
+ # The widgets we create
+ self.autocompletewindow = self.listbox = self.scrollbar = None
+ # The default foreground and background of a selection. Saved because
+ # they are changed to the regular colors of list items when the
+ # completion start is not a prefix of the selected completion
+ self.origselforeground = self.origselbackground = None
+ # The list of completions
+ self.completions = None
+ # A list with more completions, or None
+ self.morecompletions = None
+ # The completion mode. Either AutoComplete.COMPLETE_ATTRIBUTES or
+ # AutoComplete.COMPLETE_FILES
+ self.mode = None
+ # The current completion start, on the text box (a string)
+ self.start = None
+ # The index of the start of the completion
+ self.startindex = None
+ # The last typed start, used so that when the selection changes,
+ # the new start will be as close as possible to the last typed one.
+ self.lasttypedstart = None
+ # Do we have an indication that the user wants the completion window
+ # (for example, he clicked the list)
+ self.userwantswindow = None
+ # event ids
+ self.hideid = self.keypressid = self.listupdateid = self.winconfigid \
+ = self.keyreleaseid = self.doubleclickid = None
+ # Flag set if last keypress was a tab
+ self.lastkey_was_tab = False
+
+ def _change_start(self, newstart):
+ min_len = min(len(self.start), len(newstart))
+ i = 0
+ while i < min_len and self.start[i] == newstart[i]:
+ i += 1
+ if i < len(self.start):
+ self.widget.delete("%s+%dc" % (self.startindex, i),
+ "%s+%dc" % (self.startindex, len(self.start)))
+ if i < len(newstart):
+ self.widget.insert("%s+%dc" % (self.startindex, i),
+ newstart[i:])
+ self.start = newstart
+
+ def _binary_search(self, s):
+ """Find the first index in self.completions where completions[i] is
+ greater or equal to s, or the last index if there is no such
+ one."""
+ i = 0; j = len(self.completions)
+ while j > i:
+ m = (i + j) // 2
+ if self.completions[m] >= s:
+ j = m
+ else:
+ i = m + 1
+ return min(i, len(self.completions)-1)
+
+ def _complete_string(self, s):
+ """Assuming that s is the prefix of a string in self.completions,
+ return the longest string which is a prefix of all the strings which
+ s is a prefix of them. If s is not a prefix of a string, return s."""
+ first = self._binary_search(s)
+ if self.completions[first][:len(s)] != s:
+ # There is not even one completion which s is a prefix of.
+ return s
+ # Find the end of the range of completions where s is a prefix of.
+ i = first + 1
+ j = len(self.completions)
+ while j > i:
+ m = (i + j) // 2
+ if self.completions[m][:len(s)] != s:
+ j = m
+ else:
+ i = m + 1
+ last = i-1
+
+ if first == last: # only one possible completion
+ return self.completions[first]
+
+ # We should return the maximum prefix of first and last
+ first_comp = self.completions[first]
+ last_comp = self.completions[last]
+ min_len = min(len(first_comp), len(last_comp))
+ i = len(s)
+ while i < min_len and first_comp[i] == last_comp[i]:
+ i += 1
+ return first_comp[:i]
+
+ def _selection_changed(self):
+ """Should be called when the selection of the Listbox has changed.
+ Updates the Listbox display and calls _change_start."""
+ cursel = int(self.listbox.curselection()[0])
+
+ self.listbox.see(cursel)
+
+ lts = self.lasttypedstart
+ selstart = self.completions[cursel]
+ if self._binary_search(lts) == cursel:
+ newstart = lts
+ else:
+ min_len = min(len(lts), len(selstart))
+ i = 0
+ while i < min_len and lts[i] == selstart[i]:
+ i += 1
+ newstart = selstart[:i]
+ self._change_start(newstart)
+
+ if self.completions[cursel][:len(self.start)] == self.start:
+ # start is a prefix of the selected completion
+ self.listbox.configure(selectbackground=self.origselbackground,
+ selectforeground=self.origselforeground)
+ else:
+ self.listbox.configure(selectbackground=self.listbox.cget("bg"),
+ selectforeground=self.listbox.cget("fg"))
+ # If there are more completions, show them, and call me again.
+ if self.morecompletions:
+ self.completions = self.morecompletions
+ self.morecompletions = None
+ self.listbox.delete(0, END)
+ for item in self.completions:
+ self.listbox.insert(END, item)
+ self.listbox.select_set(self._binary_search(self.start))
+ self._selection_changed()
+
+ def show_window(self, comp_lists, index, complete, mode, userWantsWin):
+ """Show the autocomplete list, bind events.
+ If complete is True, complete the text, and if there is exactly one
+ matching completion, don't open a list."""
+ # Handle the start we already have
+ self.completions, self.morecompletions = comp_lists
+ self.mode = mode
+ self.startindex = self.widget.index(index)
+ self.start = self.widget.get(self.startindex, "insert")
+ if complete:
+ completed = self._complete_string(self.start)
+ start = self.start
+ self._change_start(completed)
+ i = self._binary_search(completed)
+ if self.completions[i] == completed and \
+ (i == len(self.completions)-1 or
+ self.completions[i+1][:len(completed)] != completed):
+ # There is exactly one matching completion
+ return completed == start
+ self.userwantswindow = userWantsWin
+ self.lasttypedstart = self.start
+
+ # Put widgets in place
+ self.autocompletewindow = acw = Toplevel(self.widget)
+ # Put it in a position so that it is not seen.
+ acw.wm_geometry("+10000+10000")
+ # Make it float
+ acw.wm_overrideredirect(1)
+ try:
+ # This command is only needed and available on Tk >= 8.4.0 for OSX
+ # Without it, call tips intrude on the typing process by grabbing
+ # the focus.
+ acw.tk.call("::tk::unsupported::MacWindowStyle", "style", acw._w,
+ "help", "noActivates")
+ except TclError:
+ pass
+ self.scrollbar = scrollbar = Scrollbar(acw, orient=VERTICAL)
+ self.listbox = listbox = Listbox(acw, yscrollcommand=scrollbar.set,
+ exportselection=False, bg="white")
+ for item in self.completions:
+ listbox.insert(END, item)
+ self.origselforeground = listbox.cget("selectforeground")
+ self.origselbackground = listbox.cget("selectbackground")
+ scrollbar.config(command=listbox.yview)
+ scrollbar.pack(side=RIGHT, fill=Y)
+ listbox.pack(side=LEFT, fill=BOTH, expand=True)
+ acw.lift() # work around bug in Tk 8.5.18+ (issue #24570)
+
+ # Initialize the listbox selection
+ self.listbox.select_set(self._binary_search(self.start))
+ self._selection_changed()
+
+ # bind events
+ self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME,
+ self.hide_event)
+ for seq in HIDE_SEQUENCES:
+ self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq)
+ self.keypressid = self.widget.bind(KEYPRESS_VIRTUAL_EVENT_NAME,
+ self.keypress_event)
+ for seq in KEYPRESS_SEQUENCES:
+ self.widget.event_add(KEYPRESS_VIRTUAL_EVENT_NAME, seq)
+ self.keyreleaseid = self.widget.bind(KEYRELEASE_VIRTUAL_EVENT_NAME,
+ self.keyrelease_event)
+ self.widget.event_add(KEYRELEASE_VIRTUAL_EVENT_NAME,KEYRELEASE_SEQUENCE)
+ self.listupdateid = listbox.bind(LISTUPDATE_SEQUENCE,
+ self.listselect_event)
+ self.winconfigid = acw.bind(WINCONFIG_SEQUENCE, self.winconfig_event)
+ self.doubleclickid = listbox.bind(DOUBLECLICK_SEQUENCE,
+ self.doubleclick_event)
+
+ def winconfig_event(self, event):
+ if not self.is_active():
+ return
+ # Position the completion list window
+ text = self.widget
+ text.see(self.startindex)
+ x, y, cx, cy = text.bbox(self.startindex)
+ acw = self.autocompletewindow
+ acw_width, acw_height = acw.winfo_width(), acw.winfo_height()
+ text_width, text_height = text.winfo_width(), text.winfo_height()
+ new_x = text.winfo_rootx() + min(x, max(0, text_width - acw_width))
+ new_y = text.winfo_rooty() + y
+ if (text_height - (y + cy) >= acw_height # enough height below
+ or y < acw_height): # not enough height above
+ # place acw below current line
+ new_y += cy
+ else:
+ # place acw above current line
+ new_y -= acw_height
+ acw.wm_geometry("+%d+%d" % (new_x, new_y))
+
+ def hide_event(self, event):
+ if not self.is_active():
+ return
+ self.hide_window()
+
+ def listselect_event(self, event):
+ if not self.is_active():
+ return
+ self.userwantswindow = True
+ cursel = int(self.listbox.curselection()[0])
+ self._change_start(self.completions[cursel])
+
+ def doubleclick_event(self, event):
+ # Put the selected completion in the text, and close the list
+ cursel = int(self.listbox.curselection()[0])
+ self._change_start(self.completions[cursel])
+ self.hide_window()
+
+ def keypress_event(self, event):
+ if not self.is_active():
+ return
+ keysym = event.keysym
+ if hasattr(event, "mc_state"):
+ state = event.mc_state
+ else:
+ state = 0
+ if keysym != "Tab":
+ self.lastkey_was_tab = False
+ if (len(keysym) == 1 or keysym in ("underscore", "BackSpace")
+ or (self.mode == COMPLETE_FILES and keysym in
+ ("period", "minus"))) \
+ and not (state & ~MC_SHIFT):
+ # Normal editing of text
+ if len(keysym) == 1:
+ self._change_start(self.start + keysym)
+ elif keysym == "underscore":
+ self._change_start(self.start + '_')
+ elif keysym == "period":
+ self._change_start(self.start + '.')
+ elif keysym == "minus":
+ self._change_start(self.start + '-')
+ else:
+ # keysym == "BackSpace"
+ if len(self.start) == 0:
+ self.hide_window()
+ return
+ self._change_start(self.start[:-1])
+ self.lasttypedstart = self.start
+ self.listbox.select_clear(0, int(self.listbox.curselection()[0]))
+ self.listbox.select_set(self._binary_search(self.start))
+ self._selection_changed()
+ return "break"
+
+ elif keysym == "Return":
+ self.hide_window()
+ return
+
+ elif (self.mode == COMPLETE_ATTRIBUTES and keysym in
+ ("period", "space", "parenleft", "parenright", "bracketleft",
+ "bracketright")) or \
+ (self.mode == COMPLETE_FILES and keysym in
+ ("slash", "backslash", "quotedbl", "apostrophe")) \
+ and not (state & ~MC_SHIFT):
+ # If start is a prefix of the selection, but is not '' when
+ # completing file names, put the whole
+ # selected completion. Anyway, close the list.
+ cursel = int(self.listbox.curselection()[0])
+ if self.completions[cursel][:len(self.start)] == self.start \
+ and (self.mode == COMPLETE_ATTRIBUTES or self.start):
+ self._change_start(self.completions[cursel])
+ self.hide_window()
+ return
+
+ elif keysym in ("Home", "End", "Prior", "Next", "Up", "Down") and \
+ not state:
+ # Move the selection in the listbox
+ self.userwantswindow = True
+ cursel = int(self.listbox.curselection()[0])
+ if keysym == "Home":
+ newsel = 0
+ elif keysym == "End":
+ newsel = len(self.completions)-1
+ elif keysym in ("Prior", "Next"):
+ jump = self.listbox.nearest(self.listbox.winfo_height()) - \
+ self.listbox.nearest(0)
+ if keysym == "Prior":
+ newsel = max(0, cursel-jump)
+ else:
+ assert keysym == "Next"
+ newsel = min(len(self.completions)-1, cursel+jump)
+ elif keysym == "Up":
+ newsel = max(0, cursel-1)
+ else:
+ assert keysym == "Down"
+ newsel = min(len(self.completions)-1, cursel+1)
+ self.listbox.select_clear(cursel)
+ self.listbox.select_set(newsel)
+ self._selection_changed()
+ self._change_start(self.completions[newsel])
+ return "break"
+
+ elif (keysym == "Tab" and not state):
+ if self.lastkey_was_tab:
+ # two tabs in a row; insert current selection and close acw
+ cursel = int(self.listbox.curselection()[0])
+ self._change_start(self.completions[cursel])
+ self.hide_window()
+ return "break"
+ else:
+ # first tab; let AutoComplete handle the completion
+ self.userwantswindow = True
+ self.lastkey_was_tab = True
+ return
+
+ elif any(s in keysym for s in ("Shift", "Control", "Alt",
+ "Meta", "Command", "Option")):
+ # A modifier key, so ignore
+ return
+
+ else:
+ # Unknown event, close the window and let it through.
+ self.hide_window()
+ return
+
+ def keyrelease_event(self, event):
+ if not self.is_active():
+ return
+ if self.widget.index("insert") != \
+ self.widget.index("%s+%dc" % (self.startindex, len(self.start))):
+ # If we didn't catch an event which moved the insert, close window
+ self.hide_window()
+
+ def is_active(self):
+ return self.autocompletewindow is not None
+
+ def complete(self):
+ self._change_start(self._complete_string(self.start))
+ # The selection doesn't change.
+
+ def hide_window(self):
+ if not self.is_active():
+ return
+
+ # unbind events
+ for seq in HIDE_SEQUENCES:
+ self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq)
+ self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid)
+ self.hideid = None
+ for seq in KEYPRESS_SEQUENCES:
+ self.widget.event_delete(KEYPRESS_VIRTUAL_EVENT_NAME, seq)
+ self.widget.unbind(KEYPRESS_VIRTUAL_EVENT_NAME, self.keypressid)
+ self.keypressid = None
+ self.widget.event_delete(KEYRELEASE_VIRTUAL_EVENT_NAME,
+ KEYRELEASE_SEQUENCE)
+ self.widget.unbind(KEYRELEASE_VIRTUAL_EVENT_NAME, self.keyreleaseid)
+ self.keyreleaseid = None
+ self.listbox.unbind(LISTUPDATE_SEQUENCE, self.listupdateid)
+ self.listupdateid = None
+ self.autocompletewindow.unbind(WINCONFIG_SEQUENCE, self.winconfigid)
+ self.winconfigid = None
+
+ # destroy widgets
+ self.scrollbar.destroy()
+ self.scrollbar = None
+ self.listbox.destroy()
+ self.listbox = None
+ self.autocompletewindow.destroy()
+ self.autocompletewindow = None
diff --git a/lib/python2.7/idlelib/AutoExpand.py b/lib/python2.7/idlelib/AutoExpand.py
new file mode 100644
index 0000000..7059054
--- /dev/null
+++ b/lib/python2.7/idlelib/AutoExpand.py
@@ -0,0 +1,104 @@
+'''Complete the current word before the cursor with words in the editor.
+
+Each menu selection or shortcut key selection replaces the word with a
+different word with the same prefix. The search for matches begins
+before the target and moves toward the top of the editor. It then starts
+after the cursor and moves down. It then returns to the original word and
+the cycle starts again.
+
+Changing the current text line or leaving the cursor in a different
+place before requesting the next selection causes AutoExpand to reset
+its state.
+
+This is an extension file and there is only one instance of AutoExpand.
+'''
+import string
+import re
+
+###$ event <<expand-word>>
+###$ win <Alt-slash>
+###$ unix <Alt-slash>
+
+class AutoExpand:
+
+ menudefs = [
+ ('edit', [
+ ('E_xpand Word', '<<expand-word>>'),
+ ]),
+ ]
+
+ wordchars = string.ascii_letters + string.digits + "_"
+
+ def __init__(self, editwin):
+ self.text = editwin.text
+ self.state = None
+
+ def expand_word_event(self, event):
+ "Replace the current word with the next expansion."
+ curinsert = self.text.index("insert")
+ curline = self.text.get("insert linestart", "insert lineend")
+ if not self.state:
+ words = self.getwords()
+ index = 0
+ else:
+ words, index, insert, line = self.state
+ if insert != curinsert or line != curline:
+ words = self.getwords()
+ index = 0
+ if not words:
+ self.text.bell()
+ return "break"
+ word = self.getprevword()
+ self.text.delete("insert - %d chars" % len(word), "insert")
+ newword = words[index]
+ index = (index + 1) % len(words)
+ if index == 0:
+ self.text.bell() # Warn we cycled around
+ self.text.insert("insert", newword)
+ curinsert = self.text.index("insert")
+ curline = self.text.get("insert linestart", "insert lineend")
+ self.state = words, index, curinsert, curline
+ return "break"
+
+ def getwords(self):
+ "Return a list of words that match the prefix before the cursor."
+ word = self.getprevword()
+ if not word:
+ return []
+ before = self.text.get("1.0", "insert wordstart")
+ wbefore = re.findall(r"\b" + word + r"\w+\b", before)
+ del before
+ after = self.text.get("insert wordend", "end")
+ wafter = re.findall(r"\b" + word + r"\w+\b", after)
+ del after
+ if not wbefore and not wafter:
+ return []
+ words = []
+ dict = {}
+ # search backwards through words before
+ wbefore.reverse()
+ for w in wbefore:
+ if dict.get(w):
+ continue
+ words.append(w)
+ dict[w] = w
+ # search onwards through words after
+ for w in wafter:
+ if dict.get(w):
+ continue
+ words.append(w)
+ dict[w] = w
+ words.append(word)
+ return words
+
+ def getprevword(self):
+ "Return the word prefix before the cursor."
+ line = self.text.get("insert linestart", "insert")
+ i = len(line)
+ while i > 0 and line[i-1] in self.wordchars:
+ i = i-1
+ return line[i:]
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2)
diff --git a/lib/python2.7/idlelib/Bindings.py b/lib/python2.7/idlelib/Bindings.py
new file mode 100644
index 0000000..2fd8532
--- /dev/null
+++ b/lib/python2.7/idlelib/Bindings.py
@@ -0,0 +1,91 @@
+"""Define the menu contents, hotkeys, and event bindings.
+
+There is additional configuration information in the EditorWindow class (and
+subclasses): the menus are created there based on the menu_specs (class)
+variable, and menus not created are silently skipped in the code here. This
+makes it possible, for example, to define a Debug menu which is only present in
+the PythonShell window, and a Format menu which is only present in the Editor
+windows.
+
+"""
+from idlelib.configHandler import idleConf
+
+# Warning: menudefs is altered in macosxSupport.overrideRootMenu()
+# after it is determined that an OS X Aqua Tk is in use,
+# which cannot be done until after Tk() is first called.
+# Do not alter the 'file', 'options', or 'help' cascades here
+# without altering overrideRootMenu() as well.
+# TODO: Make this more robust
+
+menudefs = [
+ # underscore prefixes character to underscore
+ ('file', [
+ ('_New File', '<<open-new-window>>'),
+ ('_Open...', '<<open-window-from-file>>'),
+ ('Open _Module...', '<<open-module>>'),
+ ('Class _Browser', '<<open-class-browser>>'),
+ ('_Path Browser', '<<open-path-browser>>'),
+ None,
+ ('_Save', '<<save-window>>'),
+ ('Save _As...', '<<save-window-as-file>>'),
+ ('Save Cop_y As...', '<<save-copy-of-window-as-file>>'),
+ None,
+ ('Prin_t Window', '<<print-window>>'),
+ None,
+ ('_Close', '<<close-window>>'),
+ ('E_xit', '<<close-all-windows>>'),
+ ]),
+ ('edit', [
+ ('_Undo', '<<undo>>'),
+ ('_Redo', '<<redo>>'),
+ None,
+ ('Cu_t', '<<cut>>'),
+ ('_Copy', '<<copy>>'),
+ ('_Paste', '<<paste>>'),
+ ('Select _All', '<<select-all>>'),
+ None,
+ ('_Find...', '<<find>>'),
+ ('Find A_gain', '<<find-again>>'),
+ ('Find _Selection', '<<find-selection>>'),
+ ('Find in Files...', '<<find-in-files>>'),
+ ('R_eplace...', '<<replace>>'),
+ ('Go to _Line', '<<goto-line>>'),
+ ]),
+('format', [
+ ('_Indent Region', '<<indent-region>>'),
+ ('_Dedent Region', '<<dedent-region>>'),
+ ('Comment _Out Region', '<<comment-region>>'),
+ ('U_ncomment Region', '<<uncomment-region>>'),
+ ('Tabify Region', '<<tabify-region>>'),
+ ('Untabify Region', '<<untabify-region>>'),
+ ('Toggle Tabs', '<<toggle-tabs>>'),
+ ('New Indent Width', '<<change-indentwidth>>'),
+ ]),
+ ('run', [
+ ('Python Shell', '<<open-python-shell>>'),
+ ]),
+ ('shell', [
+ ('_View Last Restart', '<<view-restart>>'),
+ ('_Restart Shell', '<<restart-shell>>'),
+ None,
+ ('_Interrupt Execution', '<<interrupt-execution>>'),
+ ]),
+ ('debug', [
+ ('_Go to File/Line', '<<goto-file-line>>'),
+ ('!_Debugger', '<<toggle-debugger>>'),
+ ('_Stack Viewer', '<<open-stack-viewer>>'),
+ ('!_Auto-open Stack Viewer', '<<toggle-jit-stack-viewer>>'),
+ ]),
+ ('options', [
+ ('Configure _IDLE', '<<open-config-dialog>>'),
+ None,
+ ]),
+ ('help', [
+ ('_About IDLE', '<<about-idle>>'),
+ None,
+ ('_IDLE Help', '<<help>>'),
+ ('Python _Docs', '<<python-docs>>'),
+ ]),
+]
+
+default_keydefs = idleConf.GetCurrentKeySet()
diff --git a/lib/python2.7/idlelib/CREDITS.txt b/lib/python2.7/idlelib/CREDITS.txt
new file mode 100644
index 0000000..3a50eb8
--- /dev/null
+++ b/lib/python2.7/idlelib/CREDITS.txt
@@ -0,0 +1,37 @@
+Guido van Rossum, as well as being the creator of the Python language, is the
+original creator of IDLE. Other contributors prior to Version 0.8 include
+Mark Hammond, Jeremy Hylton, Tim Peters, and Moshe Zadka.
+
+IDLE's recent development was carried out in the SF IDLEfork project. The
+objective was to develop a version of IDLE which had an execution environment
+which could be initialized prior to each run of user code.
+
+The IDLEfork project was initiated by David Scherer, with some help from Peter
+Schneider-Kamp and Nicholas Riley. David wrote the first version of the RPC
+code and designed a fast turn-around environment for VPython. Guido developed
+the RPC code and Remote Debugger currently integrated in IDLE. Bruce Sherwood
+contributed considerable time testing and suggesting improvements.
+
+Besides David and Guido, the main developers who were active on IDLEfork
+are Stephen M. Gava, who implemented the configuration GUI, the new
+configuration system, and the About dialog, and Kurt B. Kaiser, who completed
+the integration of the RPC and remote debugger, implemented the threaded
+subprocess, and made a number of usability enhancements.
+
+Other contributors include Raymond Hettinger, Tony Lownds (Mac integration),
+Neal Norwitz (code check and clean-up), Ronald Oussoren (Mac integration),
+Noam Raphael (Code Context, Call Tips, many other patches), and Chui Tey (RPC
+integration, debugger integration and persistent breakpoints).
+
+Scott David Daniels, Tal Einat, Hernan Foffani, Christos Georgiou,
+Jim Jewett, Martin v. Löwis, Jason Orendorff, Guilherme Polo, Josh Robb,
+Nigel Rowe, Bruce Sherwood, Jeff Shute, and Weeble have submitted useful
+patches. Thanks, guys!
+
+For additional details refer to NEWS.txt and Changelog.
+
+Please contact the IDLE maintainer (kbk@shore.net) to have yourself included
+here if you are one of those we missed!
+
+
+
diff --git a/lib/python2.7/idlelib/CallTipWindow.py b/lib/python2.7/idlelib/CallTipWindow.py
new file mode 100644
index 0000000..e63164b
--- /dev/null
+++ b/lib/python2.7/idlelib/CallTipWindow.py
@@ -0,0 +1,161 @@
+"""A CallTip window class for Tkinter/IDLE.
+
+After ToolTip.py, which uses ideas gleaned from PySol
+Used by the CallTips IDLE extension.
+"""
+from Tkinter import Toplevel, Label, LEFT, SOLID, TclError
+
+HIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-hide>>"
+HIDE_SEQUENCES = ("<Key-Escape>", "<FocusOut>")
+CHECKHIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-checkhide>>"
+CHECKHIDE_SEQUENCES = ("<KeyRelease>", "<ButtonRelease>")
+CHECKHIDE_TIME = 100 # milliseconds
+
+MARK_RIGHT = "calltipwindowregion_right"
+
+class CallTip:
+
+ def __init__(self, widget):
+ self.widget = widget
+ self.tipwindow = self.label = None
+ self.parenline = self.parencol = None
+ self.lastline = None
+ self.hideid = self.checkhideid = None
+ self.checkhide_after_id = None
+
+ def position_window(self):
+ """Check if needs to reposition the window, and if so - do it."""
+ curline = int(self.widget.index("insert").split('.')[0])
+ if curline == self.lastline:
+ return
+ self.lastline = curline
+ self.widget.see("insert")
+ if curline == self.parenline:
+ box = self.widget.bbox("%d.%d" % (self.parenline,
+ self.parencol))
+ else:
+ box = self.widget.bbox("%d.0" % curline)
+ if not box:
+ box = list(self.widget.bbox("insert"))
+ # align to left of window
+ box[0] = 0
+ box[2] = 0
+ x = box[0] + self.widget.winfo_rootx() + 2
+ y = box[1] + box[3] + self.widget.winfo_rooty()
+ self.tipwindow.wm_geometry("+%d+%d" % (x, y))
+
+ def showtip(self, text, parenleft, parenright):
+ """Show the calltip, bind events which will close it and reposition it.
+ """
+ # Only called in CallTips, where lines are truncated
+ self.text = text
+ if self.tipwindow or not self.text:
+ return
+
+ self.widget.mark_set(MARK_RIGHT, parenright)
+ self.parenline, self.parencol = map(
+ int, self.widget.index(parenleft).split("."))
+
+ self.tipwindow = tw = Toplevel(self.widget)
+ self.position_window()
+ # remove border on calltip window
+ tw.wm_overrideredirect(1)
+ try:
+ # This command is only needed and available on Tk >= 8.4.0 for OSX
+ # Without it, call tips intrude on the typing process by grabbing
+ # the focus.
+ tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w,
+ "help", "noActivates")
+ except TclError:
+ pass
+ self.label = Label(tw, text=self.text, justify=LEFT,
+ background="#ffffe0", relief=SOLID, borderwidth=1,
+ font = self.widget['font'])
+ self.label.pack()
+ tw.lift() # work around bug in Tk 8.5.18+ (issue #24570)
+
+ self.checkhideid = self.widget.bind(CHECKHIDE_VIRTUAL_EVENT_NAME,
+ self.checkhide_event)
+ for seq in CHECKHIDE_SEQUENCES:
+ self.widget.event_add(CHECKHIDE_VIRTUAL_EVENT_NAME, seq)
+ self.widget.after(CHECKHIDE_TIME, self.checkhide_event)
+ self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME,
+ self.hide_event)
+ for seq in HIDE_SEQUENCES:
+ self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq)
+
+ def checkhide_event(self, event=None):
+ if not self.tipwindow:
+ # If the event was triggered by the same event that unbinded
+ # this function, the function will be called nevertheless,
+ # so do nothing in this case.
+ return
+ curline, curcol = map(int, self.widget.index("insert").split('.'))
+ if curline < self.parenline or \
+ (curline == self.parenline and curcol <= self.parencol) or \
+ self.widget.compare("insert", ">", MARK_RIGHT):
+ self.hidetip()
+ else:
+ self.position_window()
+ if self.checkhide_after_id is not None:
+ self.widget.after_cancel(self.checkhide_after_id)
+ self.checkhide_after_id = \
+ self.widget.after(CHECKHIDE_TIME, self.checkhide_event)
+
+ def hide_event(self, event):
+ if not self.tipwindow:
+ # See the explanation in checkhide_event.
+ return
+ self.hidetip()
+
+ def hidetip(self):
+ if not self.tipwindow:
+ return
+
+ for seq in CHECKHIDE_SEQUENCES:
+ self.widget.event_delete(CHECKHIDE_VIRTUAL_EVENT_NAME, seq)
+ self.widget.unbind(CHECKHIDE_VIRTUAL_EVENT_NAME, self.checkhideid)
+ self.checkhideid = None
+ for seq in HIDE_SEQUENCES:
+ self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq)
+ self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid)
+ self.hideid = None
+
+ self.label.destroy()
+ self.label = None
+ self.tipwindow.destroy()
+ self.tipwindow = None
+
+ self.widget.mark_unset(MARK_RIGHT)
+ self.parenline = self.parencol = self.lastline = None
+
+ def is_active(self):
+ return bool(self.tipwindow)
+
+
+def _calltip_window(parent): # htest #
+ from Tkinter import Toplevel, Text, LEFT, BOTH
+
+ top = Toplevel(parent)
+ top.title("Test calltips")
+ top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200,
+ parent.winfo_rooty() + 150))
+ text = Text(top)
+ text.pack(side=LEFT, fill=BOTH, expand=1)
+ text.insert("insert", "string.split")
+ top.update()
+ calltip = CallTip(text)
+
+ def calltip_show(event):
+ calltip.showtip("(s=Hello world)", "insert", "end")
+ def calltip_hide(event):
+ calltip.hidetip()
+ text.event_add("<<calltip-show>>", "(")
+ text.event_add("<<calltip-hide>>", ")")
+ text.bind("<<calltip-show>>", calltip_show)
+ text.bind("<<calltip-hide>>", calltip_hide)
+ text.focus_set()
+
+if __name__=='__main__':
+ from idlelib.idle_test.htest import run
+ run(_calltip_window)
diff --git a/lib/python2.7/idlelib/CallTips.py b/lib/python2.7/idlelib/CallTips.py
new file mode 100644
index 0000000..3db2636
--- /dev/null
+++ b/lib/python2.7/idlelib/CallTips.py
@@ -0,0 +1,219 @@
+"""CallTips.py - An IDLE Extension to Jog Your Memory
+
+Call Tips are floating windows which display function, class, and method
+parameter and docstring information when you type an opening parenthesis, and
+which disappear when you type a closing parenthesis.
+
+"""
+import __main__
+import re
+import sys
+import textwrap
+import types
+
+from idlelib import CallTipWindow
+from idlelib.HyperParser import HyperParser
+
+
+class CallTips:
+
+ menudefs = [
+ ('edit', [
+ ("Show call tip", "<<force-open-calltip>>"),
+ ])
+ ]
+
+ 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
+
+ def close(self):
+ self._make_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
+
+ def force_open_calltip_event(self, event):
+ """Happens when the user really wants to open a CallTip, even if a
+ function call is needed.
+ """
+ self.open_calltip(True)
+
+ def try_open_calltip_event(self, event):
+ """Happens when it would be nice to open a CallTip, but not really
+ necessary, for example after an opening bracket, so function calls
+ won't be made.
+ """
+ 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():
+ self.open_calltip(False)
+
+ def open_calltip(self, evalfuncs):
+ self._remove_calltip_window()
+
+ hp = HyperParser(self.editwin, "insert")
+ sur_paren = hp.get_surrounding_brackets('(')
+ if not sur_paren:
+ return
+ hp.set_index(sur_paren[0])
+ expression = hp.get_expression()
+ if not expression or (not evalfuncs and expression.find('(') != -1):
+ return
+ arg_text = self.fetch_tip(expression)
+ if not arg_text:
+ return
+ self.calltip = self._make_calltip_window()
+ self.calltip.showtip(arg_text, sur_paren[0], sur_paren[1])
+
+ def fetch_tip(self, expression):
+ """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.
+
+ The subprocess environment is that of the most recently run script. If
+ two unrelated modules are being edited some calltips in the current
+ module may be inoperative if the module was not the last to run.
+
+ To find methods, fetch_tip must be fed a fully qualified name.
+
+ """
+ try:
+ rpcclt = self.editwin.flist.pyshell.interp.rpcclt
+ except AttributeError:
+ rpcclt = None
+ if rpcclt:
+ return rpcclt.remotecall("exec", "get_the_calltip",
+ (expression,), {})
+ else:
+ entity = self.get_entity(expression)
+ return get_arg_text(entity)
+
+ def get_entity(self, expression):
+ """Return the object corresponding to expression evaluated
+ in a namespace spanning sys.modules and __main.dict__.
+ """
+ if expression:
+ namespace = sys.modules.copy()
+ namespace.update(__main__.__dict__)
+ try:
+ return eval(expression, namespace)
+ except BaseException:
+ # An uncaught exception closes idle, and eval can raise any
+ # exception, especially if user classes are involved.
+ 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.
+ try:
+ return class_ob.__init__.im_func
+ except AttributeError:
+ for base in class_ob.__bases__:
+ rc = _find_constructor(base)
+ if rc is not None: return rc
+ return None
+
+# The following are used in get_arg_text
+_MAX_COLS = 85
+_MAX_LINES = 5 # enough for bytes
+_INDENT = ' '*4 # for wrapped signatures
+
+def get_arg_text(ob):
+ '''Return a string describing the signature of a callable object, or ''.
+
+ For Python-coded functions and methods, the first line is introspected.
+ Delete 'self' parameter for classes (.__init__) and bound methods.
+ The next lines are the first lines of the doc string up to the first
+ empty line or _MAX_LINES. For builtins, this typically includes
+ the arguments in addition to the return value.
+ '''
+ argspec = ""
+ try:
+ ob_call = ob.__call__
+ except BaseException:
+ if type(ob) is types.ClassType: # old-style
+ ob_call = ob
+ else:
+ return argspec
+
+ arg_offset = 0
+ if type(ob) in (types.ClassType, types.TypeType):
+ # Look for the first __init__ in the class chain with .im_func.
+ # Slot wrappers (builtins, classes defined in funcs) do not.
+ 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
+ # and drop the "self" param for bound methods
+ fob = ob.im_func
+ if ob.im_self is not None:
+ arg_offset = 1
+ elif type(ob_call) == types.MethodType:
+ # a callable class instance
+ fob = ob_call.im_func
+ arg_offset = 1
+ 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)
+ for flag, pre, name in ((0x4, '*', 'args'), (0x8, '**', 'kwargs')):
+ if fob.func_code.co_flags & flag:
+ pre_name = pre + name
+ if name not in real_args:
+ items.append(pre_name)
+ else:
+ i = 1
+ while ((name+'%s') % i) in real_args:
+ i += 1
+ items.append((pre_name+'%s') % i)
+ argspec = ", ".join(items)
+ argspec = "(%s)" % re.sub("(?<!\d)\.\d+", "<tuple>", argspec)
+
+ lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT)
+ if len(argspec) > _MAX_COLS else [argspec] if argspec else [])
+
+ if isinstance(ob_call, types.MethodType):
+ doc = ob_call.__doc__
+ else:
+ doc = getattr(ob, "__doc__", "")
+ if doc:
+ for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]:
+ line = line.strip()
+ if not line:
+ break
+ if len(line) > _MAX_COLS:
+ line = line[: _MAX_COLS - 3] + '...'
+ lines.append(line)
+ argspec = '\n'.join(lines)
+ return argspec
+
+if __name__ == '__main__':
+ from unittest import main
+ main('idlelib.idle_test.test_calltips', verbosity=2)
diff --git a/lib/python2.7/idlelib/ChangeLog b/lib/python2.7/idlelib/ChangeLog
new file mode 100644
index 0000000..968a344
--- /dev/null
+++ b/lib/python2.7/idlelib/ChangeLog
@@ -0,0 +1,1591 @@
+Please refer to the IDLEfork and IDLE CVS repositories for
+change details subsequent to the 0.8.1 release.
+
+
+IDLEfork ChangeLog
+==================
+
+2001-07-20 11:35 elguavas
+
+ * README.txt, NEWS.txt: bring up to date for 0.8.1 release
+
+2001-07-19 16:40 elguavas
+
+ * IDLEFORK.html: replaced by IDLEFORK-index.html
+
+2001-07-19 16:39 elguavas
+
+ * IDLEFORK-index.html: updated placeholder idlefork homepage
+
+2001-07-19 14:49 elguavas
+
+ * ChangeLog, EditorWindow.py, INSTALLATION, NEWS.txt, README.txt,
+ TODO.txt, idlever.py:
+ minor tidy-ups ready for 0.8.1 alpha tarball release
+
+2001-07-17 15:12 kbk
+
+ * INSTALLATION, setup.py: INSTALLATION: Remove the coexist.patch
+ instructions
+
+ **************** setup.py:
+
+ Remove the idles script, add some words on IDLE Fork to the
+ long_description, and clean up some line spacing.
+
+2001-07-17 15:01 kbk
+
+ * coexist.patch: Put this in the attic, at least for now...
+
+2001-07-17 14:59 kbk
+
+ * PyShell.py, idle, idles: Implement idle command interface as
+ suggested by GvR [idle-dev] 16 July **************** PyShell: Added
+ functionality:
+
+ usage: idle.py [-c command] [-d] [-i] [-r script] [-s] [-t title]
+ [arg] ...
+
+ idle file(s) (without options) edit the file(s)
+
+ -c cmd run the command in a shell -d enable the
+ debugger -i open an interactive shell -i file(s) open a
+ shell and also an editor window for each file -r script run a file
+ as a script in a shell -s run $IDLESTARTUP or
+ $PYTHONSTARTUP before anything else -t title set title of shell
+ window
+
+ Remaining arguments are applied to the command (-c) or script (-r).
+
+ ****************** idles: Removed the idles script, not needed
+
+ ****************** idle: Removed the IdleConf references, not
+ required anymore
+
+2001-07-16 17:08 kbk
+
+ * INSTALLATION, coexist.patch: Added installation instructions.
+
+ Added a patch which modifies idlefork so that it can co-exist with
+ "official" IDLE in the site-packages directory. This patch is not
+ necessary if only idlefork IDLE is installed. See INSTALLATION for
+ further details.
+
+2001-07-16 15:50 kbk
+
+ * idles: Add a script "idles" which opens a Python Shell window.
+
+ The default behaviour of idlefork idle is to open an editor window
+ instead of a shell. Complex expressions may be run in a fresh
+ environment by selecting "run". There are times, however, when a
+ shell is desired. Though one can be started by "idle -t 'foo'",
+ this script is more convenient. In addition, a shell and an editor
+ window can be started in parallel by "idles -e foo.py".
+
+2001-07-16 15:25 kbk
+
+ * PyShell.py: Call out IDLE Fork in startup message.
+
+2001-07-16 14:00 kbk
+
+ * PyShell.py, setup.py: Add a script "idles" which opens a Python
+ Shell window.
+
+ The default behaviour of idlefork idle is to open an editor window
+ instead of a shell. Complex expressions may be run in a fresh
+ environment by selecting "run". There are times, however, when a
+ shell is desired. Though one can be started by "idle -t 'foo'",
+ this script is more convenient. In addition, a shell and an editor
+ window can be started in parallel by "idles -e foo.py".
+
+2001-07-15 03:06 kbk
+
+ * pyclbr.py, tabnanny.py: tabnanny and pyclbr are now found in /Lib
+
+2001-07-15 02:29 kbk
+
+ * BrowserControl.py: Remove, was retained for 1.5.2 support
+
+2001-07-14 15:48 kbk
+
+ * setup.py: Installing Idle to site-packages via Distutils does not
+ copy the Idle help.txt file.
+
+ Ref SF Python Patch 422471
+
+2001-07-14 15:26 kbk
+
+ * keydefs.py: py-cvs-2001_07_13 (Rev 1.3) merge
+
+ "Make copy, cut and paste events case insensitive. Reported by
+ Patrick K. O'Brien on idle-dev. (Should other bindings follow
+ suit?)" --GvR
+
+2001-07-14 15:21 kbk
+
+ * idle.py: py-cvs-2001_07_13 (Rev 1.4) merge
+
+ "Move the action of loading the configuration to the IdleConf
+ module rather than the idle.py script. This has advantages and
+ disadvantages; the biggest advantage being that we can more easily
+ have an alternative main program." --GvR
+
+2001-07-14 15:18 kbk
+
+ * extend.txt: py-cvs-2001_07_13 (Rev 1.4) merge
+
+ "Quick update to the extension mechanism (extend.py is gone, long
+ live config.txt)" --GvR
+
+2001-07-14 15:15 kbk
+
+ * StackViewer.py: py-cvs-2001_07_13 (Rev 1.16) merge
+
+ "Refactored, with some future plans in mind. This now uses the new
+ gotofileline() method defined in FileList.py" --GvR
+
+2001-07-14 15:10 kbk
+
+ * PyShell.py: py-cvs-2001_07_13 (Rev 1.34) merge
+
+ "Amazing. A very subtle change in policy in descr-branch actually
+ found a bug here. Here's the deal: Class PyShell derives from
+ class OutputWindow. Method PyShell.close() wants to invoke its
+ parent method, but because PyShell long ago was inherited from
+ class PyShellEditorWindow, it invokes
+ PyShelEditorWindow.close(self). Now, class PyShellEditorWindow
+ itself derives from class OutputWindow, and inherits the close()
+ method from there without overriding it. Under the old rules,
+ PyShellEditorWindow.close would return an unbound method restricted
+ to the class that defined the implementation of close(), which was
+ OutputWindow.close. Under the new rules, the unbound method is
+ restricted to the class whose method was requested, that is
+ PyShellEditorWindow, and this was correctly trapped as an error."
+ --GvR
+
+2001-07-14 14:59 kbk
+
+ * PyParse.py: py-cvs-2001_07_13 (Rel 1.9) merge
+
+ "Taught IDLE's autoident parser that "yield" is a keyword that
+ begins a stmt. Along w/ the preceding change to keyword.py, making
+ all this work w/ a future-stmt just looks harder and harder."
+ --tim_one
+
+ (From Rel 1.8: "Hack to make this still work with Python 1.5.2.
+ ;-( " --fdrake)
+
+2001-07-14 14:51 kbk
+
+ * IdleConf.py: py-cvs-2001_07_13 (Rel 1.7) merge
+
+ "Move the action of loading the configuration to the IdleConf
+ module rather than the idle.py script. This has advantages and
+ disadvantages; the biggest advantage being that we can more easily
+ have an alternative main program." --GvR
+
+2001-07-14 14:45 kbk
+
+ * FileList.py: py-cvs-2000_07_13 (Rev 1.9) merge
+
+ "Delete goodname() method, which is unused. Add gotofileline(), a
+ convenience method which I intend to use in a variant. Rename
+ test() to _test()." --GvR
+
+ This was an interesting merge. The join completely missed removing
+ goodname(), which was adjacent, but outside of, a small conflict.
+ I only caught it by comparing the 1.1.3.2/1.1.3.3 diff. CVS ain't
+ infallible.
+
+2001-07-14 13:58 kbk
+
+ * EditorWindow.py: py-cvs-2000_07_13 (Rev 1.38) merge "Remove
+ legacy support for the BrowserControl module; the webbrowser module
+ has been included since Python 2.0, and that is the preferred
+ interface." --fdrake
+
+2001-07-14 13:32 kbk
+
+ * EditorWindow.py, FileList.py, IdleConf.py, PyParse.py,
+ PyShell.py, StackViewer.py, extend.txt, idle.py, keydefs.py: Import
+ the 2001 July 13 23:59 GMT version of Python CVS IDLE on the
+ existing 1.1.3 vendor branch named py-cvs-vendor-branch. Release
+ tag is py-cvs-2001_07_13.
+
+2001-07-14 12:02 kbk
+
+ * Icons/python.gif: py-cvs-rel2_1 (Rev 1.2) merge Copied py-cvs rev
+ 1.2 changed file to idlefork MAIN
+
+2001-07-14 11:58 kbk
+
+ * Icons/minusnode.gif: py-cvs-rel2_1 (Rev 1.2) merge Copied py-cvs
+ 1.2 changed file to idlefork MAIN
+
+2001-07-14 11:23 kbk
+
+ * ScrolledList.py: py-cvs-rel2_1 (rev 1.5) merge - whitespace
+ normalization
+
+2001-07-14 11:20 kbk
+
+ * Separator.py: py-cvs-rel2_1 (Rev 1.3) merge - whitespace
+ normalization
+
+2001-07-14 11:16 kbk
+
+ * StackViewer.py: py-cvs-rel2_1 (Rev 1.15) merge - whitespace
+ normalization
+
+2001-07-14 11:14 kbk
+
+ * ToolTip.py: py-cvs-rel2_1 (Rev 1.2) merge - whitespace
+ normalization
+
+2001-07-14 10:13 kbk
+
+ * PyShell.py: cvs-py-rel2_1 (Rev 1.29 - 1.33) merge
+
+ Merged the following py-cvs revs without conflict: 1.29 Reduce
+ copyright text output at startup 1.30 Delay setting sys.args until
+ Tkinter is fully initialized 1.31 Whitespace normalization 1.32
+ Turn syntax warning into error when interactive 1.33 Fix warning
+ initialization bug
+
+ Note that module is extensively modified wrt py-cvs
+
+2001-07-14 06:33 kbk
+
+ * PyParse.py: py-cvs-rel2_1 (Rev 1.6 - 1.8) merge Fix autoindent
+ bug and deflect Unicode from text.get()
+
+2001-07-14 06:00 kbk
+
+ * Percolator.py: py-cvs-rel2_1 (Rev 1.3) "move "from Tkinter import
+ *" to module level" --jhylton
+
+2001-07-14 05:57 kbk
+
+ * PathBrowser.py: py-cvs-rel2_1 (Rev 1.6) merge - whitespace
+ normalization
+
+2001-07-14 05:49 kbk
+
+ * ParenMatch.py: cvs-py-rel2_1 (Rev 1.5) merge - whitespace
+ normalization
+
+2001-07-14 03:57 kbk
+
+ * ObjectBrowser.py: py-cvs-rel2_1 (Rev 1.3) merge "Make the test
+ program work outside IDLE." -- GvR
+
+2001-07-14 03:52 kbk
+
+ * MultiStatusBar.py: py-cvs-rel2_1 (Rev 1.2) merge - whitespace
+ normalization
+
+2001-07-14 03:44 kbk
+
+ * MultiScrolledLists.py: py-cvs-rel2_1 (Rev 1.2) merge - whitespace
+ normalization
+
+2001-07-14 03:40 kbk
+
+ * IdleHistory.py: py-cvs-rel2_1 (Rev 1.4) merge - whitespace
+ normalization
+
+2001-07-14 03:38 kbk
+
+ * IdleConf.py: py-cvs-rel2_1 (Rev 1.6) merge - whitespace
+ normalization
+
+2001-07-13 14:18 kbk
+
+ * IOBinding.py: py-cvs-rel2_1 (Rev 1.4) merge - move "import *" to
+ module level
+
+2001-07-13 14:12 kbk
+
+ * FormatParagraph.py: py-cvs-rel2_1 (Rev 1.9) merge - whitespace
+ normalization
+
+2001-07-13 14:07 kbk
+
+ * FileList.py: py-cvs-rel2_1 (Rev 1.8) merge - whitespace
+ normalization
+
+2001-07-13 13:35 kbk
+
+ * EditorWindow.py: py-cvs-rel2_1 (Rev 1.33 - 1.37) merge
+
+ VP IDLE version depended on VP's ExecBinding.py and spawn.py to get
+ the path to the Windows Doc directory (relative to python.exe).
+ Removed this conflicting code in favor of py-cvs updates which on
+ Windows use a hard coded path relative to the location of this
+ module. py-cvs updates include support for webbrowser.py. Module
+ still has BrowserControl.py for 1.5.2 support.
+
+ At this point, the differences wrt py-cvs relate to menu
+ functionality.
+
+2001-07-13 11:30 kbk
+
+ * ConfigParser.py: py-cvs-rel2_1 merge - Remove, lives in /Lib
+
+2001-07-13 10:10 kbk
+
+ * Delegator.py: py-cvs-rel2_1 (Rev 1.3) merge - whitespace
+ normalization
+
+2001-07-13 10:07 kbk
+
+ * Debugger.py: py-cvs-rel2_1 (Rev 1.15) merge - whitespace
+ normalization
+
+2001-07-13 10:04 kbk
+
+ * ColorDelegator.py: py-cvs-rel2_1 (Rev 1.11 and 1.12) merge
+ Colorize "as" after "import" / use DEBUG instead of __debug__
+
+2001-07-13 09:54 kbk
+
+ * ClassBrowser.py: py-cvs-rel2_1 (Rev 1.12) merge - whitespace
+ normalization
+
+2001-07-13 09:41 kbk
+
+ * BrowserControl.py: py-cvs-rel2_1 (Rev 1.1) merge - New File -
+ Force HEAD to trunk with -f Note: browser.py was renamed
+ BrowserControl.py 10 May 2000. It provides a collection of classes
+ and convenience functions to control external browsers "for 1.5.2
+ support". It was removed from py-cvs 18 April 2001.
+
+2001-07-13 09:10 kbk
+
+ * CallTips.py: py-cvs-rel2_1 (Rev 1.8) merge - whitespace
+ normalization
+
+2001-07-13 08:26 kbk
+
+ * CallTipWindow.py: py-cvs-rel2_1 (Rev 1.3) merge - whitespace
+ normalization
+
+2001-07-13 08:13 kbk
+
+ * AutoExpand.py: py-cvs-rel1_2 (Rev 1.4) merge, "Add Alt-slash to
+ Unix keydefs (I somehow need it on RH 6.2). Get rid of assignment
+ to unused self.text.wordlist." --GvR
+
+2001-07-12 16:54 elguavas
+
+ * ReplaceDialog.py: py-cvs merge, python 1.5.2 compatibility
+
+2001-07-12 16:46 elguavas
+
+ * ScriptBinding.py: py-cvs merge, better error dialog
+
+2001-07-12 16:38 elguavas
+
+ * TODO.txt: py-cvs merge, additions
+
+2001-07-12 15:35 elguavas
+
+ * WindowList.py: py-cvs merge, correct indentation
+
+2001-07-12 15:24 elguavas
+
+ * config.txt: py-cvs merge, correct typo
+
+2001-07-12 15:21 elguavas
+
+ * help.txt: py-cvs merge, update colour changing info
+
+2001-07-12 14:51 elguavas
+
+ * idle.py: py-cvs merge, idle_dir loading changed
+
+2001-07-12 14:44 elguavas
+
+ * idlever.py: py-cvs merge, version update
+
+2001-07-11 12:53 kbk
+
+ * BrowserControl.py: Initial revision
+
+2001-07-11 12:53 kbk
+
+ * AutoExpand.py, BrowserControl.py, CallTipWindow.py, CallTips.py,
+ ClassBrowser.py, ColorDelegator.py, Debugger.py, Delegator.py,
+ EditorWindow.py, FileList.py, FormatParagraph.py, IOBinding.py,
+ IdleConf.py, IdleHistory.py, MultiScrolledLists.py,
+ MultiStatusBar.py, ObjectBrowser.py, OutputWindow.py,
+ ParenMatch.py, PathBrowser.py, Percolator.py, PyParse.py,
+ PyShell.py, RemoteInterp.py, ReplaceDialog.py, ScriptBinding.py,
+ ScrolledList.py, Separator.py, StackViewer.py, TODO.txt,
+ ToolTip.py, WindowList.py, config.txt, help.txt, idle, idle.bat,
+ idle.py, idlever.py, setup.py, Icons/minusnode.gif,
+ Icons/python.gif: Import the release 2.1 version of Python CVS IDLE
+ on the existing 1.1.3 vendor branch named py-cvs-vendor-branch,
+ with release tag py-cvs-rel2_1.
+
+2001-07-11 12:34 kbk
+
+ * AutoExpand.py, AutoIndent.py, Bindings.py, CallTipWindow.py,
+ CallTips.py, ChangeLog, ClassBrowser.py, ColorDelegator.py,
+ Debugger.py, Delegator.py, EditorWindow.py, FileList.py,
+ FormatParagraph.py, FrameViewer.py, GrepDialog.py, IOBinding.py,
+ IdleConf.py, IdleHistory.py, MultiScrolledLists.py,
+ MultiStatusBar.py, NEWS.txt, ObjectBrowser.py, OldStackViewer.py,
+ OutputWindow.py, ParenMatch.py, PathBrowser.py, Percolator.py,
+ PyParse.py, PyShell.py, README.txt, RemoteInterp.py,
+ ReplaceDialog.py, ScriptBinding.py, ScrolledList.py,
+ SearchBinding.py, SearchDialog.py, SearchDialogBase.py,
+ SearchEngine.py, Separator.py, StackViewer.py, TODO.txt,
+ ToolTip.py, TreeWidget.py, UndoDelegator.py, WidgetRedirector.py,
+ WindowList.py, ZoomHeight.py, __init__.py, config-unix.txt,
+ config-win.txt, config.txt, eventparse.py, extend.txt, help.txt,
+ idle.bat, idle.py, idle.pyw, idlever.py, keydefs.py, pyclbr.py,
+ tabnanny.py, testcode.py, Icons/folder.gif, Icons/minusnode.gif,
+ Icons/openfolder.gif, Icons/plusnode.gif, Icons/python.gif,
+ Icons/tk.gif: Import the 9 March 2000 version of Python CVS IDLE as
+ 1.1.3 vendor branch named py-cvs-vendor-branch.
+
+2001-07-04 13:43 kbk
+
+ * Icons/: folder.gif, minusnode.gif, openfolder.gif, plusnode.gif,
+ python.gif, tk.gif: Null commit with -f option to force an uprev
+ and put HEADs firmly on the trunk.
+
+2001-07-04 13:15 kbk
+
+ * AutoExpand.py, AutoIndent.py, Bindings.py, CallTipWindow.py,
+ CallTips.py, ChangeLog, ClassBrowser.py, ColorDelegator.py,
+ ConfigParser.py, Debugger.py, Delegator.py, EditorWindow.py,
+ ExecBinding.py, FileList.py, FormatParagraph.py, FrameViewer.py,
+ GrepDialog.py, IDLEFORK.html, IOBinding.py, IdleConf.py,
+ IdleHistory.py, MultiScrolledLists.py, MultiStatusBar.py, NEWS.txt,
+ ObjectBrowser.py, OldStackViewer.py, OutputWindow.py,
+ ParenMatch.py, PathBrowser.py, Percolator.py, PyParse.py,
+ PyShell.py, README.txt, Remote.py, RemoteInterp.py,
+ ReplaceDialog.py, ScriptBinding.py, ScrolledList.py,
+ SearchBinding.py, SearchDialog.py, SearchDialogBase.py,
+ SearchEngine.py, Separator.py, StackViewer.py, TODO.txt,
+ ToolTip.py, TreeWidget.py, UndoDelegator.py, WidgetRedirector.py,
+ WindowList.py, ZoomHeight.py, __init__.py, config-unix.txt,
+ config-win.txt, config.txt, eventparse.py, extend.txt, help.txt,
+ idle, idle.bat, idle.py, idle.pyw, idlever.py, keydefs.py,
+ loader.py, protocol.py, pyclbr.py, setup.py, spawn.py, tabnanny.py,
+ testcode.py: Null commit with -f option to force an uprev and put
+ HEADs firmly on the trunk.
+
+2001-06-27 10:24 elguavas
+
+ * IDLEFORK.html: updated contact details
+
+2001-06-25 17:23 elguavas
+
+ * idle, RemoteInterp.py, setup.py: Initial revision
+
+2001-06-25 17:23 elguavas
+
+ * idle, RemoteInterp.py, setup.py: import current python cvs idle
+ as a vendor branch
+
+2001-06-24 15:10 elguavas
+
+ * IDLEFORK.html: tiny change to test new syncmail setup
+
+2001-06-24 14:41 elguavas
+
+ * IDLEFORK.html: change to new developer contact, also a test
+ commit for new syncmail setup
+
+2001-06-23 18:15 elguavas
+
+ * IDLEFORK.html: tiny test update for revitalised idle-fork
+
+2000-09-24 17:29 nriley
+
+ * protocol.py: Fixes for Python 1.6 compatibility - socket bind and
+ connect get a tuple instead two arguments.
+
+2000-09-24 17:28 nriley
+
+ * spawn.py: Change for Python 1.6 compatibility - UNIX's 'os'
+ module defines 'spawnv' now, so we check for 'fork' first.
+
+2000-08-15 22:51 nowonder
+
+ * IDLEFORK.html:
+ corrected email address
+
+2000-08-15 22:47 nowonder
+
+ * IDLEFORK.html:
+ added .html file for http://idlefork.sourceforge.net
+
+2000-08-15 11:13 dscherer
+
+ * AutoExpand.py, AutoIndent.py, Bindings.py, CallTipWindow.py,
+ CallTips.py, __init__.py, ChangeLog, ClassBrowser.py,
+ ColorDelegator.py, ConfigParser.py, Debugger.py, Delegator.py,
+ FileList.py, FormatParagraph.py, FrameViewer.py, GrepDialog.py,
+ IOBinding.py, IdleConf.py, IdleHistory.py, MultiScrolledLists.py,
+ MultiStatusBar.py, NEWS.txt, ObjectBrowser.py, OldStackViewer.py,
+ OutputWindow.py, ParenMatch.py, PathBrowser.py, Percolator.py,
+ PyParse.py, PyShell.py, README.txt, ReplaceDialog.py,
+ ScriptBinding.py, ScrolledList.py, SearchBinding.py,
+ SearchDialog.py, SearchDialogBase.py, SearchEngine.py,
+ Separator.py, StackViewer.py, TODO.txt, ToolTip.py, TreeWidget.py,
+ UndoDelegator.py, WidgetRedirector.py, WindowList.py, help.txt,
+ ZoomHeight.py, config-unix.txt, config-win.txt, config.txt,
+ eventparse.py, extend.txt, idle.bat, idle.py, idle.pyw, idlever.py,
+ keydefs.py, loader.py, pyclbr.py, tabnanny.py, testcode.py,
+ EditorWindow.py, ExecBinding.py, Remote.py, protocol.py, spawn.py,
+ Icons/folder.gif, Icons/minusnode.gif, Icons/openfolder.gif,
+ Icons/plusnode.gif, Icons/python.gif, Icons/tk.gif: Initial
+ revision
+
+2000-08-15 11:13 dscherer
+
+ * AutoExpand.py, AutoIndent.py, Bindings.py, CallTipWindow.py,
+ CallTips.py, __init__.py, ChangeLog, ClassBrowser.py,
+ ColorDelegator.py, ConfigParser.py, Debugger.py, Delegator.py,
+ FileList.py, FormatParagraph.py, FrameViewer.py, GrepDialog.py,
+ IOBinding.py, IdleConf.py, IdleHistory.py, MultiScrolledLists.py,
+ MultiStatusBar.py, NEWS.txt, ObjectBrowser.py, OldStackViewer.py,
+ OutputWindow.py, ParenMatch.py, PathBrowser.py, Percolator.py,
+ PyParse.py, PyShell.py, README.txt, ReplaceDialog.py,
+ ScriptBinding.py, ScrolledList.py, SearchBinding.py,
+ SearchDialog.py, SearchDialogBase.py, SearchEngine.py,
+ Separator.py, StackViewer.py, TODO.txt, ToolTip.py, TreeWidget.py,
+ UndoDelegator.py, WidgetRedirector.py, WindowList.py, help.txt,
+ ZoomHeight.py, config-unix.txt, config-win.txt, config.txt,
+ eventparse.py, extend.txt, idle.bat, idle.py, idle.pyw, idlever.py,
+ keydefs.py, loader.py, pyclbr.py, tabnanny.py, testcode.py,
+ EditorWindow.py, ExecBinding.py, Remote.py, protocol.py, spawn.py,
+ Icons/folder.gif, Icons/minusnode.gif, Icons/openfolder.gif,
+ Icons/plusnode.gif, Icons/python.gif, Icons/tk.gif: Modified IDLE
+ from VPython 0.2
+
+
+original IDLE ChangeLog:
+========================
+
+Tue Feb 15 18:08:19 2000 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * NEWS.txt: Notice status bar and stack viewer.
+
+ * EditorWindow.py: Support for Moshe's status bar.
+
+ * MultiStatusBar.py: Status bar code -- by Moshe Zadka.
+
+ * OldStackViewer.py:
+ Adding the old stack viewer implementation back, for the debugger.
+
+ * StackViewer.py: New stack viewer, uses a tree widget.
+ (XXX: the debugger doesn't yet use this.)
+
+ * WindowList.py:
+ Correct a typo and remove an unqualified except that was hiding the error.
+
+ * ClassBrowser.py: Add an XXX comment about the ClassBrowser AIP.
+
+ * ChangeLog: Updated change log.
+
+ * NEWS.txt: News update. Probably incomplete; what else is new?
+
+ * README.txt:
+ Updated for pending IDLE 0.5 release (still very rough -- just getting
+ it out in a more convenient format than CVS).
+
+ * TODO.txt: Tiny addition.
+
+Thu Sep 9 14:16:02 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * TODO.txt: A few new TODO entries.
+
+Thu Aug 26 23:06:22 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * Bindings.py: Add Python Documentation entry to Help menu.
+
+ * EditorWindow.py:
+ Find the help.txt file relative to __file__ or ".", not in sys.path.
+ (Suggested by Moshe Zadka, but implemented differently.)
+
+ Add <<python-docs>> event which, on Unix, brings up Netscape pointing
+ to http://www.python.doc/current/ (a local copy would be nice but its
+ location can't be predicted). Windows solution TBD.
+
+Wed Aug 11 14:55:43 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * TreeWidget.py:
+ Moshe noticed an inconsistency in his comment, so I'm rephrasing it to
+ be clearer.
+
+ * TreeWidget.py:
+ Patch inspired by Moshe Zadka to search for the Icons directory in the
+ same directory as __file__, rather than searching for it along sys.path.
+ This works better when idle is a package.
+
+Thu Jul 15 13:11:02 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * TODO.txt: New wishes.
+
+Sat Jul 10 13:17:35 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * IdlePrefs.py:
+ Make the color for stderr red (i.e. the standard warning/danger/stop
+ color) rather than green. Suggested by Sam Schulenburg.
+
+Fri Jun 25 17:26:34 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * PyShell.py: Close debugger when closing. This may break a cycle.
+
+ * Debugger.py: Break cycle on close.
+
+ * ClassBrowser.py: Destroy the tree when closing.
+
+ * TreeWidget.py: Add destroy() method to recursively destroy a tree.
+
+ * PyShell.py: Extend _close() to break cycles.
+ Break some other cycles too (and destroy the root when done).
+
+ * EditorWindow.py:
+ Add _close() method that does the actual cleanup (close() asks the
+ user what they want first if there's unsaved stuff, and may cancel).
+ It closes more than before.
+
+ Add unload_extensions() method to unload all extensions; called from
+ _close(). It calls an extension's close() method if it has one.
+
+ * Percolator.py: Add close() method that breaks cycles.
+
+ * WidgetRedirector.py: Add unregister() method.
+ Unregister everything at closing.
+ Don't call close() in __del__, rely on explicit call to close().
+
+ * IOBinding.py, FormatParagraph.py, CallTips.py:
+ Add close() method that breaks a cycle.
+
+Fri Jun 11 15:03:00 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * AutoIndent.py, EditorWindow.py, FormatParagraph.py:
+ Tim Peters smart.patch:
+
+ EditorWindow.py:
+
+ + Added get_tabwidth & set_tabwidth "virtual text" methods, that get/set the
+ widget's view of what a tab means.
+
+ + Moved TK_TABWIDTH_DEFAULT here from AutoIndent.
+
+ + Renamed Mark's get_selection_index to get_selection_indices (sorry, Mark,
+ but the name was plain wrong <wink>).
+
+ FormatParagraph.py: renamed use of get_selection_index.
+
+ AutoIndent.py:
+
+ + Moved TK_TABWIDTH_DEFAULT to EditorWindow.
+
+ + Rewrote set_indentation_params to use new VTW get/set_tabwidth methods.
+
+ + Changed smart_backspace_event to delete whitespace back to closest
+ preceding virtual tab stop or real character (note that this may require
+ inserting characters if backspacing over a tab!).
+
+ + Nuked almost references to the selection tag, in favor of using
+ get_selection_indices. The sole exception is in set_region, for which no
+ "set_selection" abstraction has yet been agreed upon.
+
+ + Had too much fun using the spiffy new features of the format-paragraph
+ cmd.
+
+Thu Jun 10 17:48:02 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * FormatParagraph.py:
+ Code by Mark Hammond to format paragraphs embedded in comments.
+ Read the comments (which I reformatted using the new feature :-)
+ for some limitations.
+
+ * EditorWindow.py:
+ Added abstraction get_selection_index() (Mark Hammond). Also
+ reformatted some comment blocks to show off a cool feature I'm about
+ to check in next.
+
+ * ClassBrowser.py:
+ Adapt to the new pyclbr's support of listing top-level functions. If
+ this functionality is not present (e.g. when used with a vintage
+ Python 1.5.2 installation) top-level functions are not listed.
+
+ (Hmm... Any distribution of IDLE 0.5 should probably include a copy
+ of the new pyclbr.py!)
+
+ * AutoIndent.py:
+ Fix off-by-one error in Tim's recent change to comment_region(): the
+ list of lines returned by get_region() contains an empty line at the
+ end representing the start of the next line, and this shouldn't be
+ commented out!
+
+ * CallTips.py:
+ Mark Hammond writes: Here is another change that allows it to work for
+ class creation - tries to locate an __init__ function. Also updated
+ the test code to reflect your new "***" change.
+
+ * CallTipWindow.py:
+ Mark Hammond writes: Tim's suggestion of copying the font for the
+ CallTipWindow from the text control makes sense, and actually makes
+ the control look better IMO.
+
+Wed Jun 9 20:34:57 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * CallTips.py:
+ Append "..." if the appropriate flag (for varargs) in co_flags is set.
+ Ditto "***" for kwargs.
+
+Tue Jun 8 13:06:07 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * ReplaceDialog.py:
+ Hmm... Tim didn't turn "replace all" into a single undo block.
+ I think I like it better if it os, so here.
+
+ * ReplaceDialog.py: Tim Peters: made replacement atomic for undo/redo.
+
+ * AutoIndent.py: Tim Peters:
+
+ + Set usetabs=1. Editing pyclbr.py was driving me nuts <0.6 wink>.
+ usetabs=1 is the Emacs pymode default too, and thanks to indentwidth !=
+ tabwidth magical usetabs disabling, new files are still created with tabs
+ turned off. The only implication is that if you open a file whose first
+ indent is a single tab, IDLE will now magically use tabs for that file (and
+ set indentwidth to 8). Note that the whole scheme doesn't work right for
+ PythonWin, though, since Windows users typically set tabwidth to 4; Mark
+ probably has to hide the IDLE algorithm from them (which he already knows).
+
+ + Changed comment_region_event to stick "##" in front of every line. The
+ "holes" previously left on blank lines were visually confusing (made it
+ needlessly hard to figure out what to uncomment later).
+
+Mon Jun 7 15:38:40 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * TreeWidget.py, ObjectBrowser.py:
+ Remove unnecessary reference to pyclbr from test() code.
+
+ * PyParse.py: Tim Peters:
+
+ Smarter logic for finding a parse synch point.
+
+ Does a half to a fifth the work in normal cases; don't notice the speedup,
+ but makes more breathing room for other extensions.
+
+ Speeds terrible cases by at least a factor of 10. "Terrible" == e.g. you put
+ """ at the start of Tkinter.py, undo it, zoom to the bottom, and start
+ typing in code. Used to take about 8 seconds for ENTER to respond, now some
+ large fraction of a second. The new code gets indented correctly, despite
+ that it all remains "string colored" until the colorizer catches up (after
+ which, ENTER appears instantaneous again).
+
+Fri Jun 4 19:21:19 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * extend.py: Might as well enable CallTips by default.
+ If there are too many complaints I'll remove it again or fix it.
+
+Thu Jun 3 14:32:16 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * AutoIndent.py, EditorWindow.py, PyParse.py:
+ New offerings by Tim Peters; he writes:
+
+ IDLE is now the first Python editor in the Universe not confused by my
+ doctest.py <wink>.
+
+ As threatened, this defines IDLE's is_char_in_string function as a
+ method of EditorWindow. You just need to define one similarly in
+ whatever it is you pass as editwin to AutoIndent; looking at the
+ EditorWindow.py part of the patch should make this clear.
+
+ * GrepDialog.py: Enclose pattern in quotes in status message.
+
+ * CallTips.py:
+ Mark Hammond fixed some comments and improved the way the tip text is
+ constructed.
+
+Wed Jun 2 18:18:57 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * CallTips.py:
+ My fix to Mark's code: restore the universal check on <KeyRelease>.
+ Always cancel on <Key-Escape> or <ButtonPress>.
+
+ * CallTips.py:
+ A version that Mark Hammond posted to the newsgroup. Has some newer
+ stuff for getting the tip. Had to fix the Key-( and Key-) events
+ for Unix. Will have to re-apply my patch for catching KeyRelease and
+ ButtonRelease events.
+
+ * CallTipWindow.py, CallTips.py:
+ Call tips by Mark Hammond (plus tiny fix by me.)
+
+ * IdleHistory.py:
+ Changes by Mark Hammond: (1) support optional output_sep argument to
+ the constructor so he can eliminate the sys.ps2 that PythonWin leaves
+ in the source; (2) remove duplicate history items.
+
+ * AutoIndent.py:
+ Changes by Mark Hammond to allow using IDLE extensions in PythonWin as
+ well: make three dialog routines instance variables.
+
+ * EditorWindow.py:
+ Change by Mark Hammond to allow using IDLE extensions in PythonWin as
+ well: make three dialog routines instance variables.
+
+Tue Jun 1 20:06:44 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * AutoIndent.py: Hah! A fix of my own to Tim's code!
+ Unix bindings for <<toggle-tabs>> and <<change-indentwidth>> were
+ missing, and somehow that meant the events were never generated,
+ even though they were in the menu. The new Unix bindings are now
+ the same as the Windows bindings (M-t and M-u).
+
+ * AutoIndent.py, PyParse.py, PyShell.py: Tim Peters again:
+
+ The new version (attached) is fast enough all the time in every real module
+ I have <whew!>. You can make it slow by, e.g., creating an open list with
+ 5,000 90-character identifiers (+ trailing comma) each on its own line, then
+ adding an item to the end -- but that still consumes less than a second on
+ my P5-166. Response time in real code appears instantaneous.
+
+ Fixed some bugs.
+
+ New feature: when hitting ENTER and the cursor is beyond the line's leading
+ indentation, whitespace is removed on both sides of the cursor; before
+ whitespace was removed only on the left; e.g., assuming the cursor is
+ between the comma and the space:
+
+ def something(arg1, arg2):
+ ^ cursor to the left of here, and hit ENTER
+ arg2): # new line used to end up here
+ arg2): # but now lines up the way you expect
+
+ New hack: AutoIndent has grown a context_use_ps1 Boolean config option,
+ defaulting to 0 (false) and set to 1 (only) by PyShell. Reason: handling
+ the fancy stuff requires looking backward for a parsing synch point; ps1
+ lines are the only sensible thing to look for in a shell window, but are a
+ bad thing to look for in a file window (ps1 lines show up in my module
+ docstrings often). PythonWin's shell should set this true too.
+
+ Persistent problem: strings containing def/class can still screw things up
+ completely. No improvement. Simplest workaround is on the user's head, and
+ consists of inserting e.g.
+
+ def _(): pass
+
+ (or any other def/class) after the end of the multiline string that's
+ screwing them up. This is especially irksome because IDLE's syntax coloring
+ is *not* confused, so when this happens the colors don't match the
+ indentation behavior they see.
+
+ * AutoIndent.py: Tim Peters again:
+
+ [Tim, after adding some bracket smarts to AutoIndent.py]
+ > ...
+ > What it can't possibly do without reparsing large gobs of text is
+ > suggest a reasonable indent level after you've *closed* a bracket
+ > left open on some previous line.
+ > ...
+
+ The attached can, and actually fast enough to use -- most of the time. The
+ code is tricky beyond belief to achieve that, but it works so far; e.g.,
+
+ return len(string.expandtabs(str[self.stmt_start :
+ ^ indents to caret
+ i],
+ ^ indents to caret
+ self.tabwidth)) + 1
+ ^ indents to caret
+
+ It's about as smart as pymode now, wrt both bracket and backslash
+ continuation rules. It does require reparsing large gobs of text, and if it
+ happens to find something that looks like a "def" or "class" or sys.ps1
+ buried in a multiline string, but didn't suck up enough preceding text to
+ see the start of the string, it's completely hosed. I can't repair that --
+ it's just too slow to reparse from the start of the file all the time.
+
+ AutoIndent has grown a new num_context_lines tuple attribute that controls
+ how far to look back, and-- like other params --this could/should be made
+ user-overridable at startup and per-file on the fly.
+
+ * PyParse.py: New file by Tim Peters:
+
+ One new file in the attached, PyParse.py. The LineStudier (whatever it was
+ called <wink>) class was removed from AutoIndent; PyParse subsumes its
+ functionality.
+
+ * AutoIndent.py: Tim Peters keeps revising this module (more to come):
+
+ Removed "New tabwidth" menu binding.
+
+ Added "a tab means how many spaces?" dialog to block tabify and untabify. I
+ think prompting for this is good now: they're usually at-most-once-per-file
+ commands, and IDLE can't let them change tabwidth from the Tk default
+ anymore, so IDLE can no longer presume to have any idea what a tab means.
+
+ Irony: for the purpose of keeping comments aligned via tabs, Tk's
+ non-default approach is much nicer than the Emacs/Notepad/Codewright/vi/etc
+ approach.
+
+ * EditorWindow.py:
+ 1. Catch NameError on import (could be raised by case mismatch on Windows).
+ 2. No longer need to reset pyclbr cache and show watch cursor when calling
+ ClassBrowser -- the ClassBrowser takes care of pyclbr and the TreeWidget
+ takes care of the watch cursor.
+ 3. Reset the focus to the current window after error message about class
+ browser on buffer without filename.
+
+ * Icons/minusnode.gif, Icons/plusnode.gif: Missed a few.
+
+ * ClassBrowser.py, PathBrowser.py: Rewritten based on TreeWidget.py
+
+ * ObjectBrowser.py: Object browser, based on TreeWidget.py.
+
+ * TreeWidget.py: Tree widget done right.
+
+ * ToolTip.py: As yet unused code for tool tips.
+
+ * ScriptBinding.py:
+ Ensure sys.argv[0] is the script name on Run Script.
+
+ * ZoomHeight.py: Move zoom height functionality to separate function.
+
+ * Icons/folder.gif, Icons/openfolder.gif, Icons/python.gif, Icons/tk.gif:
+ A few icons used by ../TreeWidget.py and its callers.
+
+ * AutoIndent.py: New version by Tim Peters improves block opening test.
+
+Fri May 21 04:46:17 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * Attic/History.py, PyShell.py: Rename History to IdleHistory.
+ Add isatty() to pseudo files.
+
+ * StackViewer.py: Make initial stack viewer wider
+
+ * TODO.txt: New wishes
+
+ * AutoIndent.py, EditorWindow.py, PyShell.py:
+ Much improved autoindent and handling of tabs,
+ by Tim Peters.
+
+Mon May 3 15:49:52 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * AutoIndent.py, EditorWindow.py, FormatParagraph.py, UndoDelegator.py:
+ Tim Peters writes:
+
+ I'm still unsure, but couldn't stand the virtual event trickery so tried a
+ different sin (adding undo_block_start/stop methods to the Text instance in
+ EditorWindow.py). Like it or not, it's efficient and works <wink>. Better
+ idea?
+
+ Give the attached a whirl. Even if you hate the implementation, I think
+ you'll like the results. Think I caught all the "block edit" cmds,
+ including Format Paragraph, plus subtler ones involving smart indents and
+ backspacing.
+
+ * WidgetRedirector.py: Tim Peters writes:
+
+ [W]hile trying to dope out how redirection works, stumbled into two
+ possible glitches. In the first, it doesn't appear to make sense to try to
+ rename a command that's already been destroyed; in the second, the name
+ "previous" doesn't really bring to mind "ignore the previous value" <wink>.
+
+Fri Apr 30 19:39:25 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * __init__.py: Support for using idle as a package.
+
+ * PathBrowser.py:
+ Avoid listing files more than once (e.g. foomodule.so has two hits:
+ once for foo + module.so, once for foomodule + .so).
+
+Mon Apr 26 22:20:38 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * ChangeLog, ColorDelegator.py, PyShell.py: Tim Peters strikes again:
+
+ Ho ho ho -- that's trickier than it sounded! The colorizer is working with
+ "line.col" strings instead of Text marks, and the absolute coordinates of
+ the point of interest can change across the self.update call (voice of
+ baffled experience, when two quick backspaces no longer fooled it, but a
+ backspace followed by a quick ENTER did <wink>).
+
+ Anyway, the attached appears to do the trick. CPU usage goes way up when
+ typing quickly into a long triple-quoted string, but the latency is fine for
+ me (a relatively fast typist on a relatively slow machine). Most of the
+ changes here are left over from reducing the # of vrbl names to help me
+ reason about the logic better; I hope the code is a *little* easier to
+
+Fri Apr 23 14:01:25 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * EditorWindow.py:
+ Provide full arguments to __import__ so it works in packagized IDLE.
+
+Thu Apr 22 23:20:17 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * help.txt:
+ Bunch of updates necessary due to recent changes; added docs for File
+ menu, command line and color preferences.
+
+ * Bindings.py: Remove obsolete 'script' menu.
+
+ * TODO.txt: Several wishes fulfilled.
+
+ * OutputWindow.py:
+ Moved classes OnDemandOutputWindow and PseudoFile here,
+ from ScriptBinding.py where they are no longer needed.
+
+ * ScriptBinding.py:
+ Mostly rewritten. Instead of the old Run module and Debug module,
+ there are two new commands:
+
+ Import module (F5) imports or reloads the module and also adds its
+ name to the __main__ namespace. This gets executed in the PyShell
+ window under control of its debug settings.
+
+ Run script (Control-F5) is similar but executes the contents of the
+ file directly in the __main__ namespace.
+
+ * PyShell.py: Nits: document use of $IDLESTARTUP; display idle version
+
+ * idlever.py: New version to celebrate new command line
+
+ * OutputWindow.py: Added flush(), for completeness.
+
+ * PyShell.py:
+ A lot of changes to make the command line more useful. You can now do:
+ idle.py -e file ... -- to edit files
+ idle.py script arg ... -- to run a script
+ idle.py -c cmd arg ... -- to run a command
+ Other options, see also the usage message (also new!) for more details:
+ -d -- enable debugger
+ -s -- run $IDLESTARTUP or $PYTHONSTARTUP
+ -t title -- set Python Shell window's title
+ sys.argv is set accordingly, unless -e is used.
+ sys.path is absolutized, and all relevant paths are inserted into it.
+
+ Other changes:
+ - the environment in which commands are executed is now the
+ __main__ module
+ - explicitly save sys.stdout etc., don't restore from sys.__stdout__
+ - new interpreter methods execsource(), execfile(), stuffsource()
+ - a few small nits
+
+ * TODO.txt:
+ Some more TODO items. Made up my mind about command line args,
+ Run/Import, __main__.
+
+ * ColorDelegator.py:
+ Super-elegant patch by Tim Peters that speeds up colorization
+ dramatically (up to 15 times he claims). Works by reading more than
+ one line at a time, up to 100-line chunks (starting with one line and
+ then doubling up to the limit). On a typical machine (e.g. Tim's
+ P5-166) this doesn't reduce interactive responsiveness in a noticeable
+ way.
+
+Wed Apr 21 15:49:34 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * ColorDelegator.py:
+ Patch by Tim Peters to speed up colorizing of big multiline strings.
+
+Tue Apr 20 17:32:52 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * extend.txt:
+ For an event 'foo-bar', the corresponding method must be called
+ foo_bar_event(). Therefore, fix the references to zoom_height() in
+ the example.
+
+ * IdlePrefs.py: Restored the original IDLE color scheme.
+
+ * PyShell.py, IdlePrefs.py, ColorDelegator.py, EditorWindow.py:
+ Color preferences code by Loren Luke (massaged by me somewhat)
+
+ * SearchEngine.py:
+ Patch by Mark Favas: it fixes the search engine behaviour where an
+ unsuccessful search wraps around and re-searches that part of the file
+ between the start of the search and the end of the file - only really
+ an issue for very large files, but... (also removes a redundant
+ m.span() call).
+
+Mon Apr 19 16:26:02 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * TODO.txt: A few wishes are now fulfilled.
+
+ * AutoIndent.py: Tim Peters implements some of my wishes:
+
+ o Makes the tab key intelligently insert spaces when appropriate
+ (see Help list banter twixt David Ascher and me; idea stolen from
+ every other editor on earth <wink>).
+
+ o newline_and_indent_event trims trailing whitespace on the old
+ line (pymode and Codewright).
+
+ o newline_and_indent_event no longer fooled by trailing whitespace or
+ comment after ":" (pymode, PTUI).
+
+ o newline_and_indent_event now reduces the new line's indentation after
+ return, break, continue, raise and pass stmts (pymode).
+
+ The last two are easy to fool in the presence of strings &
+ continuations, but pymode requires Emacs's high-powered C parsing
+ functions to avoid that in finite time.
+
+======================================================================
+ Python release 1.5.2c1, IDLE version 0.4
+======================================================================
+
+Wed Apr 7 18:41:59 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * README.txt, NEWS.txt: New version.
+
+ * idlever.py: Version bump awaiting impending new release.
+ (Not much has changed :-( )
+
+Mon Mar 29 14:52:28 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * ScriptBinding.py, PyShell.py:
+ At Tim Peters' recommendation, add a dummy flush() method to
+ PseudoFile.
+
+Thu Mar 11 23:21:23 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * PathBrowser.py: Don't crash when sys.path contains an empty string.
+
+ * Attic/Outline.py: This file was never supposed to be part of IDLE.
+
+ * PathBrowser.py:
+ - Don't crash in the case where a superclass is a string instead of a
+ pyclbr.Class object; this can happen when the superclass is
+ unrecognizable (to pyclbr), e.g. when module renaming is used.
+
+ - Show a watch cursor when calling pyclbr (since it may take a while
+ recursively parsing imported modules!).
+
+Wed Mar 10 05:18:02 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * EditorWindow.py, Bindings.py: Add PathBrowser to File module
+
+ * PathBrowser.py: "Path browser" - 4 scrolled lists displaying:
+ directories on sys.path
+ modules in selected directory
+ classes in selected module
+ methods of selected class
+
+ Sinlge clicking in a directory, module or class item updates the next
+ column with info about the selected item. Double clicking in a
+ module, class or method item opens the file (and selects the clicked
+ item if it is a class or method).
+
+ I guess eventually I should be using a tree widget for this, but the
+ ones I've seen don't work well enough, so for now I use the old
+ Smalltalk or NeXT style multi-column hierarchical browser.
+
+ * MultiScrolledLists.py:
+ New utility: multiple scrolled lists in parallel
+
+ * ScrolledList.py: - White background.
+ - Display "(None)" (or text of your choosing) when empty.
+ - Don't set the focus.
+
+======================================================================
+ Python release 1.5.2b2, IDLE version 0.3
+======================================================================
+
+Wed Feb 17 22:47:41 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * NEWS.txt: News in 0.3.
+
+ * README.txt, idlever.py: Bump version to 0.3.
+
+ * EditorWindow.py:
+ After all, we don't need to call the callbacks ourselves!
+
+ * WindowList.py:
+ When deleting, call the callbacks *after* deleting the window from our list!
+
+ * EditorWindow.py:
+ Fix up the Windows menu via the new callback mechanism instead of
+ depending on menu post commands (which don't work when the menu is
+ torn off).
+
+ * WindowList.py:
+ Support callbacks to patch up Windows menus everywhere.
+
+ * ChangeLog: Oh, why not. Checking in the Emacs-generated change log.
+
+Tue Feb 16 22:34:17 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * ScriptBinding.py:
+ Only pop up the stack viewer when requested in the Debug menu.
+
+Mon Feb 8 22:27:49 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * WindowList.py: Don't crash if a window no longer exists.
+
+ * TODO.txt: Restructured a bit.
+
+Mon Feb 1 23:06:17 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * PyShell.py: Add current dir or paths of file args to sys.path.
+
+ * Debugger.py: Add canonic() function -- for brand new bdb.py feature.
+
+ * StackViewer.py: Protect against accessing an empty stack.
+
+Fri Jan 29 20:44:45 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * ZoomHeight.py:
+ Use only the height to decide whether to zoom in or out.
+
+Thu Jan 28 22:24:30 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * EditorWindow.py, FileList.py:
+ Make sure the Tcl variables are shared between windows.
+
+ * PyShell.py, EditorWindow.py, Bindings.py:
+ Move menu/key binding code from Bindings.py to EditorWindow.py,
+ with changed APIs -- it makes much more sense there.
+ Also add a new feature: if the first character of a menu label is
+ a '!', it gets a checkbox. Checkboxes are bound to Boolean Tcl variables
+ that can be accessed through the new getvar/setvar/getrawvar API;
+ the variable is named after the event to which the menu is bound.
+
+ * Debugger.py: Add Quit button to the debugger window.
+
+ * SearchDialog.py:
+ When find_again() finds exactly the current selection, it's a failure.
+
+ * idle.py, Attic/idle: Rename idle -> idle.py
+
+Mon Jan 18 15:18:57 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * EditorWindow.py, WindowList.py: Only deiconify when iconic.
+
+ * TODO.txt: Misc
+
+Tue Jan 12 22:14:34 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * testcode.py, Attic/test.py:
+ Renamed test.py to testcode.py so one can import Python's
+ test package from inside IDLE. (Suggested by Jack Jansen.)
+
+ * EditorWindow.py, ColorDelegator.py:
+ Hack to close a window that is colorizing.
+
+ * Separator.py: Vladimir Marangozov's patch:
+ The separator dances too much and seems to jump by arbitrary amounts
+ in arbitrary directions when I try to move it for resizing the frames.
+ This patch makes it more quiet.
+
+Mon Jan 11 14:52:40 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * TODO.txt: Some requests have been fulfilled.
+
+ * EditorWindow.py:
+ Set the cursor to a watch when opening the class browser (which may
+ take quite a while, browsing multiple files).
+
+ Newer, better center() -- but assumes no wrapping.
+
+ * SearchBinding.py:
+ Got rid of debug print statement in goto_line_event().
+
+ * ScriptBinding.py:
+ I think I like it better if it prints the traceback even when it displays
+ the stack viewer.
+
+ * Debugger.py: Bind ESC to close-window.
+
+ * ClassBrowser.py: Use a HSeparator between the classes and the items.
+ Make the list of classes wider by default (40 chars).
+ Bind ESC to close-window.
+
+ * Separator.py:
+ Separator classes (draggable divider between two panes).
+
+Sat Jan 9 22:01:33 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * WindowList.py:
+ Don't traceback when wakeup() is called when the window has been destroyed.
+ This can happen when a torn-of Windows menu references closed windows.
+ And Tim Peters claims that the Windows menu is his favorite to tear off...
+
+ * EditorWindow.py: Allow tearing off of the Windows menu.
+
+ * StackViewer.py: Close on ESC.
+
+ * help.txt: Updated a bunch of things (it was mostly still 0.1!)
+
+ * extend.py: Added ScriptBinding to standard bindings.
+
+ * ScriptBinding.py:
+ This now actually works. See doc string. It can run a module (i.e.
+ import or reload) or debug it (same with debugger control). Output
+ goes to a fresh output window, only created when needed.
+
+======================================================================
+ Python release 1.5.2b1, IDLE version 0.2
+======================================================================
+
+Fri Jan 8 17:26:02 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * README.txt, NEWS.txt: What's new in this release.
+
+ * Bindings.py, PyShell.py:
+ Paul Prescod's patches to allow the stack viewer to pop up when a
+ traceback is printed.
+
+Thu Jan 7 00:12:15 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * FormatParagraph.py:
+ Change paragraph width limit to 70 (like Emacs M-Q).
+
+ * README.txt:
+ Separating TODO from README. Slight reformulation of features. No
+ exact release date.
+
+ * TODO.txt: Separating TODO from README.
+
+Mon Jan 4 21:19:09 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * FormatParagraph.py:
+ Hm. There was a boundary condition error at the end of the file too.
+
+ * SearchBinding.py: Hm. Add Unix binding for replace, too.
+
+ * keydefs.py: Ran eventparse.py again.
+
+ * FormatParagraph.py: Added Unix Meta-q key binding;
+ fix find_paragraph when at start of file.
+
+ * AutoExpand.py: Added Meta-/ binding for Unix as alt for Alt-/.
+
+ * SearchBinding.py:
+ Add unix binding for grep (otherwise the menu entry doesn't work!)
+
+ * ZoomHeight.py: Adjusted Unix height to work with fvwm96. :=(
+
+ * GrepDialog.py: Need to import sys!
+
+ * help.txt, extend.txt, README.txt: Formatted some paragraphs
+
+ * extend.py, FormatParagraph.py:
+ Add new extension to reformat a (text) paragraph.
+
+ * ZoomHeight.py: Typo in Win specific height setting.
+
+Sun Jan 3 00:47:35 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * AutoIndent.py: Added something like Tim Peters' backspace patch.
+
+ * ZoomHeight.py: Adapted to Unix (i.e., more hardcoded constants).
+
+Sat Jan 2 21:28:54 1999 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * keydefs.py, idlever.py, idle.pyw, idle.bat, help.txt, extend.txt, extend.py, eventparse.py, ZoomHeight.py, WindowList.py, UndoDelegator.py, StackViewer.py, SearchEngine.py, SearchDialogBase.py, SearchDialog.py, ScrolledList.py, SearchBinding.py, ScriptBinding.py, ReplaceDialog.py, Attic/README, README.txt, PyShell.py, Attic/PopupMenu.py, OutputWindow.py, IOBinding.py, Attic/HelpWindow.py, History.py, GrepDialog.py, FileList.py, FrameViewer.py, EditorWindow.py, Debugger.py, Delegator.py, ColorDelegator.py, Bindings.py, ClassBrowser.py, AutoExpand.py, AutoIndent.py:
+ Checking in IDLE 0.2.
+
+ Much has changed -- too much, in fact, to write down.
+ The big news is that there's a standard way to write IDLE extensions;
+ see extend.txt. Some sample extensions have been provided, and
+ some existing code has been converted to extensions. Probably the
+ biggest new user feature is a new search dialog with more options,
+ search and replace, and even search in files (grep).
+
+ This is exactly as downloaded from my laptop after returning
+ from the holidays -- it hasn't even been tested on Unix yet.
+
+Fri Dec 18 15:52:54 1998 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * FileList.py, ClassBrowser.py:
+ Fix the class browser to work even when the file is not on sys.path.
+
+Tue Dec 8 20:39:36 1998 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * Attic/turtle.py: Moved to Python 1.5.2/Lib
+
+Fri Nov 27 03:19:20 1998 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * help.txt: Typo
+
+ * EditorWindow.py, FileList.py: Support underlining of menu labels
+
+ * Bindings.py:
+ New approach, separate tables for menus (platform-independent) and key
+ definitions (platform-specific), and generating accelerator strings
+ automatically from the key definitions.
+
+Mon Nov 16 18:37:42 1998 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * Attic/README: Clarify portability and main program.
+
+ * Attic/README: Added intro for 0.1 release and append Grail notes.
+
+Mon Oct 26 18:49:00 1998 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * Attic/turtle.py: root is now a global called _root
+
+Sat Oct 24 16:38:38 1998 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * Attic/turtle.py: Raise the root window on reset().
+ Different action on WM_DELETE_WINDOW is more likely to do the right thing,
+ allowing us to destroy old windows.
+
+ * Attic/turtle.py:
+ Split the goto() function in two: _goto() is the internal one,
+ using Canvas coordinates, and goto() uses turtle coordinates
+ and accepts variable argument lists.
+
+ * Attic/turtle.py: Cope with destruction of the window
+
+ * Attic/turtle.py: Turtle graphics
+
+ * Debugger.py: Use of Breakpoint class should be bdb.Breakpoint.
+
+Mon Oct 19 03:33:40 1998 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * SearchBinding.py:
+ Speed up the search a bit -- don't drag a mark around...
+
+ * PyShell.py:
+ Change our special entries from <console#N> to <pyshell#N>.
+ Patch linecache.checkcache() to keep our special entries alive.
+ Add popup menu to all editor windows to set a breakpoint.
+
+ * Debugger.py:
+ Use and pass through the 'force' flag to set_dict() where appropriate.
+ Default source and globals checkboxes to false.
+ Don't interact in user_return().
+ Add primitive set_breakpoint() method.
+
+ * ColorDelegator.py:
+ Raise priority of 'sel' tag so its foreground (on Windows) will take
+ priority over text colorization (which on Windows is almost the
+ same color as the selection background).
+
+ Define a tag and color for breakpoints ("BREAK").
+
+ * Attic/PopupMenu.py: Disable "Open stack viewer" and "help" commands.
+
+ * StackViewer.py:
+ Add optional 'force' argument (default 0) to load_dict().
+ If set, redo the display even if it's the same dict.
+
+Fri Oct 16 21:10:12 1998 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * StackViewer.py: Do nothing when loading the same dict as before.
+
+ * PyShell.py: Details for debugger interface.
+
+ * Debugger.py:
+ Restructured and more consistent. Save checkboxes across instantiations.
+
+ * EditorWindow.py, Attic/README, Bindings.py:
+ Get rid of conflicting ^X binding. Use ^W.
+
+ * Debugger.py, StackViewer.py:
+ Debugger can now show local and global variables.
+
+ * Debugger.py: Oops
+
+ * Debugger.py, PyShell.py: Better debugger support (show stack etc).
+
+ * Attic/PopupMenu.py: Follow renames in StackViewer module
+
+ * StackViewer.py:
+ Rename classes to StackViewer (the widget) and StackBrowser (the toplevel).
+
+ * ScrolledList.py: Add close() method
+
+ * EditorWindow.py: Clarify 'Open Module' dialog text
+
+ * StackViewer.py: Restructured into a browser and a widget.
+
+Thu Oct 15 23:27:08 1998 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * ClassBrowser.py, ScrolledList.py:
+ Generalized the scrolled list which is the base for the class and
+ method browser into a separate class in its own module.
+
+ * Attic/test.py: Cosmetic change
+
+ * Debugger.py: Don't show function name if there is none
+
+Wed Oct 14 03:43:05 1998 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * Debugger.py, PyShell.py: Polish the Debugger GUI a bit.
+ Closing it now also does the right thing.
+
+Tue Oct 13 23:51:13 1998 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * Debugger.py, PyShell.py, Bindings.py:
+ Ad primitive debugger interface (so far it will step and show you the
+ source, but it doesn't yet show the stack).
+
+ * Attic/README: Misc
+
+ * StackViewer.py: Whoops -- referenced self.top before it was set.
+
+ * help.txt: Added history and completion commands.
+
+ * help.txt: Updated
+
+ * FileList.py: Add class browser functionality.
+
+ * StackViewer.py:
+ Add a close() method and bind to WM_DELETE_WINDOW protocol
+
+ * PyShell.py: Clear the linecache before printing a traceback
+
+ * Bindings.py: Added class browser binding.
+
+ * ClassBrowser.py: Much improved, much left to do.
+
+ * PyShell.py: Make the return key do what I mean more often.
+
+ * ClassBrowser.py:
+ Adding the beginnings of a Class browser. Incomplete, yet.
+
+ * EditorWindow.py, Bindings.py:
+ Add new command, "Open module". You select or type a module name,
+ and it opens the source.
+
+Mon Oct 12 23:59:27 1998 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * PyShell.py: Subsume functionality from Popup menu in Debug menu.
+ Other stuff so the PyShell window can be resurrected from the Windows menu.
+
+ * FileList.py: Get rid of PopUp menu.
+ Create a simple Windows menu. (Imperfect when Untitled windows exist.)
+ Add wakeup() method: deiconify, raise, focus.
+
+ * EditorWindow.py: Generalize menu creation.
+
+ * Bindings.py: Add Debug and Help menu items.
+
+ * EditorWindow.py: Added a menu bar to every window.
+
+ * Bindings.py: Add menu configuration to the event configuration.
+
+ * Attic/PopupMenu.py: Pass a root to the help window.
+
+ * SearchBinding.py:
+ Add parent argument to 'go to line number' dialog box.
+
+Sat Oct 10 19:15:32 1998 Guido van Rossum <guido@cnri.reston.va.us>
+
+ * StackViewer.py:
+ Add a label at the top showing (very basic) help for the stack viewer.
+ Add a label at the bottom showing the exception info.
+
+ * Attic/test.py, Attic/idle: Add Unix main script and test program.
+
+ * idle.pyw, help.txt, WidgetRedirector.py, UndoDelegator.py, StackViewer.py, SearchBinding.py, Attic/README, PyShell.py, Attic/PopupMenu.py, Percolator.py, Outline.py, IOBinding.py, History.py, Attic/HelpWindow.py, FrameViewer.py, FileList.py, EditorWindow.py, Delegator.py, ColorDelegator.py, Bindings.py, AutoIndent.py, AutoExpand.py:
+ Initial checking of Tk-based Python IDE.
+ Features: text editor with syntax coloring and undo;
+ subclassed into interactive Python shell which adds history.
+
diff --git a/lib/python2.7/idlelib/ClassBrowser.py b/lib/python2.7/idlelib/ClassBrowser.py
new file mode 100644
index 0000000..d09c52f
--- /dev/null
+++ b/lib/python2.7/idlelib/ClassBrowser.py
@@ -0,0 +1,236 @@
+"""Class browser.
+
+XXX TO DO:
+
+- reparse when source changed (maybe just a button would be OK?)
+ (or recheck on window popup)
+- add popup menu with more options (e.g. doc strings, base classes, imports)
+- show function argument list? (have to do pattern matching on source)
+- should the classes and methods lists also be in the module's menu bar?
+- add base classes to class browser tree
+"""
+
+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
+
+file_open = None # Method...Item and Class...Item use this.
+# Normally PyShell.flist.open, but there is no PyShell.flist for htest.
+
+class ClassBrowser:
+
+ def __init__(self, flist, name, path, _htest=False):
+ # XXX This API should change, if the file doesn't end in ".py"
+ # XXX the code here is bogus!
+ """
+ _htest - bool, change box when location running htest.
+ """
+ global file_open
+ if not _htest:
+ file_open = PyShell.flist.open
+ self.name = name
+ self.file = os.path.join(path[0], self.name + ".py")
+ self._htest = _htest
+ self.init(flist)
+
+ def close(self, event=None):
+ self.top.destroy()
+ self.node.destroy()
+
+ def init(self, flist):
+ self.flist = flist
+ # reset pyclbr
+ pyclbr._modules.clear()
+ # create top
+ self.top = top = ListedToplevel(flist.root)
+ top.protocol("WM_DELETE_WINDOW", self.close)
+ top.bind("<Escape>", self.close)
+ if self._htest: # place dialog below parent if running htest
+ top.geometry("+%d+%d" %
+ (flist.root.winfo_rootx(), flist.root.winfo_rooty() + 200))
+ self.settitle()
+ top.focus_set()
+ # create scrolled canvas
+ theme = idleConf.CurrentTheme()
+ background = idleConf.GetHighlight(theme, 'normal')['background']
+ sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1)
+ sc.frame.pack(expand=1, fill="both")
+ item = self.rootnode()
+ self.node = node = TreeNode(sc.canvas, None, item)
+ node.update()
+ node.expand()
+
+ def settitle(self):
+ self.top.wm_title("Class Browser - " + self.name)
+ self.top.wm_iconname("Class Browser")
+
+ def rootnode(self):
+ return ModuleBrowserTreeItem(self.file)
+
+class ModuleBrowserTreeItem(TreeItem):
+
+ def __init__(self, file):
+ self.file = file
+
+ def GetText(self):
+ return os.path.basename(self.file)
+
+ def GetIconName(self):
+ return "python"
+
+ def GetSubList(self):
+ sublist = []
+ for name in self.listclasses():
+ item = ClassBrowserTreeItem(name, self.classes, self.file)
+ sublist.append(item)
+ return sublist
+
+ def OnDoubleClick(self):
+ if os.path.normcase(self.file[-3:]) != ".py":
+ return
+ if not os.path.exists(self.file):
+ return
+ PyShell.flist.open(self.file)
+
+ def IsExpandable(self):
+ return os.path.normcase(self.file[-3:]) == ".py"
+
+ def listclasses(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:
+ 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
+ items.sort()
+ list = []
+ for item, s in items:
+ list.append(s)
+ return list
+
+class ClassBrowserTreeItem(TreeItem):
+
+ def __init__(self, name, classes, file):
+ self.name = name
+ self.classes = classes
+ self.file = file
+ try:
+ self.cl = self.classes[self.name]
+ except (IndexError, KeyError):
+ self.cl = None
+ self.isfunction = isinstance(self.cl, pyclbr.Function)
+
+ def GetText(self):
+ if self.isfunction:
+ return "def " + self.name + "(...)"
+ else:
+ return "class " + self.name
+
+ def GetIconName(self):
+ if self.isfunction:
+ return "python"
+ else:
+ return "folder"
+
+ def IsExpandable(self):
+ if self.cl:
+ try:
+ return not not self.cl.methods
+ except AttributeError:
+ return False
+
+ def GetSubList(self):
+ if not self.cl:
+ return []
+ sublist = []
+ for name in self.listmethods():
+ item = MethodBrowserTreeItem(name, self.cl, self.file)
+ sublist.append(item)
+ return sublist
+
+ def OnDoubleClick(self):
+ if not os.path.exists(self.file):
+ return
+ edit = file_open(self.file)
+ if hasattr(self.cl, 'lineno'):
+ lineno = self.cl.lineno
+ edit.gotoline(lineno)
+
+ def listmethods(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
+
+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 = file_open(self.file)
+ edit.gotoline(self.cl.methods[self.name])
+
+def _class_browser(parent): #Wrapper for htest
+ try:
+ file = __file__
+ except NameError:
+ file = sys.argv[0]
+ if sys.argv[1:]:
+ file = sys.argv[1]
+ else:
+ file = sys.argv[0]
+ dir, file = os.path.split(file)
+ name = os.path.splitext(file)[0]
+ flist = PyShell.PyShellFileList(parent)
+ global file_open
+ file_open = flist.open
+ ClassBrowser(flist, name, [dir], _htest=True)
+
+if __name__ == "__main__":
+ from idlelib.idle_test.htest import run
+ run(_class_browser)
diff --git a/lib/python2.7/idlelib/CodeContext.py b/lib/python2.7/idlelib/CodeContext.py
new file mode 100644
index 0000000..bb0cc9c
--- /dev/null
+++ b/lib/python2.7/idlelib/CodeContext.py
@@ -0,0 +1,176 @@
+"""CodeContext - Extension to display the block context above the edit window
+
+Once code has scrolled off the top of a window, it can be difficult to
+determine which block you are in. This extension implements a pane at the top
+of each IDLE edit window which provides block structure hints. These hints are
+the lines which contain the block opening keywords, e.g. 'if', for the
+enclosing block. The number of hint lines is determined by the numlines
+variable in the CodeContext section of config-extensions.def. Lines which do
+not open blocks are not shown in the context hints pane.
+
+"""
+import Tkinter
+from Tkconstants import TOP, LEFT, X, W, SUNKEN
+import re
+from sys import maxint as INFINITY
+from idlelib.configHandler import idleConf
+
+BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for",
+ "if", "try", "while", "with"}
+UPDATEINTERVAL = 100 # millisec
+FONTUPDATEINTERVAL = 1000 # millisec
+
+getspacesfirstword =\
+ lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups()
+
+class CodeContext:
+ menudefs = [('options', [('!Code Conte_xt', '<<toggle-code-context>>')])]
+ context_depth = idleConf.GetOption("extensions", "CodeContext",
+ "numlines", type="int", default=3)
+ bgcolor = idleConf.GetOption("extensions", "CodeContext",
+ "bgcolor", type="str", default="LightGray")
+ fgcolor = idleConf.GetOption("extensions", "CodeContext",
+ "fgcolor", type="str", default="Black")
+ def __init__(self, editwin):
+ self.editwin = editwin
+ self.text = editwin.text
+ self.textfont = self.text["font"]
+ self.label = None
+ # self.info is a list of (line number, indent level, line text, block
+ # keyword) tuples providing the block structure associated with
+ # self.topvisible (the linenumber of the line displayed at the top of
+ # the edit window). self.info[0] is initialized as a 'dummy' line which
+ # starts the toplevel 'block' of the module.
+ self.info = [(0, -1, "", False)]
+ self.topvisible = 1
+ visible = idleConf.GetOption("extensions", "CodeContext",
+ "visible", type="bool", default=False)
+ if visible:
+ self.toggle_code_context_event()
+ self.editwin.setvar('<<toggle-code-context>>', True)
+ # Start two update cycles, one for context lines, one for font changes.
+ self.text.after(UPDATEINTERVAL, self.timer_event)
+ self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
+
+ def toggle_code_context_event(self, event=None):
+ if not self.label:
+ # Calculate the border width and horizontal padding required to
+ # align the context with the text in the main Text widget.
+ #
+ # All values are passed through int(str(<value>)), since some
+ # values may be pixel objects, which can't simply be added to ints.
+ widgets = self.editwin.text, self.editwin.text_frame
+ # Calculate the required vertical padding
+ padx = 0
+ for widget in widgets:
+ padx += int(str( widget.pack_info()['padx'] ))
+ padx += int(str( widget.cget('padx') ))
+ # Calculate the required border width
+ border = 0
+ for widget in widgets:
+ border += int(str( widget.cget('border') ))
+ self.label = Tkinter.Label(self.editwin.top,
+ text="\n" * (self.context_depth - 1),
+ anchor=W, justify=LEFT,
+ font=self.textfont,
+ bg=self.bgcolor, fg=self.fgcolor,
+ width=1, #don't request more than we get
+ padx=padx, border=border,
+ relief=SUNKEN)
+ # Pack the label widget before and above the text_frame widget,
+ # thus ensuring that it will appear directly above text_frame
+ self.label.pack(side=TOP, fill=X, expand=False,
+ before=self.editwin.text_frame)
+ else:
+ self.label.destroy()
+ self.label = None
+ idleConf.SetOption("extensions", "CodeContext", "visible",
+ str(self.label is not None))
+ idleConf.SaveUserCfgFiles()
+
+ def get_line_info(self, linenum):
+ """Get the line indent value, text, and any block start keyword
+
+ If the line does not start a block, the keyword value is False.
+ The indentation of empty lines (or comment lines) is INFINITY.
+
+ """
+ text = self.text.get("%d.0" % linenum, "%d.end" % linenum)
+ spaces, firstword = getspacesfirstword(text)
+ opener = firstword in BLOCKOPENERS and firstword
+ if len(text) == len(spaces) or text[len(spaces)] == '#':
+ indent = INFINITY
+ else:
+ indent = len(spaces)
+ return indent, text, opener
+
+ def get_context(self, new_topvisible, stopline=1, stopindent=0):
+ """Get context lines, starting at new_topvisible and working backwards.
+
+ Stop when stopline or stopindent is reached. Return a tuple of context
+ data and the indent level at the top of the region inspected.
+
+ """
+ assert stopline > 0
+ lines = []
+ # The indentation level we are currently in:
+ 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):
+ indent, text, opener = self.get_line_info(linenum)
+ if indent < lastindent:
+ lastindent = indent
+ if opener in ("else", "elif"):
+ # We also show the if statement
+ lastindent += 1
+ if opener and linenum < new_topvisible and indent >= stopindent:
+ lines.append((linenum, indent, text, opener))
+ if lastindent <= stopindent:
+ break
+ lines.reverse()
+ return lines, lastindent
+
+ def update_code_context(self):
+ """Update context information and lines visible in the context pane.
+
+ """
+ new_topvisible = int(self.text.index("@0,0").split('.')[0])
+ if self.topvisible == new_topvisible: # haven't scrolled
+ return
+ if self.topvisible < new_topvisible: # scroll down
+ lines, lastindent = self.get_context(new_topvisible,
+ self.topvisible)
+ # retain only context info applicable to the region
+ # between topvisible and new_topvisible:
+ while self.info[-1][1] >= lastindent:
+ del self.info[-1]
+ elif self.topvisible > new_topvisible: # scroll up
+ stopindent = self.info[-1][1] + 1
+ # retain only context info associated
+ # with lines above new_topvisible:
+ while self.info[-1][0] >= new_topvisible:
+ stopindent = self.info[-1][1]
+ del self.info[-1]
+ lines, lastindent = self.get_context(new_topvisible,
+ self.info[-1][0]+1,
+ stopindent)
+ self.info.extend(lines)
+ self.topvisible = new_topvisible
+ # empty lines in context pane:
+ context_strings = [""] * max(0, self.context_depth - len(self.info))
+ # followed by the context hint lines:
+ context_strings += [x[2] for x in self.info[-self.context_depth:]]
+ self.label["text"] = '\n'.join(context_strings)
+
+ def timer_event(self):
+ if self.label:
+ self.update_code_context()
+ self.text.after(UPDATEINTERVAL, self.timer_event)
+
+ def font_timer_event(self):
+ newtextfont = self.text["font"]
+ if self.label and newtextfont != self.textfont:
+ self.textfont = newtextfont
+ self.label["font"] = self.textfont
+ self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
diff --git a/lib/python2.7/idlelib/ColorDelegator.py b/lib/python2.7/idlelib/ColorDelegator.py
new file mode 100644
index 0000000..fec2670
--- /dev/null
+++ b/lib/python2.7/idlelib/ColorDelegator.py
@@ -0,0 +1,258 @@
+import time
+import re
+import keyword
+import __builtin__
+from idlelib.Delegator import Delegator
+from idlelib.configHandler import idleConf
+
+DEBUG = False
+
+def any(name, alternates):
+ "Return a named group pattern matching list of alternates."
+ return "(?P<%s>" % name + "|".join(alternates) + ")"
+
+def make_pat():
+ kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b"
+ builtinlist = [str(name) for name in dir(__builtin__)
+ if not name.startswith('_')]
+ # We don't know whether "print" is a function or a keyword,
+ # so we always treat is as a keyword (the most common case).
+ builtinlist.remove('print')
+ # self.file = file("file") :
+ # 1st 'file' colorized normal, 2nd as builtin, 3rd as string
+ builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b"
+ comment = any("COMMENT", [r"#[^\n]*"])
+ stringprefix = r"(\br|u|ur|R|U|UR|Ur|uR|b|B|br|Br|bR|BR)?"
+ sqstring = stringprefix + r"'[^'\\\n]*(\\.[^'\\\n]*)*'?"
+ dqstring = stringprefix + r'"[^"\\\n]*(\\.[^"\\\n]*)*"?'
+ sq3string = stringprefix + r"'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
+ dq3string = stringprefix + r'"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
+ string = any("STRING", [sq3string, dq3string, sqstring, dqstring])
+ return kw + "|" + builtin + "|" + comment + "|" + string +\
+ "|" + any("SYNC", [r"\n"])
+
+prog = re.compile(make_pat(), re.S)
+idprog = re.compile(r"\s+(\w+)", re.S)
+
+class ColorDelegator(Delegator):
+
+ def __init__(self):
+ Delegator.__init__(self)
+ self.prog = prog
+ self.idprog = idprog
+ self.LoadTagDefs()
+
+ def setdelegate(self, delegate):
+ if self.delegate is not None:
+ self.unbind("<<toggle-auto-coloring>>")
+ Delegator.setdelegate(self, delegate)
+ if delegate is not None:
+ self.config_colors()
+ self.bind("<<toggle-auto-coloring>>", self.toggle_colorize_event)
+ self.notify_range("1.0", "end")
+ else:
+ # No delegate - stop any colorizing
+ self.stop_colorizing = True
+ self.allow_colorizing = False
+
+ def config_colors(self):
+ for tag, cnf in self.tagdefs.items():
+ if cnf:
+ self.tag_configure(tag, **cnf)
+ self.tag_raise('sel')
+
+ def LoadTagDefs(self):
+ theme = idleConf.CurrentTheme()
+ self.tagdefs = {
+ "COMMENT": idleConf.GetHighlight(theme, "comment"),
+ "KEYWORD": idleConf.GetHighlight(theme, "keyword"),
+ "BUILTIN": idleConf.GetHighlight(theme, "builtin"),
+ "STRING": idleConf.GetHighlight(theme, "string"),
+ "DEFINITION": idleConf.GetHighlight(theme, "definition"),
+ "SYNC": {'background':None,'foreground':None},
+ "TODO": {'background':None,'foreground':None},
+ "ERROR": idleConf.GetHighlight(theme, "error"),
+ # The following is used by ReplaceDialog:
+ "hit": idleConf.GetHighlight(theme, "hit"),
+ }
+
+ if DEBUG: print 'tagdefs',self.tagdefs
+
+ def insert(self, index, chars, tags=None):
+ index = self.index(index)
+ self.delegate.insert(index, chars, tags)
+ self.notify_range(index, index + "+%dc" % len(chars))
+
+ def delete(self, index1, index2=None):
+ index1 = self.index(index1)
+ self.delegate.delete(index1, index2)
+ self.notify_range(index1)
+
+ after_id = None
+ allow_colorizing = True
+ colorizing = False
+
+ def notify_range(self, index1, index2=None):
+ self.tag_add("TODO", index1, index2)
+ if self.after_id:
+ if DEBUG: print "colorizing already scheduled"
+ return
+ if self.colorizing:
+ self.stop_colorizing = True
+ if DEBUG: print "stop colorizing"
+ if self.allow_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
+
+ def close(self, close_when_done=None):
+ if self.after_id:
+ after_id = self.after_id
+ self.after_id = None
+ if DEBUG: print "cancel scheduled recolorizer"
+ self.after_cancel(after_id)
+ self.allow_colorizing = False
+ self.stop_colorizing = True
+ if close_when_done:
+ if not self.colorizing:
+ close_when_done.destroy()
+ else:
+ self.close_when_done = close_when_done
+
+ def toggle_colorize_event(self, event):
+ if self.after_id:
+ after_id = self.after_id
+ self.after_id = None
+ if DEBUG: print "cancel scheduled recolorizer"
+ self.after_cancel(after_id)
+ if self.allow_colorizing and self.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"
+ return "break"
+
+ def recolorize(self):
+ self.after_id = None
+ if not self.delegate:
+ if DEBUG: print "no delegate"
+ return
+ if not self.allow_colorizing:
+ if DEBUG: print "auto colorizing is off"
+ return
+ if self.colorizing:
+ if DEBUG: print "already colorizing"
+ return
+ try:
+ self.stop_colorizing = False
+ self.colorizing = True
+ if DEBUG: print "colorizing..."
+ t0 = time.clock()
+ self.recolorize_main()
+ t1 = time.clock()
+ 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"
+ self.after_id = self.after(1, self.recolorize)
+ if self.close_when_done:
+ top = self.close_when_done
+ self.close_when_done = None
+ top.destroy()
+
+ def recolorize_main(self):
+ next = "1.0"
+ while True:
+ item = self.tag_nextrange("TODO", next)
+ if not item:
+ break
+ head, tail = item
+ self.tag_remove("SYNC", head, tail)
+ item = self.tag_prevrange("SYNC", head)
+ if item:
+ head = item[1]
+ else:
+ head = "1.0"
+
+ chars = ""
+ next = head
+ lines_to_get = 1
+ ok = False
+ while not ok:
+ mark = next
+ next = self.index(mark + "+%d lines linestart" %
+ lines_to_get)
+ lines_to_get = min(lines_to_get * 2, 100)
+ ok = "SYNC" in self.tag_names(next + "-1c")
+ line = self.get(mark, next)
+ ##print head, "get", mark, next, "->", repr(line)
+ if not line:
+ return
+ for tag in self.tagdefs.keys():
+ self.tag_remove(tag, mark, next)
+ chars = chars + line
+ m = self.prog.search(chars)
+ while m:
+ for key, value in m.groupdict().items():
+ if value:
+ a, b = m.span(key)
+ self.tag_add(key,
+ head + "+%dc" % a,
+ head + "+%dc" % b)
+ if value in ("def", "class"):
+ m1 = self.idprog.match(chars, b)
+ if m1:
+ a, b = m1.span(1)
+ self.tag_add("DEFINITION",
+ head + "+%dc" % a,
+ head + "+%dc" % b)
+ m = self.prog.search(chars, m.end())
+ if "SYNC" in self.tag_names(next + "-1c"):
+ head = next
+ chars = ""
+ else:
+ ok = False
+ if not ok:
+ # We're in an inconsistent state, and the call to
+ # update may tell us to stop. It may also change
+ # the correct value for "next" (since this is a
+ # line.col string, not a true mark). So leave a
+ # crumb telling the next invocation to resume here
+ # in case update tells us to leave.
+ self.tag_add("TODO", next)
+ self.update()
+ if self.stop_colorizing:
+ if DEBUG: print "colorizing stopped"
+ return
+
+ def removecolors(self):
+ for tag in self.tagdefs.keys():
+ self.tag_remove(tag, "1.0", "end")
+
+def _color_delegator(parent): # htest #
+ from Tkinter import Toplevel, Text
+ from idlelib.Percolator import Percolator
+
+ top = Toplevel(parent)
+ top.title("Test ColorDelegator")
+ top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200,
+ parent.winfo_rooty() + 150))
+ source = "if somename: x = 'abc' # comment\nprint\n"
+ text = Text(top, background="white")
+ text.pack(expand=1, fill="both")
+ text.insert("insert", source)
+ text.focus_set()
+
+ p = Percolator(text)
+ d = ColorDelegator()
+ p.insertfilter(d)
+
+if __name__ == "__main__":
+ from idlelib.idle_test.htest import run
+ run(_color_delegator)
diff --git a/lib/python2.7/idlelib/Debugger.py b/lib/python2.7/idlelib/Debugger.py
new file mode 100644
index 0000000..c517065
--- /dev/null
+++ b/lib/python2.7/idlelib/Debugger.py
@@ -0,0 +1,529 @@
+import os
+import bdb
+from Tkinter import *
+from idlelib.WindowList import ListedToplevel
+from idlelib.ScrolledList import ScrolledList
+from idlelib import macosxSupport
+
+
+class Idb(bdb.Bdb):
+
+ def __init__(self, gui):
+ self.gui = gui
+ bdb.Bdb.__init__(self)
+
+ def user_line(self, frame):
+ if self.in_rpc_code(frame):
+ self.set_step()
+ return
+ message = self.__frame2message(frame)
+ try:
+ self.gui.interaction(message, frame)
+ except TclError: # When closing debugger window with [x] in 3.x
+ pass
+
+ def user_exception(self, frame, info):
+ if self.in_rpc_code(frame):
+ self.set_step()
+ return
+ message = self.__frame2message(frame)
+ self.gui.interaction(message, frame, info)
+
+ def in_rpc_code(self, frame):
+ if frame.f_code.co_filename.count('rpc.py'):
+ return True
+ else:
+ prev_frame = frame.f_back
+ if prev_frame.f_code.co_filename.count('Debugger.py'):
+ # (that test will catch both Debugger.py and RemoteDebugger.py)
+ return False
+ return self.in_rpc_code(prev_frame)
+
+ def __frame2message(self, frame):
+ code = frame.f_code
+ filename = code.co_filename
+ lineno = frame.f_lineno
+ basename = os.path.basename(filename)
+ message = "%s:%s" % (basename, lineno)
+ if code.co_name != "?":
+ message = "%s: %s()" % (message, code.co_name)
+ return message
+
+
+class Debugger:
+
+ vstack = vsource = vlocals = vglobals = None
+
+ def __init__(self, pyshell, idb=None):
+ if idb is None:
+ idb = Idb(self)
+ self.pyshell = pyshell
+ self.idb = idb
+ self.frame = None
+ self.make_gui()
+ self.interacting = 0
+ self.nesting_level = 0
+
+ def run(self, *args):
+ # Deal with the scenario where we've already got a program running
+ # in the debugger and we want to start another. If that is the case,
+ # our second 'run' was invoked from an event dispatched not from
+ # the main event loop, but from the nested event loop in 'interaction'
+ # below. So our stack looks something like this:
+ # outer main event loop
+ # run()
+ # <running program with traces>
+ # callback to debugger's interaction()
+ # nested event loop
+ # run() for second command
+ #
+ # This kind of nesting of event loops causes all kinds of problems
+ # (see e.g. issue #24455) especially when dealing with running as a
+ # subprocess, where there's all kinds of extra stuff happening in
+ # there - insert a traceback.print_stack() to check it out.
+ #
+ # By this point, we've already called restart_subprocess() in
+ # ScriptBinding. However, we also need to unwind the stack back to
+ # that outer event loop. To accomplish this, we:
+ # - return immediately from the nested run()
+ # - abort_loop ensures the nested event loop will terminate
+ # - the debugger's interaction routine completes normally
+ # - the restart_subprocess() will have taken care of stopping
+ # the running program, which will also let the outer run complete
+ #
+ # That leaves us back at the outer main event loop, at which point our
+ # after event can fire, and we'll come back to this routine with a
+ # clean stack.
+ if self.nesting_level > 0:
+ self.abort_loop()
+ self.root.after(100, lambda: self.run(*args))
+ return
+ try:
+ self.interacting = 1
+ return self.idb.run(*args)
+ finally:
+ self.interacting = 0
+
+ def close(self, event=None):
+ try:
+ self.quit()
+ except Exception:
+ pass
+ if self.interacting:
+ self.top.bell()
+ return
+ if self.stackviewer:
+ self.stackviewer.close(); self.stackviewer = None
+ # Clean up pyshell if user clicked debugger control close widget.
+ # (Causes a harmless extra cycle through close_debugger() if user
+ # toggled debugger from pyshell Debug menu)
+ self.pyshell.close_debugger()
+ # Now close the debugger control window....
+ self.top.destroy()
+
+ def make_gui(self):
+ pyshell = self.pyshell
+ self.flist = pyshell.flist
+ self.root = root = pyshell.root
+ self.top = top = ListedToplevel(root)
+ self.top.wm_title("Debug Control")
+ self.top.wm_iconname("Debug")
+ top.wm_protocol("WM_DELETE_WINDOW", self.close)
+ self.top.bind("<Escape>", self.close)
+ #
+ self.bframe = bframe = Frame(top)
+ self.bframe.pack(anchor="w")
+ self.buttons = bl = []
+ #
+ self.bcont = b = Button(bframe, text="Go", command=self.cont)
+ bl.append(b)
+ self.bstep = b = Button(bframe, text="Step", command=self.step)
+ bl.append(b)
+ self.bnext = b = Button(bframe, text="Over", command=self.next)
+ bl.append(b)
+ self.bret = b = Button(bframe, text="Out", command=self.ret)
+ bl.append(b)
+ self.bret = b = Button(bframe, text="Quit", command=self.quit)
+ bl.append(b)
+ #
+ for b in bl:
+ b.configure(state="disabled")
+ b.pack(side="left")
+ #
+ self.cframe = cframe = Frame(bframe)
+ self.cframe.pack(side="left")
+ #
+ if not self.vstack:
+ self.__class__.vstack = BooleanVar(top)
+ self.vstack.set(1)
+ self.bstack = Checkbutton(cframe,
+ text="Stack", command=self.show_stack, variable=self.vstack)
+ self.bstack.grid(row=0, column=0)
+ if not self.vsource:
+ self.__class__.vsource = BooleanVar(top)
+ self.bsource = Checkbutton(cframe,
+ text="Source", command=self.show_source, variable=self.vsource)
+ self.bsource.grid(row=0, column=1)
+ if not self.vlocals:
+ self.__class__.vlocals = BooleanVar(top)
+ self.vlocals.set(1)
+ self.blocals = Checkbutton(cframe,
+ text="Locals", command=self.show_locals, variable=self.vlocals)
+ self.blocals.grid(row=1, column=0)
+ if not self.vglobals:
+ self.__class__.vglobals = BooleanVar(top)
+ self.bglobals = Checkbutton(cframe,
+ text="Globals", command=self.show_globals, variable=self.vglobals)
+ self.bglobals.grid(row=1, column=1)
+ #
+ self.status = Label(top, anchor="w")
+ self.status.pack(anchor="w")
+ self.error = Label(top, anchor="w")
+ self.error.pack(anchor="w", fill="x")
+ self.errorbg = self.error.cget("background")
+ #
+ self.fstack = Frame(top, height=1)
+ self.fstack.pack(expand=1, fill="both")
+ self.flocals = Frame(top)
+ self.flocals.pack(expand=1, fill="both")
+ self.fglobals = Frame(top, height=1)
+ self.fglobals.pack(expand=1, fill="both")
+ #
+ if self.vstack.get():
+ self.show_stack()
+ if self.vlocals.get():
+ self.show_locals()
+ if self.vglobals.get():
+ self.show_globals()
+
+ def interaction(self, message, frame, info=None):
+ self.frame = frame
+ self.status.configure(text=message)
+ #
+ if info:
+ type, value, tb = info
+ try:
+ m1 = type.__name__
+ except AttributeError:
+ m1 = "%s" % str(type)
+ if value is not None:
+ try:
+ m1 = "%s: %s" % (m1, str(value))
+ except:
+ pass
+ bg = "yellow"
+ else:
+ m1 = ""
+ tb = None
+ bg = self.errorbg
+ self.error.configure(text=m1, background=bg)
+ #
+ sv = self.stackviewer
+ if sv:
+ stack, i = self.idb.get_stack(self.frame, tb)
+ sv.load_stack(stack, i)
+ #
+ self.show_variables(1)
+ #
+ if self.vsource.get():
+ self.sync_source_line()
+ #
+ for b in self.buttons:
+ b.configure(state="normal")
+ #
+ self.top.wakeup()
+ # Nested main loop: Tkinter's main loop is not reentrant, so use
+ # Tcl's vwait facility, which reenters the event loop until an
+ # event handler sets the variable we're waiting on
+ self.nesting_level += 1
+ self.root.tk.call('vwait', '::idledebugwait')
+ self.nesting_level -= 1
+ #
+ for b in self.buttons:
+ b.configure(state="disabled")
+ self.status.configure(text="")
+ self.error.configure(text="", background=self.errorbg)
+ self.frame = None
+
+ def sync_source_line(self):
+ frame = self.frame
+ if not frame:
+ return
+ filename, lineno = self.__frame2fileline(frame)
+ if filename[:1] + filename[-1:] != "<>" and os.path.exists(filename):
+ self.flist.gotofileline(filename, lineno)
+
+ def __frame2fileline(self, frame):
+ code = frame.f_code
+ filename = code.co_filename
+ lineno = frame.f_lineno
+ return filename, lineno
+
+ def cont(self):
+ self.idb.set_continue()
+ self.abort_loop()
+
+ def step(self):
+ self.idb.set_step()
+ self.abort_loop()
+
+ def next(self):
+ self.idb.set_next(self.frame)
+ self.abort_loop()
+
+ def ret(self):
+ self.idb.set_return(self.frame)
+ self.abort_loop()
+
+ def quit(self):
+ self.idb.set_quit()
+ self.abort_loop()
+
+ def abort_loop(self):
+ self.root.tk.call('set', '::idledebugwait', '1')
+
+ stackviewer = None
+
+ def show_stack(self):
+ if not self.stackviewer and self.vstack.get():
+ self.stackviewer = sv = StackViewer(self.fstack, self.flist, self)
+ if self.frame:
+ stack, i = self.idb.get_stack(self.frame, None)
+ sv.load_stack(stack, i)
+ else:
+ sv = self.stackviewer
+ if sv and not self.vstack.get():
+ self.stackviewer = None
+ sv.close()
+ self.fstack['height'] = 1
+
+ def show_source(self):
+ if self.vsource.get():
+ self.sync_source_line()
+
+ def show_frame(self, stackitem):
+ self.frame = stackitem[0] # lineno is stackitem[1]
+ self.show_variables()
+
+ localsviewer = None
+ globalsviewer = None
+
+ def show_locals(self):
+ lv = self.localsviewer
+ if self.vlocals.get():
+ if not lv:
+ self.localsviewer = NamespaceViewer(self.flocals, "Locals")
+ else:
+ if lv:
+ self.localsviewer = None
+ lv.close()
+ self.flocals['height'] = 1
+ self.show_variables()
+
+ def show_globals(self):
+ gv = self.globalsviewer
+ if self.vglobals.get():
+ if not gv:
+ self.globalsviewer = NamespaceViewer(self.fglobals, "Globals")
+ else:
+ if gv:
+ self.globalsviewer = None
+ gv.close()
+ self.fglobals['height'] = 1
+ self.show_variables()
+
+ def show_variables(self, force=0):
+ lv = self.localsviewer
+ gv = self.globalsviewer
+ frame = self.frame
+ if not frame:
+ ldict = gdict = None
+ else:
+ ldict = frame.f_locals
+ gdict = frame.f_globals
+ if lv and gv and ldict is gdict:
+ ldict = None
+ if lv:
+ lv.load_dict(ldict, force, self.pyshell.interp.rpcclt)
+ if gv:
+ gv.load_dict(gdict, force, self.pyshell.interp.rpcclt)
+
+ def set_breakpoint_here(self, filename, lineno):
+ self.idb.set_break(filename, lineno)
+
+ def clear_breakpoint_here(self, filename, lineno):
+ self.idb.clear_break(filename, lineno)
+
+ def clear_file_breaks(self, filename):
+ self.idb.clear_all_file_breaks(filename)
+
+ def load_breakpoints(self):
+ "Load PyShellEditorWindow breakpoints into subprocess debugger"
+ pyshell_edit_windows = self.pyshell.flist.inversedict.keys()
+ for editwin in pyshell_edit_windows:
+ filename = editwin.io.filename
+ try:
+ for lineno in editwin.breakpoints:
+ self.set_breakpoint_here(filename, lineno)
+ except AttributeError:
+ continue
+
+class StackViewer(ScrolledList):
+
+ def __init__(self, master, flist, gui):
+ if macosxSupport.isAquaTk():
+ # At least on with the stock AquaTk version on OSX 10.4 you'll
+ # get a shaking GUI that eventually kills IDLE if the width
+ # argument is specified.
+ ScrolledList.__init__(self, master)
+ else:
+ ScrolledList.__init__(self, master, width=80)
+ self.flist = flist
+ self.gui = gui
+ self.stack = []
+
+ def load_stack(self, stack, index=None):
+ self.stack = stack
+ self.clear()
+ for i in range(len(stack)):
+ frame, lineno = stack[i]
+ try:
+ modname = frame.f_globals["__name__"]
+ except:
+ modname = "?"
+ code = frame.f_code
+ filename = code.co_filename
+ funcname = code.co_name
+ import linecache
+ sourceline = linecache.getline(filename, lineno)
+ import string
+ sourceline = string.strip(sourceline)
+ if funcname in ("?", "", None):
+ item = "%s, line %d: %s" % (modname, lineno, sourceline)
+ else:
+ item = "%s.%s(), line %d: %s" % (modname, funcname,
+ lineno, sourceline)
+ if i == index:
+ item = "> " + item
+ self.append(item)
+ if index is not None:
+ self.select(index)
+
+ def popup_event(self, event):
+ "override base method"
+ if self.stack:
+ return ScrolledList.popup_event(self, event)
+
+ def fill_menu(self):
+ "override base method"
+ menu = self.menu
+ menu.add_command(label="Go to source line",
+ command=self.goto_source_line)
+ menu.add_command(label="Show stack frame",
+ command=self.show_stack_frame)
+
+ def on_select(self, index):
+ "override base method"
+ if 0 <= index < len(self.stack):
+ self.gui.show_frame(self.stack[index])
+
+ def on_double(self, index):
+ "override base method"
+ self.show_source(index)
+
+ def goto_source_line(self):
+ index = self.listbox.index("active")
+ self.show_source(index)
+
+ def show_stack_frame(self):
+ index = self.listbox.index("active")
+ if 0 <= index < len(self.stack):
+ self.gui.show_frame(self.stack[index])
+
+ def show_source(self, index):
+ if not (0 <= index < len(self.stack)):
+ return
+ frame, lineno = self.stack[index]
+ code = frame.f_code
+ filename = code.co_filename
+ if os.path.isfile(filename):
+ edit = self.flist.open(filename)
+ if edit:
+ edit.gotoline(lineno)
+
+
+class NamespaceViewer:
+
+ def __init__(self, master, title, dict=None):
+ width = 0
+ height = 40
+ if dict:
+ height = 20*len(dict) # XXX 20 == observed height of Entry widget
+ self.master = master
+ self.title = title
+ import repr
+ self.repr = repr.Repr()
+ self.repr.maxstring = 60
+ self.repr.maxother = 60
+ self.frame = frame = Frame(master)
+ self.frame.pack(expand=1, fill="both")
+ self.label = Label(frame, text=title, borderwidth=2, relief="groove")
+ self.label.pack(fill="x")
+ self.vbar = vbar = Scrollbar(frame, name="vbar")
+ vbar.pack(side="right", fill="y")
+ self.canvas = canvas = Canvas(frame,
+ height=min(300, max(40, height)),
+ scrollregion=(0, 0, width, height))
+ canvas.pack(side="left", fill="both", expand=1)
+ vbar["command"] = canvas.yview
+ canvas["yscrollcommand"] = vbar.set
+ self.subframe = subframe = Frame(canvas)
+ self.sfid = canvas.create_window(0, 0, window=subframe, anchor="nw")
+ self.load_dict(dict)
+
+ dict = -1
+
+ def load_dict(self, dict, force=0, rpc_client=None):
+ if dict is self.dict and not force:
+ return
+ subframe = self.subframe
+ frame = self.frame
+ for c in 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()
+ row = 0
+ for name in names:
+ value = dict[name]
+ svalue = self.repr.repr(value) # repr(value)
+ # Strip extra quotes caused by calling repr on the (already)
+ # repr'd value sent across the RPC interface:
+ if rpc_client:
+ svalue = svalue[1:-1]
+ l = Label(subframe, text=name)
+ l.grid(row=row, column=0, sticky="nw")
+ l = Entry(subframe, width=0, borderwidth=0)
+ l.insert(0, svalue)
+ l.grid(row=row, column=1, sticky="nw")
+ row = row+1
+ self.dict = dict
+ # XXX Could we use a <Configure> callback for the following?
+ subframe.update_idletasks() # Alas!
+ width = subframe.winfo_reqwidth()
+ height = subframe.winfo_reqheight()
+ canvas = self.canvas
+ self.canvas["scrollregion"] = (0, 0, width, height)
+ if height > 300:
+ canvas["height"] = 300
+ frame.pack(expand=1)
+ else:
+ canvas["height"] = height
+ frame.pack(expand=0)
+
+ def close(self):
+ self.frame.destroy()
diff --git a/lib/python2.7/idlelib/Delegator.py b/lib/python2.7/idlelib/Delegator.py
new file mode 100644
index 0000000..c476516
--- /dev/null
+++ b/lib/python2.7/idlelib/Delegator.py
@@ -0,0 +1,25 @@
+class Delegator:
+
+ # The cache is only used to be able to change delegates!
+
+ def __init__(self, delegate=None):
+ self.delegate = delegate
+ self.__cache = set()
+
+ def __getattr__(self, name):
+ attr = getattr(self.delegate, name) # May raise AttributeError
+ setattr(self, name, attr)
+ self.__cache.add(name)
+ return attr
+
+ def resetcache(self):
+ for key in self.__cache:
+ try:
+ delattr(self, key)
+ except AttributeError:
+ pass
+ self.__cache.clear()
+
+ def setdelegate(self, delegate):
+ self.resetcache()
+ self.delegate = delegate
diff --git a/lib/python2.7/idlelib/EditorWindow.py b/lib/python2.7/idlelib/EditorWindow.py
new file mode 100644
index 0000000..8a33719
--- /dev/null
+++ b/lib/python2.7/idlelib/EditorWindow.py
@@ -0,0 +1,1704 @@
+import sys
+import os
+import platform
+import re
+import imp
+from Tkinter import *
+import tkSimpleDialog
+import tkMessageBox
+import webbrowser
+
+from idlelib.MultiCall import MultiCallCreator
+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 idlelib import help
+
+# The default tab setting for a Text widget, in average-width characters.
+TK_TABWIDTH_DEFAULT = 8
+
+_py_version = ' (%s)' % platform.python_version()
+
+def _sphinx_version():
+ "Format sys.version_info to produce the Sphinx version string used to install the chm docs"
+ major, minor, micro, level, serial = sys.version_info
+ release = '%s%s' % (major, minor)
+ if micro:
+ release += '%s' % (micro,)
+ if level == 'candidate':
+ release += 'rc%s' % (serial,)
+ elif level != 'final':
+ release += '%s%s' % (level[0], serial)
+ return release
+
+def _find_module(fullname, path=None):
+ """Version of imp.find_module() that handles hierarchical module names"""
+
+ file = None
+ for tgt in fullname.split('.'):
+ if file is not None:
+ file.close() # close intermediate files
+ (file, filename, descr) = imp.find_module(tgt, path)
+ if descr[2] == imp.PY_SOURCE:
+ break # find but not load the source file
+ module = imp.load_module(tgt, file, filename, descr)
+ try:
+ path = module.__path__
+ except AttributeError:
+ raise ImportError, 'No source for module ' + module.__name__
+ if descr[2] != imp.PY_SOURCE:
+ # If all of the above fails and didn't raise an exception,fallback
+ # to a straight import which can find __init__.py in a package.
+ m = __import__(fullname)
+ try:
+ filename = m.__file__
+ except AttributeError:
+ pass
+ else:
+ file = None
+ base, ext = os.path.splitext(filename)
+ if ext == '.pyc':
+ ext = '.py'
+ filename = base + ext
+ descr = filename, None, imp.PY_SOURCE
+ return file, filename, descr
+
+
+class HelpDialog(object):
+
+ def __init__(self):
+ self.parent = None # parent of help window
+ self.dlg = None # the help window iteself
+
+ def display(self, parent, near=None):
+ """ Display the help dialog.
+
+ parent - parent widget for the help window
+
+ near - a Toplevel widget (e.g. EditorWindow or PyShell)
+ to use as a reference for placing the help window
+ """
+ import warnings as w
+ w.warn("EditorWindow.HelpDialog is no longer used by Idle.\n"
+ "It will be removed in 3.6 or later.\n"
+ "It has been replaced by private help.HelpWindow\n",
+ DeprecationWarning, stacklevel=2)
+ if self.dlg is None:
+ self.show_dialog(parent)
+ if near:
+ self.nearwindow(near)
+
+ def show_dialog(self, parent):
+ self.parent = parent
+ fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')
+ self.dlg = dlg = textView.view_file(parent,'Help',fn, modal=False)
+ dlg.bind('<Destroy>', self.destroy, '+')
+
+ def nearwindow(self, near):
+ # Place the help dialog near the window specified by parent.
+ # Note - this may not reposition the window in Metacity
+ # if "/apps/metacity/general/disable_workarounds" is enabled
+ dlg = self.dlg
+ geom = (near.winfo_rootx() + 10, near.winfo_rooty() + 10)
+ dlg.withdraw()
+ dlg.geometry("=+%d+%d" % geom)
+ dlg.deiconify()
+ dlg.lift()
+
+ def destroy(self, ev=None):
+ self.dlg = None
+ self.parent = None
+
+helpDialog = HelpDialog() # singleton instance, no longer used
+
+
+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 Tkinter import Toplevel
+ from idlelib.MultiStatusBar import MultiStatusBar
+
+ help_url = None
+
+ def __init__(self, flist=None, filename=None, key=None, root=None):
+ if EditorWindow.help_url is None:
+ dochome = os.path.join(sys.prefix, 'Doc', 'index.html')
+ if sys.platform.count('linux'):
+ # look for html docs in a couple of standard places
+ pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3]
+ if os.path.isdir('/var/www/html/python/'): # "python2" rpm
+ dochome = '/var/www/html/python/index.html'
+ else:
+ basepath = '/usr/share/doc/' # standard location
+ dochome = os.path.join(basepath, pyver,
+ 'Doc', 'index.html')
+ elif sys.platform[:3] == 'win':
+ chmfile = os.path.join(sys.prefix, 'Doc',
+ 'Python%s.chm' % _sphinx_version())
+ if os.path.isfile(chmfile):
+ dochome = chmfile
+ elif sys.platform == 'darwin':
+ # documentation may be stored inside a python framework
+ dochome = os.path.join(sys.prefix,
+ 'Resources/English.lproj/Documentation/index.html')
+ dochome = os.path.normpath(dochome)
+ if os.path.isfile(dochome):
+ EditorWindow.help_url = dochome
+ if sys.platform == 'darwin':
+ # Safari requires real file:-URLs
+ EditorWindow.help_url = 'file://' + EditorWindow.help_url
+ else:
+ EditorWindow.help_url = "https://docs.python.org/%d.%d/" % sys.version_info[:2]
+ self.flist = flist
+ root = root or flist.root
+ self.root = root
+ try:
+ sys.ps1
+ except AttributeError:
+ sys.ps1 = '>>> '
+ self.menubar = Menu(root)
+ self.top = top = WindowList.ListedToplevel(root, menu=self.menubar)
+ if flist:
+ self.tkinter_vars = flist.vars
+ #self.top.instance_dict makes flist.inversedict available to
+ #configDialog.py so it can access all EditorWindow instances
+ self.top.instance_dict = flist.inversedict
+ else:
+ self.tkinter_vars = {} # keys: Tkinter event names
+ # values: Tkinter variable instances
+ self.top.instance_dict = {}
+ self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(),
+ 'recent-files.lst')
+ self.text_frame = text_frame = Frame(top)
+ self.vbar = vbar = Scrollbar(text_frame, name='vbar')
+ self.width = idleConf.GetOption('main','EditorWindow','width', type='int')
+ text_options = {
+ 'name': 'text',
+ 'padx': 5,
+ 'wrap': 'none',
+ 'highlightthickness': 0,
+ 'width': self.width,
+ 'height': idleConf.GetOption('main', 'EditorWindow', 'height', type='int')}
+ if TkVersion >= 8.5:
+ # Starting with tk 8.5 we have to set the new tabstyle option
+ # to 'wordprocessor' to achieve the same display of tabs as in
+ # older tk versions.
+ text_options['tabstyle'] = 'wordprocessor'
+ self.text = text = MultiCallCreator(Text)(text_frame, **text_options)
+ self.top.focused_widget = self.text
+
+ self.createmenubar()
+ self.apply_bindings()
+
+ self.top.protocol("WM_DELETE_WINDOW", self.close)
+ self.top.bind("<<close-window>>", self.close_event)
+ if macosxSupport.isAquaTk():
+ # Command-W on editorwindows doesn't work without this.
+ text.bind('<<close-window>>', self.close_event)
+ # Some OS X systems have only one mouse button, so use
+ # control-click for popup context menus there. For two
+ # buttons, AquaTk defines <2> as the right button, not <3>.
+ text.bind("<Control-Button-1>",self.right_menu_event)
+ text.bind("<2>", self.right_menu_event)
+ else:
+ # Elsewhere, use right-click for popup menus.
+ text.bind("<3>",self.right_menu_event)
+ text.bind("<<cut>>", self.cut)
+ text.bind("<<copy>>", self.copy)
+ text.bind("<<paste>>", self.paste)
+ text.bind("<<center-insert>>", self.center_insert_event)
+ text.bind("<<help>>", self.help_dialog)
+ text.bind("<<python-docs>>", self.python_docs)
+ text.bind("<<about-idle>>", self.about_dialog)
+ text.bind("<<open-config-dialog>>", self.config_dialog)
+ text.bind("<<open-module>>", self.open_module)
+ text.bind("<<do-nothing>>", lambda event: "break")
+ text.bind("<<select-all>>", self.select_all)
+ text.bind("<<remove-selection>>", self.remove_selection)
+ text.bind("<<find>>", self.find_event)
+ text.bind("<<find-again>>", self.find_again_event)
+ text.bind("<<find-in-files>>", self.find_in_files_event)
+ text.bind("<<find-selection>>", self.find_selection_event)
+ text.bind("<<replace>>", self.replace_event)
+ text.bind("<<goto-line>>", self.goto_line_event)
+ text.bind("<<smart-backspace>>",self.smart_backspace_event)
+ text.bind("<<newline-and-indent>>",self.newline_and_indent_event)
+ text.bind("<<smart-indent>>",self.smart_indent_event)
+ text.bind("<<indent-region>>",self.indent_region_event)
+ text.bind("<<dedent-region>>",self.dedent_region_event)
+ text.bind("<<comment-region>>",self.comment_region_event)
+ text.bind("<<uncomment-region>>",self.uncomment_region_event)
+ text.bind("<<tabify-region>>",self.tabify_region_event)
+ text.bind("<<untabify-region>>",self.untabify_region_event)
+ text.bind("<<toggle-tabs>>",self.toggle_tabs_event)
+ text.bind("<<change-indentwidth>>",self.change_indentwidth_event)
+ text.bind("<Left>", self.move_at_edge_if_selection(0))
+ text.bind("<Right>", self.move_at_edge_if_selection(1))
+ text.bind("<<del-word-left>>", self.del_word_left)
+ text.bind("<<del-word-right>>", self.del_word_right)
+ text.bind("<<beginning-of-line>>", self.home_callback)
+
+ if flist:
+ flist.inversedict[self] = key
+ if key:
+ flist.dict[key] = self
+ text.bind("<<open-new-window>>", self.new_callback)
+ text.bind("<<close-all-windows>>", self.flist.close_all_callback)
+ text.bind("<<open-class-browser>>", self.open_class_browser)
+ text.bind("<<open-path-browser>>", self.open_path_browser)
+
+ self.set_status_bar()
+ vbar['command'] = text.yview
+ vbar.pack(side=RIGHT, fill=Y)
+ text['yscrollcommand'] = vbar.set
+ text['font'] = idleConf.GetFont(self.root, 'main', 'EditorWindow')
+ text_frame.pack(side=LEFT, fill=BOTH, expand=1)
+ text.pack(side=TOP, fill=BOTH, expand=1)
+ text.focus_set()
+
+ # usetabs true -> literal tab characters are used by indent and
+ # dedent cmds, possibly mixed with spaces if
+ # indentwidth is not a multiple of tabwidth,
+ # which will cause Tabnanny to nag!
+ # false -> tab characters are converted to spaces by indent
+ # and dedent cmds, and ditto TAB keystrokes
+ # Although use-spaces=0 can be configured manually in config-main.def,
+ # configuration of tabs v. spaces is not supported in the configuration
+ # dialog. IDLE promotes the preferred Python indentation: use spaces!
+ usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces', type='bool')
+ self.usetabs = not usespaces
+
+ # tabwidth is the display width of a literal tab character.
+ # CAUTION: telling Tk to use anything other than its default
+ # tab setting causes it to use an entirely different tabbing algorithm,
+ # treating tab stops as fixed distances from the left margin.
+ # Nobody expects this, so for now tabwidth should never be changed.
+ self.tabwidth = 8 # must remain 8 until Tk is fixed.
+
+ # indentwidth is the number of screen characters per indent level.
+ # The recommended Python indentation is four spaces.
+ self.indentwidth = self.tabwidth
+ self.set_notabs_indentwidth()
+
+ # If context_use_ps1 is true, parsing searches back for a ps1 line;
+ # else searches for a popular (if, def, ...) Python stmt.
+ self.context_use_ps1 = False
+
+ # When searching backwards for a reliable place to begin parsing,
+ # first start num_context_lines[0] lines back, then
+ # num_context_lines[1] lines back if that didn't work, and so on.
+ # The last value should be huge (larger than the # of lines in a
+ # 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, tearoff=0)
+ self.menudict['file'].insert_cascade(3, label='Recent Files',
+ underline=0,
+ menu=self.recent_files_menu)
+ self.update_recent_files_list()
+
+ self.color = None # initialized below in self.ResetColorizer
+ if filename:
+ if os.path.exists(filename) and not os.path.isdir(filename):
+ io.loadfile(filename)
+ else:
+ io.set_filename(filename)
+ self.ResetColorizer()
+ self.saved_change_hook()
+
+ self.set_indentation_params(self.ispythonsource(filename))
+
+ self.load_extensions()
+
+ menu = self.menudict.get('windows')
+ if menu:
+ end = menu.index("end")
+ if end is None:
+ end = -1
+ if end >= 0:
+ menu.add_separator()
+ end = end + 1
+ self.wmenu_end = end
+ WindowList.register_callback(self.postwindowsmenu)
+
+ # Some abstractions so IDLE extensions are cross-IDE
+ self.askyesno = tkMessageBox.askyesno
+ self.askinteger = tkSimpleDialog.askinteger
+ self.showerror = tkMessageBox.showerror
+
+ def _filename_to_unicode(self, filename):
+ """convert filename to unicode in order to display it in Tk"""
+ if isinstance(filename, unicode) or not filename:
+ return filename
+ else:
+ try:
+ return filename.decode(self.filesystemencoding)
+ except UnicodeDecodeError:
+ # XXX
+ try:
+ return filename.decode(self.encoding)
+ except UnicodeDecodeError:
+ # byte-to-byte conversion
+ return filename.decode('iso8859-1')
+
+ def new_callback(self, event):
+ dirname, basename = self.io.defaultfilename()
+ self.flist.new(dirname)
+ return "break"
+
+ def home_callback(self, event):
+ if (event.state & 4) != 0 and event.keysym == "Home":
+ # state&4==Control. If <Control-Home>, use the Tk binding.
+ return
+ if self.text.index("iomark") and \
+ self.text.compare("iomark", "<=", "insert lineend") and \
+ self.text.compare("insert linestart", "<=", "iomark"):
+ # In Shell on input line, go to just after prompt
+ insertpt = int(self.text.index("iomark").split(".")[1])
+ else:
+ line = self.text.get("insert linestart", "insert lineend")
+ for insertpt in xrange(len(line)):
+ if line[insertpt] not in (' ','\t'):
+ break
+ else:
+ insertpt=len(line)
+ lineat = int(self.text.index("insert").split('.')[1])
+ if insertpt == lineat:
+ insertpt = 0
+ dest = "insert linestart+"+str(insertpt)+"c"
+ if (event.state&1) == 0:
+ # shift was not pressed
+ self.text.tag_remove("sel", "1.0", "end")
+ else:
+ if not self.text.index("sel.first"):
+ self.text.mark_set("my_anchor", "insert") # there was no previous selection
+ else:
+ if self.text.compare(self.text.index("sel.first"), "<", self.text.index("insert")):
+ self.text.mark_set("my_anchor", "sel.first") # extend back
+ else:
+ self.text.mark_set("my_anchor", "sel.last") # extend forward
+ first = self.text.index(dest)
+ last = self.text.index("my_anchor")
+ if self.text.compare(first,">",last):
+ first,last = last,first
+ self.text.tag_remove("sel", "1.0", "end")
+ self.text.tag_add("sel", first, last)
+ self.text.mark_set("insert", dest)
+ self.text.see("insert")
+ return "break"
+
+ def set_status_bar(self):
+ self.status_bar = self.MultiStatusBar(self.top)
+ sep = Frame(self.top, height=1, borderwidth=1, background='grey75')
+ if sys.platform == "darwin":
+ # Insert some padding to avoid obscuring some of the statusbar
+ # by the resize widget.
+ self.status_bar.set_label('_padding1', ' ', side=RIGHT)
+ self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
+ self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
+ self.status_bar.pack(side=BOTTOM, fill=X)
+ sep.pack(side=BOTTOM, fill=X)
+ self.text.bind("<<set-line-and-column>>", self.set_line_and_column)
+ self.text.event_add("<<set-line-and-column>>",
+ "<KeyRelease>", "<ButtonRelease>")
+ self.text.after_idle(self.set_line_and_column)
+
+ def set_line_and_column(self, event=None):
+ line, column = self.text.index(INSERT).split('.')
+ self.status_bar.set_label('column', 'Col: %s' % column)
+ self.status_bar.set_label('line', 'Ln: %s' % line)
+
+ menu_specs = [
+ ("file", "_File"),
+ ("edit", "_Edit"),
+ ("format", "F_ormat"),
+ ("run", "_Run"),
+ ("options", "_Options"),
+ ("windows", "_Window"),
+ ("help", "_Help"),
+ ]
+
+
+ def createmenubar(self):
+ mbar = self.menubar
+ self.menudict = menudict = {}
+ for name, label in self.menu_specs:
+ underline, label = prepstr(label)
+ menudict[name] = menu = Menu(mbar, name=name, tearoff=0)
+ mbar.add_cascade(label=label, menu=menu, underline=underline)
+
+ if macosxSupport.isCarbonTk():
+ # Insert the application menu
+ menudict['application'] = menu = Menu(mbar, name='apple',
+ tearoff=0)
+ mbar.add_cascade(label='IDLE', menu=menu)
+
+ self.fill_menus()
+ self.base_helpmenu_length = self.menudict['help'].index(END)
+ self.reset_help_menu_entries()
+
+ def postwindowsmenu(self):
+ # Only called when Windows menu exists
+ menu = self.menudict['windows']
+ end = menu.index("end")
+ if end is None:
+ end = -1
+ if end > self.wmenu_end:
+ menu.delete(self.wmenu_end+1, end)
+ WindowList.add_windows_to_menu(menu)
+
+ rmenu = None
+
+ def right_menu_event(self, event):
+ self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
+ if not self.rmenu:
+ self.make_rmenu()
+ rmenu = self.rmenu
+ self.event = event
+ iswin = sys.platform[:3] == 'win'
+ if iswin:
+ self.text.config(cursor="arrow")
+
+ for item in self.rmenu_specs:
+ try:
+ label, eventname, verify_state = item
+ except ValueError: # see issue1207589
+ continue
+
+ 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", "<<virtual-event>>", "statefuncname"), ...
+ ("Close", "<<close-window>>", None), # Example
+ ]
+
+ def make_rmenu(self):
+ rmenu = Menu(self.text, tearoff=0)
+ for item in self.rmenu_specs:
+ label, eventname = item[0], item[1]
+ 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):
+ return self.rmenu_check_copy()
+
+ 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):
+ "Handle Help 'About IDLE' event."
+ # Synchronize with macosxSupport.overrideRootMenu.about_dialog.
+ aboutDialog.AboutDialog(self.top,'About IDLE')
+
+ def config_dialog(self, event=None):
+ "Handle Options 'Configure IDLE' event."
+ # Synchronize with macosxSupport.overrideRootMenu.config_dialog.
+ configDialog.ConfigDialog(self.top,'Settings')
+
+ def help_dialog(self, event=None):
+ "Handle Help 'IDLE Help' event."
+ # Synchronize with macosxSupport.overrideRootMenu.help_dialog.
+ if self.root:
+ parent = self.root
+ else:
+ parent = self.top
+ help.show_idlehelp(parent)
+
+ def python_docs(self, event=None):
+ if sys.platform[:3] == 'win':
+ try:
+ os.startfile(self.help_url)
+ except WindowsError as why:
+ tkMessageBox.showerror(title='Document Start Failure',
+ message=str(why), parent=self.text)
+ else:
+ webbrowser.open(self.help_url)
+ return "break"
+
+ def cut(self,event):
+ self.text.event_generate("<<Cut>>")
+ return "break"
+
+ def copy(self,event):
+ if not self.text.tag_ranges("sel"):
+ # There is no selection, so do nothing and maybe interrupt.
+ return
+ self.text.event_generate("<<Copy>>")
+ return "break"
+
+ def paste(self,event):
+ self.text.event_generate("<<Paste>>")
+ self.text.see("insert")
+ return "break"
+
+ def select_all(self, event=None):
+ self.text.tag_add("sel", "1.0", "end-1c")
+ self.text.mark_set("insert", "1.0")
+ self.text.see("insert")
+ return "break"
+
+ def remove_selection(self, event=None):
+ self.text.tag_remove("sel", "1.0", "end")
+ self.text.see("insert")
+
+ def move_at_edge_if_selection(self, edge_index):
+ """Cursor move begins at start or end of selection
+
+ When a left/right cursor key is pressed create and return to Tkinter a
+ function which causes a cursor move from the associated edge of the
+ selection.
+
+ """
+ self_text_index = self.text.index
+ self_text_mark_set = self.text.mark_set
+ edges_table = ("sel.first+1c", "sel.last-1c")
+ def move_at_edge(event):
+ if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed
+ try:
+ self_text_index("sel.first")
+ self_text_mark_set("insert", edges_table[edge_index])
+ except TclError:
+ pass
+ return move_at_edge
+
+ def del_word_left(self, event):
+ self.text.event_generate('<Meta-Delete>')
+ return "break"
+
+ def del_word_right(self, event):
+ self.text.event_generate('<Meta-d>')
+ return "break"
+
+ def find_event(self, event):
+ SearchDialog.find(self.text)
+ return "break"
+
+ def find_again_event(self, event):
+ SearchDialog.find_again(self.text)
+ return "break"
+
+ def find_selection_event(self, event):
+ SearchDialog.find_selection(self.text)
+ return "break"
+
+ def find_in_files_event(self, event):
+ GrepDialog.grep(self.text, self.io, self.flist)
+ return "break"
+
+ def replace_event(self, event):
+ ReplaceDialog.replace(self.text)
+ return "break"
+
+ def goto_line_event(self, event):
+ text = self.text
+ lineno = tkSimpleDialog.askinteger("Goto",
+ "Go to line number:",parent=text)
+ if lineno is None:
+ return "break"
+ if lineno <= 0:
+ text.bell()
+ return "break"
+ text.mark_set("insert", "%d.0" % lineno)
+ text.see("insert")
+
+ def open_module(self, event=None):
+ # XXX Shouldn't this be in IOBinding or in FileList?
+ try:
+ name = self.text.get("sel.first", "sel.last")
+ except TclError:
+ name = ""
+ else:
+ name = name.strip()
+ name = tkSimpleDialog.askstring("Module",
+ "Enter the name of a Python module\n"
+ "to search on sys.path and open:",
+ parent=self.text, initialvalue=name)
+ if name:
+ name = name.strip()
+ if not name:
+ return
+ # XXX Ought to insert current file's directory in front of path
+ try:
+ (f, file_path, (suffix, mode, mtype)) = _find_module(name)
+ except (NameError, ImportError) as msg:
+ tkMessageBox.showerror("Import error", str(msg), parent=self.text)
+ return
+ if mtype != imp.PY_SOURCE:
+ tkMessageBox.showerror("Unsupported type",
+ "%s is not a source module" % name, parent=self.text)
+ return
+ if f:
+ f.close()
+ if self.flist:
+ self.flist.open(file_path)
+ else:
+ self.io.loadfile(file_path)
+ return file_path
+
+ def open_class_browser(self, event=None):
+ filename = self.io.filename
+ if not (self.__class__.__name__ == 'PyShellEditorWindow'
+ and filename):
+ filename = self.open_module()
+ if filename is None:
+ return
+ head, tail = os.path.split(filename)
+ base, ext = os.path.splitext(tail)
+ from idlelib import ClassBrowser
+ ClassBrowser.ClassBrowser(self.flist, base, [head])
+
+ def open_path_browser(self, event=None):
+ from idlelib import PathBrowser
+ PathBrowser.PathBrowser(self.flist)
+
+ def gotoline(self, lineno):
+ if lineno is not None and lineno > 0:
+ self.text.mark_set("insert", "%d.0" % lineno)
+ self.text.tag_remove("sel", "1.0", "end")
+ self.text.tag_add("sel", "insert", "insert +1l")
+ self.center()
+
+ def ispythonsource(self, filename):
+ if not filename or os.path.isdir(filename):
+ return True
+ 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
+
+ def close_hook(self):
+ if self.flist:
+ self.flist.unregister_maybe_terminate(self)
+ self.flist = None
+
+ def set_close_hook(self, close_hook):
+ self.close_hook = close_hook
+
+ def filename_change_hook(self):
+ if self.flist:
+ self.flist.filename_changed_edit(self)
+ self.saved_change_hook()
+ self.top.update_windowlist_registry(self)
+ self.ResetColorizer()
+
+ def _addcolorizer(self):
+ if self.color:
+ return
+ if self.ispythonsource(self.io.filename):
+ self.color = self.ColorDelegator()
+ # can add more colorizers here...
+ if self.color:
+ self.per.removefilter(self.undo)
+ self.per.insertfilter(self.color)
+ self.per.insertfilter(self.undo)
+
+ def _rmcolorizer(self):
+ if not self.color:
+ return
+ self.color.removecolors()
+ self.per.removefilter(self.color)
+ self.color = None
+
+ def ResetColorizer(self):
+ "Update the color theme"
+ # Called from self.filename_change_hook and from configDialog.py
+ self._rmcolorizer()
+ self._addcolorizer()
+ theme = idleConf.CurrentTheme()
+ normal_colors = idleConf.GetHighlight(theme, 'normal')
+ cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg')
+ select_colors = idleConf.GetHighlight(theme, 'hilite')
+ self.text.config(
+ foreground=normal_colors['foreground'],
+ background=normal_colors['background'],
+ insertbackground=cursor_color,
+ selectforeground=select_colors['foreground'],
+ selectbackground=select_colors['background'],
+ )
+ if TkVersion >= 8.5:
+ self.text.config(
+ inactiveselectbackground=select_colors['background'])
+
+ def ResetFont(self):
+ "Update the text widgets' font if it is changed"
+ # Called from configDialog.py
+
+ self.text['font'] = idleConf.GetFont(self.root, 'main','EditorWindow')
+
+ def RemoveKeybindings(self):
+ "Remove the keybindings before they are changed."
+ # Called from configDialog.py
+ self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
+ for event, keylist in keydefs.items():
+ self.text.event_delete(event, *keylist)
+ for extensionName in self.get_standard_extension_names():
+ xkeydefs = idleConf.GetExtensionBindings(extensionName)
+ if xkeydefs:
+ for event, keylist in xkeydefs.items():
+ self.text.event_delete(event, *keylist)
+
+ def ApplyKeybindings(self):
+ "Update the keybindings after they are changed"
+ # Called from configDialog.py
+ self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
+ self.apply_bindings()
+ for extensionName in self.get_standard_extension_names():
+ xkeydefs = idleConf.GetExtensionBindings(extensionName)
+ if xkeydefs:
+ self.apply_bindings(xkeydefs)
+ #update menu accelerators
+ menuEventDict = {}
+ for menu in self.Bindings.menudefs:
+ menuEventDict[menu[0]] = {}
+ for item in menu[1]:
+ if item:
+ menuEventDict[menu[0]][prepstr(item[0])[1]] = item[1]
+ for menubarItem in self.menudict.keys():
+ menu = self.menudict[menubarItem]
+ end = menu.index(END)
+ if end is None:
+ # Skip empty menus
+ continue
+ end += 1
+ for index in range(0, end):
+ if menu.type(index) == 'command':
+ accel = menu.entrycget(index, 'accelerator')
+ if accel:
+ itemName = menu.entrycget(index, 'label')
+ event = ''
+ if menubarItem in menuEventDict:
+ if itemName in menuEventDict[menubarItem]:
+ event = menuEventDict[menubarItem][itemName]
+ if event:
+ accel = get_accelerator(keydefs, event)
+ menu.entryconfig(index, accelerator=accel)
+
+ def set_notabs_indentwidth(self):
+ "Update the indentwidth if changed and not using tabs in this window"
+ # Called from configDialog.py
+ if not self.usetabs:
+ self.indentwidth = idleConf.GetOption('main', 'Indent','num-spaces',
+ type='int')
+
+ def reset_help_menu_entries(self):
+ "Update the additional help entries on the Help menu"
+ help_list = idleConf.GetAllExtraHelpSourcesList()
+ helpmenu = self.menudict['help']
+ # first delete the extra help entries, if any
+ helpmenu_length = helpmenu.index(END)
+ if helpmenu_length > self.base_helpmenu_length:
+ helpmenu.delete((self.base_helpmenu_length + 1), helpmenu_length)
+ # then rebuild them
+ if help_list:
+ helpmenu.add_separator()
+ for entry in help_list:
+ cmd = self.__extra_help_callback(entry[1])
+ helpmenu.add_command(label=entry[0], command=cmd)
+ # and update the menu dictionary
+ self.menudict['help'] = helpmenu
+
+ def __extra_help_callback(self, helpfile):
+ "Create a callback with the helpfile value frozen at definition time"
+ def display_extra_help(helpfile=helpfile):
+ if not helpfile.startswith(('www', 'http')):
+ helpfile = os.path.normpath(helpfile)
+ if sys.platform[:3] == 'win':
+ try:
+ os.startfile(helpfile)
+ except WindowsError as why:
+ tkMessageBox.showerror(title='Document Start Failure',
+ message=str(why), parent=self.text)
+ else:
+ webbrowser.open(helpfile)
+ return display_extra_help
+
+ def update_recent_files_list(self, new_file=None):
+ "Load and update the recent files list and menus"
+ rf_list = []
+ if os.path.exists(self.recent_files_path):
+ with open(self.recent_files_path, 'r') as rf_list_file:
+ rf_list = rf_list_file.readlines()
+ if new_file:
+ new_file = os.path.abspath(new_file) + '\n'
+ if new_file in rf_list:
+ rf_list.remove(new_file) # move to top
+ rf_list.insert(0, new_file)
+ # clean and save the recent files list
+ bad_paths = []
+ for path in rf_list:
+ if '\0' in path or not os.path.exists(path[0:-1]):
+ bad_paths.append(path)
+ rf_list = [path for path in rf_list if path not in bad_paths]
+ ulchars = "1234567890ABCDEFGHIJK"
+ rf_list = rf_list[0:len(ulchars)]
+ try:
+ with open(self.recent_files_path, 'w') as rf_file:
+ rf_file.writelines(rf_list)
+ except IOError as err:
+ if not getattr(self.root, "recentfilelist_error_displayed", False):
+ self.root.recentfilelist_error_displayed = True
+ tkMessageBox.showwarning(title='IDLE Warning',
+ message="Cannot update File menu Recent Files list. "
+ "Your operating system says:\n%s\n"
+ "Select OK and IDLE will continue without updating."
+ % str(err),
+ parent=self.text)
+ # for each edit window instance, construct the recent files menu
+ for instance in self.top.instance_dict.keys():
+ menu = instance.recent_files_menu
+ menu.delete(0, END) # clear, and rebuild:
+ for i, file_name in enumerate(rf_list):
+ file_name = file_name.rstrip() # zap \n
+ # make unicode string to display non-ASCII chars correctly
+ ufile_name = self._filename_to_unicode(file_name)
+ callback = instance.__recent_file_callback(file_name)
+ menu.add_command(label=ulchars[i] + " " + ufile_name,
+ command=callback,
+ underline=0)
+
+ def __recent_file_callback(self, file_name):
+ def open_recent_file(fn_closure=file_name):
+ self.io.open(editFile=fn_closure)
+ return open_recent_file
+
+ def saved_change_hook(self):
+ short = self.short_title()
+ long = self.long_title()
+ if short and long:
+ title = short + " - " + long + _py_version
+ elif short:
+ title = short
+ elif long:
+ title = long
+ else:
+ title = "Untitled"
+ icon = short or long or title
+ if not self.get_saved():
+ title = "*%s*" % title
+ icon = "*%s" % icon
+ self.top.wm_title(title)
+ self.top.wm_iconname(icon)
+
+ def get_saved(self):
+ return self.undo.get_saved()
+
+ def set_saved(self, flag):
+ self.undo.set_saved(flag)
+
+ def reset_undo(self):
+ self.undo.reset_undo()
+
+ def short_title(self):
+ filename = self.io.filename
+ if filename:
+ filename = os.path.basename(filename)
+ else:
+ filename = "Untitled"
+ # return unicode string to display non-ASCII chars correctly
+ return self._filename_to_unicode(filename)
+
+ def long_title(self):
+ # return unicode string to display non-ASCII chars correctly
+ return self._filename_to_unicode(self.io.filename or "")
+
+ def center_insert_event(self, event):
+ self.center()
+
+ def center(self, mark="insert"):
+ text = self.text
+ top, bot = self.getwindowlines()
+ lineno = self.getlineno(mark)
+ height = bot - top
+ newtop = max(1, lineno - height//2)
+ text.yview(float(newtop))
+
+ def getwindowlines(self):
+ text = self.text
+ top = self.getlineno("@0,0")
+ bot = self.getlineno("@0,65535")
+ if top == bot and text.winfo_height() == 1:
+ # Geometry manager hasn't run yet
+ height = int(text['height'])
+ bot = top + height - 1
+ return top, bot
+
+ def getlineno(self, mark="insert"):
+ text = self.text
+ return int(float(text.index(mark)))
+
+ def get_geometry(self):
+ "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
+
+ def close_event(self, event):
+ self.close()
+
+ def maybesave(self):
+ if self.io:
+ if not self.get_saved():
+ if self.top.state()!='normal':
+ self.top.deiconify()
+ self.top.lower()
+ self.top.lift()
+ return self.io.maybesave()
+
+ def close(self):
+ reply = self.maybesave()
+ if str(reply) != "cancel":
+ self._close()
+ return reply
+
+ def _close(self):
+ if self.io.filename:
+ self.update_recent_files_list(new_file=self.io.filename)
+ WindowList.unregister_callback(self.postwindowsmenu)
+ self.unload_extensions()
+ self.io.close()
+ self.io = None
+ self.undo = None
+ if self.color:
+ self.color.close(False)
+ self.color = None
+ self.text = None
+ self.tkinter_vars = None
+ self.per.close()
+ self.per = None
+ self.top.destroy()
+ if self.close_hook:
+ # unless override: unregister from flist, terminate if last window
+ self.close_hook()
+
+ def load_extensions(self):
+ self.extensions = {}
+ self.load_standard_extensions()
+
+ def unload_extensions(self):
+ for ins in self.extensions.values():
+ if hasattr(ins, "close"):
+ ins.close()
+ self.extensions = {}
+
+ def load_standard_extensions(self):
+ for name in self.get_standard_extension_names():
+ try:
+ self.load_extension(name)
+ except:
+ print "Failed to load extension", repr(name)
+ import traceback
+ traceback.print_exc()
+
+ def get_standard_extension_names(self):
+ return idleConf.GetExtensions(editor_only=True)
+
+ def load_extension(self, name):
+ try:
+ mod = __import__(name, globals(), locals(), [])
+ except ImportError:
+ print "\nFailed to import extension: ", name
+ return
+ cls = getattr(mod, name)
+ keydefs = idleConf.GetExtensionBindings(name)
+ if hasattr(cls, "menudefs"):
+ self.fill_menus(cls.menudefs, keydefs)
+ ins = cls(self)
+ self.extensions[name] = ins
+ if keydefs:
+ self.apply_bindings(keydefs)
+ for vevent in keydefs.keys():
+ methodname = vevent.replace("-", "_")
+ while methodname[:1] == '<':
+ methodname = methodname[1:]
+ while methodname[-1:] == '>':
+ methodname = methodname[:-1]
+ methodname = methodname + "_event"
+ if hasattr(ins, methodname):
+ self.text.bind(vevent, getattr(ins, methodname))
+
+ def apply_bindings(self, keydefs=None):
+ if keydefs is None:
+ keydefs = self.Bindings.default_keydefs
+ text = self.text
+ text.keydefs = keydefs
+ for event, keylist in keydefs.items():
+ if keylist:
+ text.event_add(event, *keylist)
+
+ def fill_menus(self, menudefs=None, keydefs=None):
+ """Add appropriate entries to the menus and submenus
+
+ Menus that are absent or None in self.menudict are ignored.
+ """
+ if menudefs is None:
+ menudefs = self.Bindings.menudefs
+ if keydefs is None:
+ keydefs = self.Bindings.default_keydefs
+ menudict = self.menudict
+ text = self.text
+ for mname, entrylist in menudefs:
+ menu = menudict.get(mname)
+ if not menu:
+ continue
+ for entry in entrylist:
+ if not entry:
+ menu.add_separator()
+ else:
+ label, eventname = entry
+ checkbutton = (label[:1] == '!')
+ if checkbutton:
+ label = label[1:]
+ underline, label = prepstr(label)
+ accelerator = get_accelerator(keydefs, eventname)
+ def command(text=text, eventname=eventname):
+ text.event_generate(eventname)
+ if checkbutton:
+ var = self.get_var_obj(eventname, BooleanVar)
+ menu.add_checkbutton(label=label, underline=underline,
+ command=command, accelerator=accelerator,
+ variable=var)
+ else:
+ menu.add_command(label=label, underline=underline,
+ command=command,
+ accelerator=accelerator)
+
+ def getvar(self, name):
+ var = self.get_var_obj(name)
+ if var:
+ value = var.get()
+ return value
+ else:
+ 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
+
+ def get_var_obj(self, name, vartype=None):
+ 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)
+ return var
+
+ # Tk implementations of "virtual text methods" -- each platform
+ # reusing IDLE's support code needs to define these for its GUI's
+ # flavor of widget.
+
+ # Is character at text_index in a Python string? Return 0 for
+ # "guaranteed no", true for anything else. This info is expensive
+ # to compute ab initio, but is probably already known by the
+ # platform's colorizer.
+
+ def is_char_in_string(self, text_index):
+ if self.color:
+ # Return true iff colorizer hasn't (re)gotten this far
+ # yet, or the character is tagged as being in a string
+ return self.text.tag_prevrange("TODO", text_index) or \
+ "STRING" in self.text.tag_names(text_index)
+ else:
+ # The colorizer is missing: assume the worst
+ return 1
+
+ # If a selection is defined in the text widget, return (start,
+ # end) as Tkinter text indices, otherwise return (None, None)
+ def get_selection_indices(self):
+ try:
+ first = self.text.index("sel.first")
+ last = self.text.index("sel.last")
+ return first, last
+ except TclError:
+ return None, None
+
+ # Return the text widget's current view of what a tab stop means
+ # (equivalent width in spaces).
+
+ def get_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):
+ text = self.text
+ if self.get_tabwidth() != newtabwidth:
+ 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.
+
+ def set_indentation_params(self, ispythonsource, guess=True):
+ if guess and ispythonsource:
+ i = self.guess_indent()
+ if 2 <= i <= 8:
+ self.indentwidth = i
+ if self.indentwidth != self.tabwidth:
+ self.usetabs = False
+ self.set_tabwidth(self.tabwidth)
+
+ def smart_backspace_event(self, event):
+ text = self.text
+ first, last = self.get_selection_indices()
+ if first and last:
+ text.delete(first, last)
+ text.mark_set("insert", first)
+ return "break"
+ # Delete whitespace left, until hitting a real char or closest
+ # preceding virtual tab stop.
+ chars = text.get("insert linestart", "insert")
+ if chars == '':
+ if text.compare("insert", ">", "1.0"):
+ # easy: delete preceding newline
+ text.delete("insert-1c")
+ else:
+ text.bell() # at start of buffer
+ return "break"
+ if chars[-1] not in " \t":
+ # easy: delete preceding real char
+ text.delete("insert-1c")
+ return "break"
+ # Ick. It may require *inserting* spaces if we back up over a
+ # tab character! This is written to be clear, not fast.
+ tabwidth = self.tabwidth
+ have = len(chars.expandtabs(tabwidth))
+ assert have > 0
+ want = ((have - 1) // self.indentwidth) * self.indentwidth
+ # Debug prompt is multilined....
+ if self.context_use_ps1:
+ last_line_of_prompt = sys.ps1.split('\n')[-1]
+ else:
+ last_line_of_prompt = ''
+ ncharsdeleted = 0
+ while 1:
+ if chars == last_line_of_prompt:
+ break
+ chars = chars[:-1]
+ ncharsdeleted = ncharsdeleted + 1
+ have = len(chars.expandtabs(tabwidth))
+ if have <= want or chars[-1] not in " \t":
+ break
+ text.undo_block_start()
+ text.delete("insert-%dc" % ncharsdeleted, "insert")
+ if have < want:
+ text.insert("insert", ' ' * (want - have))
+ text.undo_block_stop()
+ return "break"
+
+ def smart_indent_event(self, event):
+ # if intraline selection:
+ # delete it
+ # elif multiline selection:
+ # do indent-region
+ # else:
+ # indent one level
+ text = self.text
+ first, last = self.get_selection_indices()
+ text.undo_block_start()
+ try:
+ if first and last:
+ if index2line(first) != index2line(last):
+ return self.indent_region_event(event)
+ text.delete(first, last)
+ text.mark_set("insert", first)
+ prefix = text.get("insert linestart", "insert")
+ raw, effective = classifyws(prefix, self.tabwidth)
+ if raw == len(prefix):
+ # only whitespace to the left
+ self.reindent_to(effective + self.indentwidth)
+ else:
+ # tab to the next 'stop' within or to right of line's text:
+ if self.usetabs:
+ pad = '\t'
+ else:
+ effective = len(prefix.expandtabs(self.tabwidth))
+ n = self.indentwidth
+ pad = ' ' * (n - effective % n)
+ text.insert("insert", pad)
+ text.see("insert")
+ return "break"
+ finally:
+ text.undo_block_stop()
+
+ def newline_and_indent_event(self, event):
+ text = self.text
+ first, last = self.get_selection_indices()
+ text.undo_block_start()
+ try:
+ if first and last:
+ text.delete(first, last)
+ text.mark_set("insert", first)
+ line = text.get("insert linestart", "insert")
+ i, n = 0, len(line)
+ while i < n and line[i] in " \t":
+ i = i+1
+ if i == n:
+ # the cursor is in or at leading indentation in a continuation
+ # line; just inject an empty line at the start
+ text.insert("insert linestart", '\n')
+ return "break"
+ indent = line[:i]
+ # strip whitespace before insert point unless it's in the prompt
+ i = 0
+ last_line_of_prompt = sys.ps1.split('\n')[-1]
+ while line and line[-1] in " \t" and line != last_line_of_prompt:
+ line = line[:-1]
+ i = i+1
+ if i:
+ text.delete("insert - %d chars" % i, "insert")
+ # strip whitespace after insert point
+ while text.get("insert") in " \t":
+ text.delete("insert")
+ # start new line
+ text.insert("insert", '\n')
+
+ # adjust indentation for continuations and block
+ # open/close first need to find the last stmt
+ lno = index2line(text.index('insert'))
+ y = PyParse.Parser(self.indentwidth, self.tabwidth)
+ if not self.context_use_ps1:
+ for context in self.num_context_lines:
+ startat = max(lno - context, 1)
+ startatindex = repr(startat) + ".0"
+ rawtext = text.get(startatindex, "insert")
+ y.set_str(rawtext)
+ bod = y.find_good_parse_start(
+ self.context_use_ps1,
+ self._build_char_in_string_func(startatindex))
+ if bod is not None or startat == 1:
+ break
+ y.set_lo(bod or 0)
+ else:
+ r = text.tag_prevrange("console", "insert")
+ if r:
+ startatindex = r[1]
+ else:
+ startatindex = "1.0"
+ rawtext = text.get(startatindex, "insert")
+ y.set_str(rawtext)
+ y.set_lo(0)
+
+ c = y.get_continuation_type()
+ if c != PyParse.C_NONE:
+ # The current stmt hasn't ended yet.
+ if c == PyParse.C_STRING_FIRST_LINE:
+ # after the first line of a string; do not indent at all
+ pass
+ elif c == PyParse.C_STRING_NEXT_LINES:
+ # inside a string which started before this line;
+ # just mimic the current indent
+ text.insert("insert", indent)
+ elif c == PyParse.C_BRACKET:
+ # line up with the first (if any) element of the
+ # last open bracket structure; else indent one
+ # level beyond the indent of the line with the
+ # last open bracket
+ self.reindent_to(y.compute_bracket_indent())
+ elif c == PyParse.C_BACKSLASH:
+ # if more than one line in this stmt already, just
+ # mimic the current indent; else if initial line
+ # has a start on an assignment stmt, indent to
+ # beyond leftmost =; else to beyond first chunk of
+ # non-whitespace on initial line
+ if y.get_num_lines_in_stmt() > 1:
+ text.insert("insert", indent)
+ else:
+ self.reindent_to(y.compute_backslash_indent())
+ else:
+ assert 0, "bogus continuation type %r" % (c,)
+ return "break"
+
+ # This line starts a brand new stmt; indent relative to
+ # indentation of initial line of closest preceding
+ # interesting stmt.
+ indent = y.get_base_indent_string()
+ text.insert("insert", indent)
+ if y.is_block_opener():
+ self.smart_indent_event(event)
+ elif indent and y.is_block_closer():
+ self.smart_backspace_event(event)
+ return "break"
+ finally:
+ text.see("insert")
+ text.undo_block_stop()
+
+ # Our editwin provides an is_char_in_string function that works
+ # with a Tk text index, but PyParse only knows about offsets into
+ # a string. This builds a function for PyParse that accepts an
+ # offset.
+
+ def _build_char_in_string_func(self, startindex):
+ def inner(offset, _startindex=startindex,
+ _icis=self.is_char_in_string):
+ return _icis(_startindex + "+%dc" % offset)
+ return inner
+
+ def indent_region_event(self, event):
+ head, tail, chars, lines = self.get_region()
+ for pos in range(len(lines)):
+ line = lines[pos]
+ if line:
+ raw, effective = classifyws(line, self.tabwidth)
+ effective = effective + self.indentwidth
+ lines[pos] = self._make_blanks(effective) + line[raw:]
+ self.set_region(head, tail, chars, lines)
+ return "break"
+
+ def dedent_region_event(self, event):
+ head, tail, chars, lines = self.get_region()
+ for pos in range(len(lines)):
+ line = lines[pos]
+ if line:
+ raw, effective = classifyws(line, self.tabwidth)
+ effective = max(effective - self.indentwidth, 0)
+ lines[pos] = self._make_blanks(effective) + line[raw:]
+ self.set_region(head, tail, chars, lines)
+ return "break"
+
+ def comment_region_event(self, event):
+ head, tail, chars, lines = self.get_region()
+ for pos in range(len(lines) - 1):
+ line = lines[pos]
+ lines[pos] = '##' + line
+ self.set_region(head, tail, chars, lines)
+
+ def uncomment_region_event(self, event):
+ head, tail, chars, lines = self.get_region()
+ for pos in range(len(lines)):
+ line = lines[pos]
+ if not line:
+ continue
+ if line[:2] == '##':
+ line = line[2:]
+ elif line[:1] == '#':
+ line = line[1:]
+ lines[pos] = line
+ self.set_region(head, tail, chars, lines)
+
+ def tabify_region_event(self, event):
+ head, tail, chars, lines = self.get_region()
+ tabwidth = self._asktabwidth()
+ if tabwidth is None: return
+ for pos in range(len(lines)):
+ line = lines[pos]
+ if line:
+ raw, effective = classifyws(line, tabwidth)
+ ntabs, nspaces = divmod(effective, tabwidth)
+ lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:]
+ self.set_region(head, tail, chars, lines)
+
+ def untabify_region_event(self, event):
+ head, tail, chars, lines = self.get_region()
+ tabwidth = self._asktabwidth()
+ if tabwidth is None: return
+ for pos in range(len(lines)):
+ lines[pos] = lines[pos].expandtabs(tabwidth)
+ self.set_region(head, tail, chars, lines)
+
+ def toggle_tabs_event(self, event):
+ if self.askyesno(
+ "Toggle tabs",
+ "Turn tabs " + ("on", "off")[self.usetabs] +
+ "?\nIndent width " +
+ ("will be", "remains at")[self.usetabs] + " 8." +
+ "\n Note: a tab is always 8 columns",
+ parent=self.text):
+ self.usetabs = not self.usetabs
+ # Try to prevent inconsistent indentation.
+ # User must change indent width manually after using tabs.
+ self.indentwidth = 8
+ return "break"
+
+ # XXX this isn't bound to anything -- see tabwidth comments
+## def change_tabwidth_event(self, event):
+## new = self._asktabwidth()
+## if new != self.tabwidth:
+## self.tabwidth = new
+## self.set_indentation_params(0, guess=0)
+## return "break"
+
+ def change_indentwidth_event(self, event):
+ new = self.askinteger(
+ "Indent width",
+ "New indent width (2-16)\n(Always use 8 when using tabs)",
+ parent=self.text,
+ initialvalue=self.indentwidth,
+ minvalue=2,
+ maxvalue=16)
+ if new and new != self.indentwidth and not self.usetabs:
+ self.indentwidth = new
+ return "break"
+
+ def get_region(self):
+ text = self.text
+ first, last = self.get_selection_indices()
+ if first and last:
+ head = text.index(first + " linestart")
+ tail = text.index(last + "-1c lineend +1c")
+ else:
+ head = text.index("insert linestart")
+ tail = text.index("insert lineend +1c")
+ chars = text.get(head, tail)
+ lines = chars.split("\n")
+ return head, tail, chars, lines
+
+ def set_region(self, head, tail, chars, lines):
+ text = self.text
+ newchars = "\n".join(lines)
+ if newchars == chars:
+ text.bell()
+ return
+ text.tag_remove("sel", "1.0", "end")
+ text.mark_set("insert", head)
+ text.undo_block_start()
+ text.delete(head, tail)
+ text.insert(head, newchars)
+ text.undo_block_stop()
+ text.tag_add("sel", head, "insert")
+
+ # Make string that displays as n leading blanks.
+
+ def _make_blanks(self, n):
+ if self.usetabs:
+ ntabs, nspaces = divmod(n, self.tabwidth)
+ return '\t' * ntabs + ' ' * nspaces
+ else:
+ return ' ' * n
+
+ # Delete from beginning of line to insert point, then reinsert
+ # column logical (meaning use tabs if appropriate) spaces.
+
+ def reindent_to(self, column):
+ text = self.text
+ text.undo_block_start()
+ if text.compare("insert linestart", "!=", "insert"):
+ text.delete("insert linestart", "insert")
+ if column:
+ text.insert("insert", self._make_blanks(column))
+ text.undo_block_stop()
+
+ def _asktabwidth(self):
+ return self.askinteger(
+ "Tab width",
+ "Columns per tab? (2-16)",
+ parent=self.text,
+ initialvalue=self.indentwidth,
+ minvalue=2,
+ maxvalue=16)
+
+ # Guess indentwidth from text content.
+ # Return guessed indentwidth. This should not be believed unless
+ # it's in a reasonable range (e.g., it will be 0 if no indented
+ # blocks are found).
+
+ def guess_indent(self):
+ opener, indented = IndentSearcher(self.text, self.tabwidth).run()
+ if opener and indented:
+ raw, indentsmall = classifyws(opener, self.tabwidth)
+ raw, indentlarge = classifyws(indented, self.tabwidth)
+ else:
+ indentsmall = indentlarge = 0
+ return indentlarge - indentsmall
+
+# "line.col" -> line, as an int
+def index2line(index):
+ return int(float(index))
+
+# Look at the leading whitespace in s.
+# Return pair (# of leading ws characters,
+# effective # of leading blanks after expanding
+# tabs to width tabwidth)
+
+def classifyws(s, tabwidth):
+ raw = effective = 0
+ for ch in s:
+ if ch == ' ':
+ raw = raw + 1
+ effective = effective + 1
+ elif ch == '\t':
+ raw = raw + 1
+ effective = (effective // tabwidth + 1) * tabwidth
+ else:
+ break
+ return raw, effective
+
+import tokenize
+_tokenize = tokenize
+del tokenize
+
+class IndentSearcher(object):
+
+ # .run() chews over the Text widget, looking for a block opener
+ # and the stmt following it. Returns a pair,
+ # (line containing block opener, line containing stmt)
+ # Either or both may be None.
+
+ def __init__(self, text, tabwidth):
+ self.text = text
+ self.tabwidth = tabwidth
+ self.i = self.finished = 0
+ self.blkopenline = self.indentedline = None
+
+ def readline(self):
+ if self.finished:
+ return ""
+ i = self.i = self.i + 1
+ mark = repr(i) + ".0"
+ if self.text.compare(mark, ">=", "end"):
+ return ""
+ return self.text.get(mark, mark + " lineend+1c")
+
+ def tokeneater(self, type, token, start, end, line,
+ INDENT=_tokenize.INDENT,
+ NAME=_tokenize.NAME,
+ OPENERS=('class', 'def', 'for', 'if', 'try', 'while')):
+ if self.finished:
+ pass
+ elif type == NAME and token in OPENERS:
+ self.blkopenline = line
+ elif type == INDENT and self.blkopenline:
+ self.indentedline = line
+ self.finished = 1
+
+ def run(self):
+ save_tabsize = _tokenize.tabsize
+ _tokenize.tabsize = self.tabwidth
+ try:
+ try:
+ _tokenize.tokenize(self.readline, self.tokeneater)
+ except (_tokenize.TokenError, SyntaxError):
+ # since we cut off the tokenizer early, we can trigger
+ # spurious errors
+ pass
+ finally:
+ _tokenize.tabsize = save_tabsize
+ return self.blkopenline, self.indentedline
+
+### end autoindent code ###
+
+def prepstr(s):
+ # Helper to extract the underscore from a string, e.g.
+ # prepstr("Co_py") returns (2, "Copy").
+ i = s.find('_')
+ if i >= 0:
+ s = s[:i] + s[i+1:]
+ return i, s
+
+
+keynames = {
+ 'bracketleft': '[',
+ 'bracketright': ']',
+ 'slash': '/',
+}
+
+def get_accelerator(keydefs, eventname):
+ keylist = keydefs.get(eventname)
+ # issue10940: temporary workaround to prevent hang with OS X Cocoa Tk 8.5
+ # if not keylist:
+ if (not keylist) or (macosxSupport.isCocoaTk() and eventname in {
+ "<<open-module>>",
+ "<<goto-line>>",
+ "<<change-indentwidth>>"}):
+ return ""
+ s = keylist[0]
+ s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s)
+ s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
+ s = re.sub("Key-", "", s)
+ s = re.sub("Cancel","Ctrl-Break",s) # dscherer@cmu.edu
+ s = re.sub("Control-", "Ctrl-", s)
+ s = re.sub("-", "+", s)
+ s = re.sub("><", " ", s)
+ s = re.sub("<", "", s)
+ s = re.sub(">", "", s)
+ return s
+
+
+def fixwordbreaks(root):
+ # Make sure that Tk's double-click and next/previous word
+ # operations use our definition of a word (i.e. an identifier)
+ tk = root.tk
+ tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
+ tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
+ tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
+
+
+def _editor_window(parent): # htest #
+ # error if close master window first - timer event, after script
+ root = parent
+ fixwordbreaks(root)
+ if sys.argv[1:]:
+ filename = sys.argv[1]
+ else:
+ filename = None
+ macosxSupport.setupApp(root, None)
+ edit = EditorWindow(root=root, filename=filename)
+ edit.text.bind("<<close-all-windows>>", edit.close_event)
+ # Does not stop error, neither does following
+ # edit.text.bind("<<close-window>>", edit.close_event)
+
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_editor_window)
diff --git a/lib/python2.7/idlelib/FileList.py b/lib/python2.7/idlelib/FileList.py
new file mode 100644
index 0000000..8318ff1
--- /dev/null
+++ b/lib/python2.7/idlelib/FileList.py
@@ -0,0 +1,124 @@
+import os
+from Tkinter import *
+import tkMessageBox
+
+
+class FileList:
+
+ # N.B. this import overridden in PyShellFileList.
+ from idlelib.EditorWindow import EditorWindow
+
+ def __init__(self, root):
+ self.root = root
+ self.dict = {}
+ self.inversedict = {}
+ self.vars = {} # For EditorWindow.getrawvar (shared Tcl variables)
+
+ def open(self, filename, action=None):
+ assert filename
+ filename = self.canonize(filename)
+ if os.path.isdir(filename):
+ # This can happen when bad filename is passed on command line:
+ tkMessageBox.showerror(
+ "File Error",
+ "%r is a directory." % (filename,),
+ master=self.root)
+ return None
+ key = os.path.normcase(filename)
+ if key in self.dict:
+ edit = self.dict[key]
+ edit.top.wakeup()
+ return edit
+ if action:
+ # Don't create window, perform 'action', e.g. open in same window
+ return action(filename)
+ else:
+ return self.EditorWindow(self, filename, key)
+
+ def gotofileline(self, filename, lineno=None):
+ edit = self.open(filename)
+ if edit is not None and lineno is not None:
+ edit.gotoline(lineno)
+
+ def new(self, filename=None):
+ return self.EditorWindow(self, filename)
+
+ def close_all_callback(self, *args, **kwds):
+ for edit in self.inversedict.keys():
+ reply = edit.close()
+ if reply == "cancel":
+ break
+ return "break"
+
+ def unregister_maybe_terminate(self, edit):
+ try:
+ key = self.inversedict[edit]
+ except KeyError:
+ print "Don't know this EditorWindow object. (close)"
+ return
+ if key:
+ del self.dict[key]
+ del self.inversedict[edit]
+ if not self.inversedict:
+ self.root.quit()
+
+ def filename_changed_edit(self, edit):
+ edit.saved_change_hook()
+ try:
+ key = self.inversedict[edit]
+ except KeyError:
+ print "Don't know this EditorWindow object. (rename)"
+ return
+ filename = edit.io.filename
+ if not filename:
+ if key:
+ del self.dict[key]
+ self.inversedict[edit] = None
+ return
+ filename = self.canonize(filename)
+ newkey = os.path.normcase(filename)
+ if newkey == key:
+ return
+ if newkey in self.dict:
+ conflict = self.dict[newkey]
+ self.inversedict[conflict] = None
+ tkMessageBox.showerror(
+ "Name Conflict",
+ "You now have multiple edit windows open for %r" % (filename,),
+ master=self.root)
+ self.dict[newkey] = edit
+ self.inversedict[edit] = newkey
+ if key:
+ try:
+ del self.dict[key]
+ except KeyError:
+ pass
+
+ def canonize(self, filename):
+ if not os.path.isabs(filename):
+ try:
+ pwd = os.getcwd()
+ except os.error:
+ pass
+ else:
+ filename = os.path.join(pwd, filename)
+ return os.path.normpath(filename)
+
+
+def _test():
+ from idlelib.EditorWindow import fixwordbreaks
+ import sys
+ root = Tk()
+ fixwordbreaks(root)
+ root.withdraw()
+ flist = FileList(root)
+ if sys.argv[1:]:
+ for filename in sys.argv[1:]:
+ flist.open(filename)
+ else:
+ flist.new()
+ if flist.inversedict:
+ root.mainloop()
+
+if __name__ == '__main__':
+ _test()
diff --git a/lib/python2.7/idlelib/FormatParagraph.py b/lib/python2.7/idlelib/FormatParagraph.py
new file mode 100644
index 0000000..7a9d185
--- /dev/null
+++ b/lib/python2.7/idlelib/FormatParagraph.py
@@ -0,0 +1,195 @@
+"""Extension to format a paragraph or selection to a max width.
+
+Does basic, standard text formatting, and also understands Python
+comment blocks. Thus, for editing Python source code, this
+extension is really only suitable for reformatting these comment
+blocks or triple-quoted strings.
+
+Known problems with comment reformatting:
+* If there is a selection marked, and the first line of the
+ selection is not complete, the block will probably not be detected
+ as comments, and will have the normal "text formatting" rules
+ applied.
+* If a comment block has leading whitespace that mixes tabs and
+ spaces, they will not be considered part of the same block.
+* Fancy comments, like this bulleted list, aren't handled :-)
+"""
+
+import re
+from idlelib.configHandler import idleConf
+
+class FormatParagraph:
+
+ menudefs = [
+ ('format', [ # /s/edit/format dscherer@cmu.edu
+ ('Format Paragraph', '<<format-paragraph>>'),
+ ])
+ ]
+
+ def __init__(self, editwin):
+ self.editwin = editwin
+
+ def close(self):
+ self.editwin = None
+
+ def format_paragraph_event(self, event, limit=None):
+ """Formats paragraph to a max width specified in idleConf.
+
+ If text is selected, format_paragraph_event will start breaking lines
+ at the max width, starting from the beginning selection.
+
+ If no text is selected, format_paragraph_event uses the current
+ cursor location to determine the paragraph (lines of text surrounded
+ by blank lines) and formats it.
+
+ The length limit parameter is for testing with a known value.
+ """
+ if limit is None:
+ # The default length limit is that defined by pep8
+ limit = idleConf.GetOption(
+ 'extensions', 'FormatParagraph', 'max-width',
+ type='int', default=72)
+ text = self.editwin.text
+ first, last = self.editwin.get_selection_indices()
+ if first and last:
+ data = text.get(first, last)
+ comment_header = get_comment_header(data)
+ else:
+ first, last, comment_header, data = \
+ find_paragraph(text, text.index("insert"))
+ if comment_header:
+ newdata = reformat_comment(data, limit, comment_header)
+ else:
+ newdata = reformat_paragraph(data, limit)
+ text.tag_remove("sel", "1.0", "end")
+
+ if newdata != data:
+ text.mark_set("insert", first)
+ text.undo_block_start()
+ text.delete(first, last)
+ text.insert(first, newdata)
+ text.undo_block_stop()
+ else:
+ text.mark_set("insert", last)
+ text.see("insert")
+ return "break"
+
+def find_paragraph(text, mark):
+ """Returns the start/stop indices enclosing the paragraph that mark is in.
+
+ Also returns the comment format string, if any, and paragraph of text
+ between the start/stop indices.
+ """
+ lineno, col = map(int, mark.split("."))
+ line = text.get("%d.0" % lineno, "%d.end" % lineno)
+
+ # Look for start of next paragraph if the index passed in is a blank line
+ while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line):
+ lineno = lineno + 1
+ line = text.get("%d.0" % lineno, "%d.end" % lineno)
+ first_lineno = lineno
+ comment_header = get_comment_header(line)
+ comment_header_len = len(comment_header)
+
+ # Once start line found, search for end of paragraph (a blank line)
+ while get_comment_header(line)==comment_header and \
+ not is_all_white(line[comment_header_len:]):
+ lineno = lineno + 1
+ line = text.get("%d.0" % lineno, "%d.end" % lineno)
+ last = "%d.0" % lineno
+
+ # Search back to beginning of paragraph (first blank line before)
+ lineno = first_lineno - 1
+ line = text.get("%d.0" % lineno, "%d.end" % lineno)
+ while lineno > 0 and \
+ get_comment_header(line)==comment_header and \
+ not is_all_white(line[comment_header_len:]):
+ lineno = lineno - 1
+ line = text.get("%d.0" % lineno, "%d.end" % lineno)
+ first = "%d.0" % (lineno+1)
+
+ return first, last, comment_header, text.get(first, last)
+
+# This should perhaps be replaced with textwrap.wrap
+def reformat_paragraph(data, limit):
+ """Return data reformatted to specified width (limit)."""
+ lines = data.split("\n")
+ i = 0
+ n = len(lines)
+ while i < n and is_all_white(lines[i]):
+ i = i+1
+ if i >= n:
+ return data
+ indent1 = get_indent(lines[i])
+ if i+1 < n and not is_all_white(lines[i+1]):
+ indent2 = get_indent(lines[i+1])
+ else:
+ indent2 = indent1
+ new = lines[:i]
+ partial = indent1
+ while i < n and not is_all_white(lines[i]):
+ # XXX Should take double space after period (etc.) into account
+ words = re.split("(\s+)", lines[i])
+ for j in range(0, len(words), 2):
+ word = words[j]
+ if not word:
+ continue # Can happen when line ends in whitespace
+ if len((partial + word).expandtabs()) > limit and \
+ partial != indent1:
+ new.append(partial.rstrip())
+ partial = indent2
+ partial = partial + word + " "
+ if j+1 < len(words) and words[j+1] != " ":
+ partial = partial + " "
+ i = i+1
+ new.append(partial.rstrip())
+ # XXX Should reformat remaining paragraphs as well
+ new.extend(lines[i:])
+ return "\n".join(new)
+
+def reformat_comment(data, limit, comment_header):
+ """Return data reformatted to specified width with comment header."""
+
+ # Remove header from the comment lines
+ lc = len(comment_header)
+ data = "\n".join(line[lc:] for line in data.split("\n"))
+ # Reformat to maxformatwidth chars or a 20 char width,
+ # whichever is greater.
+ format_width = max(limit - len(comment_header), 20)
+ newdata = reformat_paragraph(data, format_width)
+ # re-split and re-insert the comment header.
+ newdata = newdata.split("\n")
+ # If the block ends in a \n, we dont want the comment prefix
+ # inserted after it. (Im not sure it makes sense to reformat a
+ # comment block that is not made of complete lines, but whatever!)
+ # Can't think of a clean solution, so we hack away
+ block_suffix = ""
+ if not newdata[-1]:
+ block_suffix = "\n"
+ newdata = newdata[:-1]
+ return '\n'.join(comment_header+line for line in newdata) + block_suffix
+
+def is_all_white(line):
+ """Return True if line is empty or all whitespace."""
+
+ return re.match(r"^\s*$", line) is not None
+
+def get_indent(line):
+ """Return the initial space or tab indent of line."""
+ return re.match(r"^([ \t]*)", line).group()
+
+def get_comment_header(line):
+ """Return string with leading whitespace and '#' from line or ''.
+
+ A null return indicates that the line is not a comment line. A non-
+ null return, such as ' #', will be used to find the other lines of
+ a comment block with the same indent.
+ """
+ m = re.match(r"^([ \t]*#*)", line)
+ if m is None: return ""
+ return m.group(1)
+
+if __name__ == "__main__":
+ import unittest
+ unittest.main('idlelib.idle_test.test_formatparagraph',
+ verbosity=2, exit=False)
diff --git a/lib/python2.7/idlelib/GrepDialog.py b/lib/python2.7/idlelib/GrepDialog.py
new file mode 100644
index 0000000..d86d50d
--- /dev/null
+++ b/lib/python2.7/idlelib/GrepDialog.py
@@ -0,0 +1,159 @@
+from __future__ import print_function
+import os
+import fnmatch
+import re # for htest
+import sys
+from Tkinter import StringVar, BooleanVar, Checkbutton # for GrepDialog
+from Tkinter import Tk, Text, Button, SEL, END # for htest
+from idlelib import SearchEngine
+from idlelib.SearchDialogBase import SearchDialogBase
+# Importing OutputWindow fails due to import loop
+# EditorWindow -> GrepDialop -> OutputWindow -> EditorWindow
+
+def grep(text, io=None, flist=None):
+ root = text._root()
+ engine = SearchEngine.get(root)
+ if not hasattr(engine, "_grepdialog"):
+ engine._grepdialog = GrepDialog(root, engine, flist)
+ dialog = engine._grepdialog
+ searchphrase = text.get("sel.first", "sel.last")
+ dialog.open(text, searchphrase, io)
+
+class GrepDialog(SearchDialogBase):
+
+ title = "Find in Files Dialog"
+ icon = "Grep"
+ needwrapbutton = 0
+
+ def __init__(self, root, engine, flist):
+ SearchDialogBase.__init__(self, root, engine)
+ self.flist = flist
+ self.globvar = StringVar(root)
+ self.recvar = BooleanVar(root)
+
+ def open(self, text, searchphrase, io=None):
+ SearchDialogBase.open(self, text, searchphrase)
+ if io:
+ path = io.filename or ""
+ else:
+ path = ""
+ dir, base = os.path.split(path)
+ head, tail = os.path.splitext(base)
+ if not tail:
+ tail = ".py"
+ self.globvar.set(os.path.join(dir, "*" + tail))
+
+ def create_entries(self):
+ SearchDialogBase.create_entries(self)
+ self.globent = self.make_entry("In files:", self.globvar)[0]
+
+ def create_other_buttons(self):
+ f = self.make_frame()[0]
+
+ btn = Checkbutton(f, anchor="w",
+ variable=self.recvar,
+ text="Recurse down subdirectories")
+ btn.pack(side="top", fill="both")
+ btn.select()
+
+ def create_command_buttons(self):
+ SearchDialogBase.create_command_buttons(self)
+ self.make_button("Search Files", self.default_command, 1)
+
+ def default_command(self, event=None):
+ prog = self.engine.getprog()
+ if not prog:
+ return
+ path = self.globvar.get()
+ if not path:
+ self.top.bell()
+ return
+ from idlelib.OutputWindow import OutputWindow # leave here!
+ save = sys.stdout
+ try:
+ sys.stdout = OutputWindow(self.flist)
+ self.grep_it(prog, path)
+ finally:
+ sys.stdout = save
+
+ def grep_it(self, prog, path):
+ dir, base = os.path.split(path)
+ list = self.findfiles(dir, base, self.recvar.get())
+ list.sort()
+ self.close()
+ pat = self.engine.getpat()
+ print("Searching %r in %s ..." % (pat, path))
+ hits = 0
+ try:
+ for fn in list:
+ try:
+ with open(fn) as f:
+ for lineno, line in enumerate(f, 1):
+ if line[-1:] == '\n':
+ line = line[:-1]
+ if prog.search(line):
+ sys.stdout.write("%s: %s: %s\n" %
+ (fn, lineno, line))
+ hits += 1
+ except IOError as msg:
+ print(msg)
+ print(("Hits found: %s\n"
+ "(Hint: right-click to open locations.)"
+ % hits) if hits else "No hits.")
+ except AttributeError:
+ # Tk window has been closed, OutputWindow.text = None,
+ # so in OW.write, OW.text.insert fails.
+ pass
+
+ def findfiles(self, dir, base, rec):
+ try:
+ names = os.listdir(dir or os.curdir)
+ except os.error as msg:
+ print(msg)
+ return []
+ list = []
+ subdirs = []
+ for name in names:
+ fn = os.path.join(dir, name)
+ if os.path.isdir(fn):
+ subdirs.append(fn)
+ else:
+ if fnmatch.fnmatch(name, base):
+ list.append(fn)
+ if rec:
+ for subdir in subdirs:
+ list.extend(self.findfiles(subdir, base, rec))
+ return list
+
+ def close(self, event=None):
+ if self.top:
+ self.top.grab_release()
+ self.top.withdraw()
+
+
+def _grep_dialog(parent): # htest #
+ from idlelib.PyShell import PyShellFileList
+ root = Tk()
+ root.title("Test GrepDialog")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+
+ flist = PyShellFileList(root)
+ text = Text(root, height=5)
+ text.pack()
+
+ def show_grep_dialog():
+ text.tag_add(SEL, "1.0", END)
+ grep(text, flist=flist)
+ text.tag_remove(SEL, "1.0", END)
+
+ button = Button(root, text="Show GrepDialog", command=show_grep_dialog)
+ button.pack()
+ root.mainloop()
+
+if __name__ == "__main__":
+ import unittest
+ unittest.main('idlelib.idle_test.test_grep', verbosity=2, exit=False)
+
+ from idlelib.idle_test.htest import run
+ run(_grep_dialog)
diff --git a/lib/python2.7/idlelib/HISTORY.txt b/lib/python2.7/idlelib/HISTORY.txt
new file mode 100644
index 0000000..01d73ed
--- /dev/null
+++ b/lib/python2.7/idlelib/HISTORY.txt
@@ -0,0 +1,296 @@
+IDLE History
+============
+
+This file contains the release messages for previous IDLE releases.
+As you read on you go back to the dark ages of IDLE's history.
+
+
+What's New in IDLEfork 0.8.1?
+=============================
+
+*Release date: 22-Jul-2001*
+
+- New tarball released as a result of the 'revitalisation' of the IDLEfork
+ project.
+
+- This release requires python 2.1 or better. Compatibility with earlier
+ versions of python (especially ancient ones like 1.5x) is no longer a
+ priority in IDLEfork development.
+
+- This release is based on a merging of the earlier IDLE fork work with current
+ cvs IDLE (post IDLE version 0.8), with some minor additional coding by Kurt
+ B. Kaiser and Stephen M. Gava.
+
+- This release is basically functional but also contains some known breakages,
+ particularly with running things from the shell window. Also the debugger is
+ not working, but I believe this was the case with the previous IDLE fork
+ release (0.7.1) as well.
+
+- This release is being made now to mark the point at which IDLEfork is
+ launching into a new stage of development.
+
+- IDLEfork CVS will now be branched to enable further development and
+ exploration of the two "execution in a remote process" patches submitted by
+ David Scherer (David's is currently in IDLEfork) and GvR, while stabilisation
+ and development of less heavyweight improvements (like user customisation)
+ can continue on the trunk.
+
+
+What's New in IDLEfork 0.7.1?
+==============================
+
+*Release date: 15-Aug-2000*
+
+- First project tarball released.
+
+- This was the first release of IDLE fork, which at this stage was a
+ combination of IDLE 0.5 and the VPython idle fork, with additional changes
+ coded by David Scherer, Peter Schneider-Kamp and Nicholas Riley.
+
+
+
+IDLEfork 0.7.1 - 29 May 2000
+-----------------------------
+
+ David Scherer <dscherer@cmu.edu>
+
+- This is a modification of the CVS version of IDLE 0.5, updated as of
+ 2000-03-09. It is alpha software and might be unstable. If it breaks, you
+ get to keep both pieces.
+
+- If you have problems or suggestions, you should either contact me or post to
+ the list at http://www.python.org/mailman/listinfo/idle-dev (making it clear
+ that you are using this modified version of IDLE).
+
+- Changes:
+
+ - The ExecBinding module, a replacement for ScriptBinding, executes programs
+ in a separate process, piping standard I/O through an RPC mechanism to an
+ OnDemandOutputWindow in IDLE. It supports executing unnamed programs
+ (through a temporary file). It does not yet support debugging.
+
+ - When running programs with ExecBinding, tracebacks will be clipped to
+ exclude system modules. If, however, a system module calls back into the
+ user program, that part of the traceback will be shown.
+
+ - The OnDemandOutputWindow class has been improved. In particular, it now
+ supports a readline() function used to implement user input, and a
+ scroll_clear() operation which is used to hide the output of a previous run
+ by scrolling it out of the window.
+
+ - Startup behavior has been changed. By default IDLE starts up with just a
+ blank editor window, rather than an interactive window. Opening a file in
+ such a blank window replaces the (nonexistent) contents of that window
+ instead of creating another window. Because of the need to have a
+ well-known port for the ExecBinding protocol, only one copy of IDLE can be
+ running. Additional invocations use the RPC mechanism to report their
+ command line arguments to the copy already running.
+
+ - The menus have been reorganized. In particular, the excessively large
+ 'edit' menu has been split up into 'edit', 'format', and 'run'.
+
+ - 'Python Documentation' now works on Windows, if the win32api module is
+ present.
+
+ - A few key bindings have been changed: F1 now loads Python Documentation
+ instead of the IDLE help; shift-TAB is now a synonym for unindent.
+
+- New modules:
+
+ ExecBinding.py Executes program through loader
+ loader.py Bootstraps user program
+ protocol.py RPC protocol
+ Remote.py User-process interpreter
+ spawn.py OS-specific code to start programs
+
+- Files modified:
+
+ autoindent.py ( bindings tweaked )
+ bindings.py ( menus reorganized )
+ config.txt ( execbinding enabled )
+ editorwindow.py ( new menus, fixed 'Python Documentation' )
+ filelist.py ( hook for "open in same window" )
+ formatparagraph.py ( bindings tweaked )
+ idle.bat ( removed absolute pathname )
+ idle.pyw ( weird bug due to import with same name? )
+ iobinding.py ( open in same window, EOL convention )
+ keydefs.py ( bindings tweaked )
+ outputwindow.py ( readline, scroll_clear, etc )
+ pyshell.py ( changed startup behavior )
+ readme.txt ( <Recursion on file with id=1234567> )
+
+
+
+IDLE 0.5 - February 2000 - Release Notes
+----------------------------------------
+
+This is an early release of IDLE, my own attempt at a Tkinter-based
+IDE for Python.
+
+(For a more detailed change log, see the file ChangeLog.)
+
+FEATURES
+
+IDLE has the following features:
+
+- coded in 100% pure Python, using the Tkinter GUI toolkit (i.e. Tcl/Tk)
+
+- cross-platform: works on Windows and Unix (on the Mac, there are
+currently problems with Tcl/Tk)
+
+- multi-window text editor with multiple undo, Python colorizing
+and many other features, e.g. smart indent and call tips
+
+- Python shell window (a.k.a. interactive interpreter)
+
+- debugger (not complete, but you can set breakpoints, view and step)
+
+USAGE
+
+The main program is in the file "idle.py"; on Unix, you should be able
+to run it by typing "./idle.py" to your shell. On Windows, you can
+run it by double-clicking it; you can use idle.pyw to avoid popping up
+a DOS console. If you want to pass command line arguments on Windows,
+use the batch file idle.bat.
+
+Command line arguments: files passed on the command line are executed,
+not opened for editing, unless you give the -e command line option.
+Try "./idle.py -h" to see other command line options.
+
+IDLE requires Python 1.5.2, so it is currently only usable with a
+Python 1.5.2 distribution. (An older version of IDLE is distributed
+with Python 1.5.2; you can drop this version on top of it.)
+
+COPYRIGHT
+
+IDLE is covered by the standard Python copyright notice
+(http://www.python.org/doc/Copyright.html).
+
+
+New in IDLE 0.5 (2/15/2000)
+---------------------------
+
+Tons of stuff, much of it contributed by Tim Peters and Mark Hammond:
+
+- Status bar, displaying current line/column (Moshe Zadka).
+
+- Better stack viewer, using tree widget. (XXX Only used by Stack
+Viewer menu, not by the debugger.)
+
+- Format paragraph now recognizes Python block comments and reformats
+them correctly (MH)
+
+- New version of pyclbr.py parses top-level functions and understands
+much more of Python's syntax; this is reflected in the class and path
+browsers (TP)
+
+- Much better auto-indent; knows how to indent the insides of
+multi-line statements (TP)
+
+- Call tip window pops up when you type the name of a known function
+followed by an open parenthesis. Hit ESC or click elsewhere in the
+window to close the tip window (MH)
+
+- Comment out region now inserts ## to make it stand out more (TP)
+
+- New path and class browsers based on a tree widget that looks
+familiar to Windows users
+
+- Reworked script running commands to be more intuitive: I/O now
+always goes to the *Python Shell* window, and raw_input() works
+correctly. You use F5 to import/reload a module: this adds the module
+name to the __main__ namespace. You use Control-F5 to run a script:
+this runs the script *in* the __main__ namespace. The latter also
+sets sys.argv[] to the script name
+
+
+New in IDLE 0.4 (4/7/99)
+------------------------
+
+Most important change: a new menu entry "File -> Path browser", shows
+a 4-column hierarchical browser which lets you browse sys.path,
+directories, modules, and classes. Yes, it's a superset of the Class
+browser menu entry. There's also a new internal module,
+MultiScrolledLists.py, which provides the framework for this dialog.
+
+
+New in IDLE 0.3 (2/17/99)
+-------------------------
+
+Most important changes:
+
+- Enabled support for running a module, with or without the debugger.
+Output goes to a new window. Pressing F5 in a module is effectively a
+reload of that module; Control-F5 loads it under the debugger.
+
+- Re-enable tearing off the Windows menu, and make a torn-off Windows
+menu update itself whenever a window is opened or closed.
+
+- Menu items can now be have a checkbox (when the menu label starts
+with "!"); use this for the Debugger and "Auto-open stack viewer"
+(was: JIT stack viewer) menu items.
+
+- Added a Quit button to the Debugger API.
+
+- The current directory is explicitly inserted into sys.path.
+
+- Fix the debugger (when using Python 1.5.2b2) to use canonical
+filenames for breakpoints, so these actually work. (There's still a
+lot of work to be done to the management of breakpoints in the
+debugger though.)
+
+- Closing a window that is still colorizing now actually works.
+
+- Allow dragging of the separator between the two list boxes in the
+class browser.
+
+- Bind ESC to "close window" of the debugger, stack viewer and class
+browser. It removes the selection highlighting in regular text
+windows. (These are standard Windows conventions.)
+
+
+New in IDLE 0.2 (1/8/99)
+------------------------
+
+Lots of changes; here are the highlights:
+
+General:
+
+- You can now write and configure your own IDLE extension modules; see
+extend.txt.
+
+
+File menu:
+
+The command to open the Python shell window is now in the File menu.
+
+
+Edit menu:
+
+New Find dialog with more options; replace dialog; find in files dialog.
+
+Commands to tabify or untabify a region.
+
+Command to format a paragraph.
+
+
+Debug menu:
+
+JIT (Just-In-Time) stack viewer toggle -- if set, the stack viewer
+automaticall pops up when you get a traceback.
+
+Windows menu:
+
+Zoom height -- make the window full height.
+
+
+Help menu:
+
+The help text now show up in a regular window so you can search and
+even edit it if you like.
+
+
+
+IDLE 0.1 was distributed with the Python 1.5.2b1 release on 12/22/98.
+
+======================================================================
diff --git a/lib/python2.7/idlelib/HyperParser.py b/lib/python2.7/idlelib/HyperParser.py
new file mode 100644
index 0000000..5816d00
--- /dev/null
+++ b/lib/python2.7/idlelib/HyperParser.py
@@ -0,0 +1,255 @@
+"""Provide advanced parsing abilities for ParenMatch and other extensions.
+
+HyperParser uses PyParser. PyParser mostly gives information on the
+proper indentation of code. HyperParser gives additional information on
+the structure of code.
+"""
+
+import string
+import keyword
+from idlelib import PyParse
+
+class HyperParser:
+
+ def __init__(self, editwin, index):
+ "To initialize, analyze the surroundings of the given index."
+
+ self.editwin = editwin
+ self.text = text = editwin.text
+
+ parser = PyParse.Parser(editwin.indentwidth, editwin.tabwidth)
+
+ def index2line(index):
+ return int(float(index))
+ lno = index2line(text.index(index))
+
+ if not editwin.context_use_ps1:
+ for context in editwin.num_context_lines:
+ startat = max(lno - context, 1)
+ startatindex = repr(startat) + ".0"
+ stopatindex = "%d.end" % lno
+ # We add the newline because PyParse requires a newline
+ # at end. We add a space so that index won't be at end
+ # of line, so that its status will be the same as the
+ # char before it, if should.
+ parser.set_str(text.get(startatindex, stopatindex)+' \n')
+ bod = parser.find_good_parse_start(
+ editwin._build_char_in_string_func(startatindex))
+ if bod is not None or startat == 1:
+ break
+ parser.set_lo(bod or 0)
+ else:
+ r = text.tag_prevrange("console", index)
+ if r:
+ startatindex = r[1]
+ else:
+ startatindex = "1.0"
+ stopatindex = "%d.end" % lno
+ # We add the newline because PyParse requires it. We add a
+ # space so that index won't be at end of line, so that its
+ # status will be the same as the char before it, if should.
+ parser.set_str(text.get(startatindex, stopatindex)+' \n')
+ parser.set_lo(0)
+
+ # We want what the parser has, minus the last newline and space.
+ self.rawtext = parser.str[:-2]
+ # Parser.str apparently preserves the statement we are in, so
+ # that stopatindex can be used to synchronize the string with
+ # the text box indices.
+ self.stopatindex = stopatindex
+ self.bracketing = parser.get_last_stmt_bracketing()
+ # find which pairs of bracketing are openers. These always
+ # correspond to a character of rawtext.
+ self.isopener = [i>0 and self.bracketing[i][1] >
+ self.bracketing[i-1][1]
+ for i in range(len(self.bracketing))]
+
+ self.set_index(index)
+
+ def set_index(self, index):
+ """Set the index to which the functions relate.
+
+ The index must be in the same statement.
+ """
+ indexinrawtext = (len(self.rawtext) -
+ len(self.text.get(index, self.stopatindex)))
+ if indexinrawtext < 0:
+ raise ValueError("Index %s precedes the analyzed statement"
+ % index)
+ self.indexinrawtext = indexinrawtext
+ # find the rightmost bracket to which index belongs
+ self.indexbracket = 0
+ while (self.indexbracket < len(self.bracketing)-1 and
+ self.bracketing[self.indexbracket+1][0] < self.indexinrawtext):
+ self.indexbracket += 1
+ if (self.indexbracket < len(self.bracketing)-1 and
+ self.bracketing[self.indexbracket+1][0] == self.indexinrawtext and
+ not self.isopener[self.indexbracket+1]):
+ self.indexbracket += 1
+
+ def is_in_string(self):
+ """Is the index given to the HyperParser in a string?"""
+ # The bracket to which we belong should be an opener.
+ # If it's an opener, it has to have a character.
+ return (self.isopener[self.indexbracket] and
+ self.rawtext[self.bracketing[self.indexbracket][0]]
+ in ('"', "'"))
+
+ def is_in_code(self):
+ """Is the index given to the HyperParser in normal code?"""
+ return (not self.isopener[self.indexbracket] or
+ self.rawtext[self.bracketing[self.indexbracket][0]]
+ not in ('#', '"', "'"))
+
+ def get_surrounding_brackets(self, openers='([{', mustclose=False):
+ """Return bracket indexes or None.
+
+ If the index given to the HyperParser is surrounded by a
+ bracket defined in openers (or at least has one before it),
+ return the indices of the opening bracket and the closing
+ bracket (or the end of line, whichever comes first).
+
+ If it is not surrounded by brackets, or the end of line comes
+ before the closing bracket and mustclose is True, returns None.
+ """
+
+ bracketinglevel = self.bracketing[self.indexbracket][1]
+ before = self.indexbracket
+ while (not self.isopener[before] or
+ self.rawtext[self.bracketing[before][0]] not in openers or
+ self.bracketing[before][1] > bracketinglevel):
+ before -= 1
+ if before < 0:
+ return None
+ bracketinglevel = min(bracketinglevel, self.bracketing[before][1])
+ after = self.indexbracket + 1
+ while (after < len(self.bracketing) and
+ self.bracketing[after][1] >= bracketinglevel):
+ after += 1
+
+ beforeindex = self.text.index("%s-%dc" %
+ (self.stopatindex, len(self.rawtext)-self.bracketing[before][0]))
+ if (after >= len(self.bracketing) or
+ self.bracketing[after][0] > len(self.rawtext)):
+ if mustclose:
+ return None
+ afterindex = self.stopatindex
+ else:
+ # We are after a real char, so it is a ')' and we give the
+ # index before it.
+ afterindex = self.text.index(
+ "%s-%dc" % (self.stopatindex,
+ len(self.rawtext)-(self.bracketing[after][0]-1)))
+
+ return beforeindex, afterindex
+
+ # Ascii chars that may be in a white space
+ _whitespace_chars = " \t\n\\"
+ # Ascii chars that may be in an identifier
+ _id_chars = string.ascii_letters + string.digits + "_"
+ # Ascii chars that may be the first char of an identifier
+ _id_first_chars = string.ascii_letters + "_"
+
+ # Given a string and pos, return the number of chars in the
+ # identifier which ends at pos, or 0 if there is no such one. Saved
+ # words are not identifiers.
+ def _eat_identifier(self, str, limit, pos):
+ i = pos
+ while i > limit and str[i-1] in self._id_chars:
+ i -= 1
+ if (i < pos and (str[i] not in self._id_first_chars or
+ keyword.iskeyword(str[i:pos]))):
+ i = pos
+ return pos - i
+
+ def get_expression(self):
+ """Return a string with the Python expression which ends at the
+ given index, which is empty if there is no real one.
+ """
+ if not self.is_in_code():
+ raise ValueError("get_expression should only be called"
+ "if index is inside a code.")
+
+ rawtext = self.rawtext
+ bracketing = self.bracketing
+
+ brck_index = self.indexbracket
+ brck_limit = bracketing[brck_index][0]
+ pos = self.indexinrawtext
+
+ last_identifier_pos = pos
+ postdot_phase = True
+
+ while 1:
+ # Eat whitespaces, comments, and if postdot_phase is False - a dot
+ while 1:
+ if pos>brck_limit and rawtext[pos-1] in self._whitespace_chars:
+ # Eat a whitespace
+ pos -= 1
+ elif (not postdot_phase and
+ pos > brck_limit and rawtext[pos-1] == '.'):
+ # Eat a dot
+ pos -= 1
+ postdot_phase = True
+ # The next line will fail if we are *inside* a comment,
+ # but we shouldn't be.
+ elif (pos == brck_limit and brck_index > 0 and
+ rawtext[bracketing[brck_index-1][0]] == '#'):
+ # Eat a comment
+ brck_index -= 2
+ brck_limit = bracketing[brck_index][0]
+ pos = bracketing[brck_index+1][0]
+ else:
+ # If we didn't eat anything, quit.
+ break
+
+ if not postdot_phase:
+ # We didn't find a dot, so the expression end at the
+ # last identifier pos.
+ break
+
+ ret = self._eat_identifier(rawtext, brck_limit, pos)
+ if ret:
+ # There is an identifier to eat
+ pos = pos - ret
+ last_identifier_pos = pos
+ # Now, to continue the search, we must find a dot.
+ postdot_phase = False
+ # (the loop continues now)
+
+ elif pos == brck_limit:
+ # We are at a bracketing limit. If it is a closing
+ # bracket, eat the bracket, otherwise, stop the search.
+ level = bracketing[brck_index][1]
+ while brck_index > 0 and bracketing[brck_index-1][1] > level:
+ brck_index -= 1
+ if bracketing[brck_index][0] == brck_limit:
+ # We were not at the end of a closing bracket
+ break
+ pos = bracketing[brck_index][0]
+ brck_index -= 1
+ brck_limit = bracketing[brck_index][0]
+ last_identifier_pos = pos
+ if rawtext[pos] in "([":
+ # [] and () may be used after an identifier, so we
+ # continue. postdot_phase is True, so we don't allow a dot.
+ pass
+ else:
+ # We can't continue after other types of brackets
+ if rawtext[pos] in "'\"":
+ # Scan a string prefix
+ while pos > 0 and rawtext[pos - 1] in "rRbBuU":
+ pos -= 1
+ last_identifier_pos = pos
+ break
+
+ else:
+ # We've found an operator or something.
+ break
+
+ return rawtext[last_identifier_pos:self.indexinrawtext]
+
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main('idlelib.idle_test.test_hyperparser', verbosity=2)
diff --git a/lib/python2.7/idlelib/IOBinding.py b/lib/python2.7/idlelib/IOBinding.py
new file mode 100644
index 0000000..2aba46e
--- /dev/null
+++ b/lib/python2.7/idlelib/IOBinding.py
@@ -0,0 +1,610 @@
+# 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 codecs
+from codecs import BOM_UTF8
+import os
+import pipes
+import re
+import sys
+import tempfile
+
+from Tkinter import *
+import tkFileDialog
+import tkMessageBox
+from SimpleDialog import SimpleDialog
+
+from idlelib.configHandler import idleConf
+
+# Try setting the locale, so that we can find out
+# what encoding to use
+try:
+ import locale
+ locale.setlocale(locale.LC_CTYPE, "")
+except (ImportError, locale.Error):
+ pass
+
+# Encoding for file names
+filesystemencoding = sys.getfilesystemencoding()
+
+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)
+ except LookupError:
+ pass
+else:
+ try:
+ # Different things can fail here: the locale module may not be
+ # 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 '':
+ # situation occurs on Mac OS X
+ encoding = 'ascii'
+ codecs.lookup(encoding)
+ except (NameError, AttributeError, LookupError):
+ # Try getdefaultlocale well: 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 '':
+ # situation occurs on Mac OS X
+ encoding = 'ascii'
+ codecs.lookup(encoding)
+ except (ValueError, LookupError):
+ pass
+
+encoding = encoding.lower()
+
+coding_re = re.compile(r'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)')
+blank_re = re.compile(r'^[ \t\f]*(?:[#\r\n]|$)')
+
+class EncodingMessage(SimpleDialog):
+ "Inform user that an encoding declaration is needed."
+ def __init__(self, master, enc):
+ self.should_edit = False
+
+ self.root = top = Toplevel(master)
+ top.bind("<Return>", self.return_event)
+ top.bind("<Escape>", self.do_ok)
+ top.protocol("WM_DELETE_WINDOW", self.wm_delete_window)
+ top.wm_title("I/O Warning")
+ top.wm_iconname("I/O Warning")
+ self.top = top
+
+ l1 = Label(top,
+ text="Non-ASCII found, yet no encoding declared. Add a line like")
+ l1.pack(side=TOP, anchor=W)
+ l2 = Entry(top, font="courier")
+ l2.insert(0, "# -*- coding: %s -*-" % enc)
+ # For some reason, the text is not selectable anymore if the
+ # widget is disabled.
+ # l2['state'] = DISABLED
+ l2.pack(side=TOP, anchor = W, fill=X)
+ l3 = Label(top, text="to your file\n"
+ "See Language Reference, 2.1.4 Encoding declarations.\n"
+ "Choose OK to save this file as %s\n"
+ "Edit your general options to silence this warning" % enc)
+ l3.pack(side=TOP, anchor = W)
+
+ buttons = Frame(top)
+ buttons.pack(side=TOP, fill=X)
+ # Both return and cancel mean the same thing: do nothing
+ self.default = self.cancel = 0
+ b1 = Button(buttons, text="Ok", default="active",
+ command=self.do_ok)
+ b1.pack(side=LEFT, fill=BOTH, expand=1)
+ b2 = Button(buttons, text="Edit my file",
+ command=self.do_edit)
+ b2.pack(side=LEFT, fill=BOTH, expand=1)
+
+ self._set_transient(master)
+
+ def do_ok(self):
+ self.done(0)
+
+ def do_edit(self):
+ self.done(1)
+
+def coding_spec(str):
+ """Return the encoding declaration according to PEP 263.
+
+ Raise LookupError if the encoding is declared but unknown.
+ """
+ # Only consider the first two lines
+ lst = str.split("\n", 2)[:2]
+ for line in lst:
+ match = coding_re.match(line)
+ if match is not None:
+ break
+ if not blank_re.match(line):
+ return None
+ else:
+ return None
+ name = match.group(1)
+ # Check whether the encoding is known
+ import codecs
+ try:
+ codecs.lookup(name)
+ except LookupError:
+ # The standard encoding error does not indicate the encoding
+ raise LookupError, "Unknown encoding "+name
+ return name
+
+class IOBinding:
+
+ def __init__(self, editwin):
+ self.editwin = editwin
+ self.text = editwin.text
+ self.__id_open = self.text.bind("<<open-window-from-file>>", self.open)
+ self.__id_save = self.text.bind("<<save-window>>", self.save)
+ self.__id_saveas = self.text.bind("<<save-window-as-file>>",
+ self.save_as)
+ self.__id_savecopy = self.text.bind("<<save-copy-of-window-as-file>>",
+ self.save_a_copy)
+ self.fileencoding = None
+ self.__id_print = self.text.bind("<<print-window>>", self.print_window)
+
+ def close(self):
+ # Undo command bindings
+ self.text.unbind("<<open-window-from-file>>", self.__id_open)
+ self.text.unbind("<<save-window>>", self.__id_save)
+ self.text.unbind("<<save-window-as-file>>",self.__id_saveas)
+ self.text.unbind("<<save-copy-of-window-as-file>>", self.__id_savecopy)
+ self.text.unbind("<<print-window>>", self.__id_print)
+ # Break cycles
+ self.editwin = None
+ self.text = None
+ self.filename_change_hook = None
+
+ def get_saved(self):
+ return self.editwin.get_saved()
+
+ def set_saved(self, flag):
+ self.editwin.set_saved(flag)
+
+ def reset_undo(self):
+ self.editwin.reset_undo()
+
+ filename_change_hook = None
+
+ def set_filename_change_hook(self, hook):
+ self.filename_change_hook = hook
+
+ filename = None
+ dirname = None
+
+ def set_filename(self, filename):
+ if filename and os.path.isdir(filename):
+ self.filename = None
+ self.dirname = filename
+ else:
+ self.filename = filename
+ self.dirname = None
+ self.set_saved(1)
+ if self.filename_change_hook:
+ self.filename_change_hook()
+
+ def open(self, event=None, editFile=None):
+ flist = self.editwin.flist
+ # Save in case parent window is closed (ie, during askopenfile()).
+ if flist:
+ if not editFile:
+ filename = self.askopenfile()
+ else:
+ filename=editFile
+ if filename:
+ # If editFile is valid and already open, flist.open will
+ # shift focus to its existing window.
+ # If the current window exists and is a fresh unnamed,
+ # unmodified editor window (not an interpreter shell),
+ # pass self.loadfile to flist.open so it will load the file
+ # in the current window (if the file is not already open)
+ # instead of a new window.
+ if (self.editwin and
+ not getattr(self.editwin, 'interp', None) and
+ not self.filename and
+ self.get_saved()):
+ flist.open(filename, self.loadfile)
+ else:
+ flist.open(filename)
+ else:
+ if self.text:
+ self.text.focus_set()
+ return "break"
+
+ # Code for use outside IDLE:
+ if self.get_saved():
+ reply = self.maybesave()
+ if reply == "cancel":
+ self.text.focus_set()
+ return "break"
+ if not editFile:
+ filename = self.askopenfile()
+ else:
+ filename=editFile
+ if filename:
+ self.loadfile(filename)
+ else:
+ self.text.focus_set()
+ return "break"
+
+ eol = r"(\r\n)|\n|\r" # \r\n (Windows), \n (UNIX), or \r (Mac)
+ eol_re = re.compile(eol)
+ eol_convention = os.linesep # Default
+
+ def loadfile(self, filename):
+ try:
+ # open the file in binary mode so that we can handle
+ # end-of-line convention ourselves.
+ with open(filename, 'rb') as f:
+ chars = f.read()
+ except IOError as msg:
+ tkMessageBox.showerror("I/O Error", str(msg), parent=self.text)
+ return False
+
+ chars = self.decode(chars)
+ # We now convert all end-of-lines to '\n's
+ firsteol = self.eol_re.search(chars)
+ if firsteol:
+ self.eol_convention = firsteol.group(0)
+ if isinstance(self.eol_convention, unicode):
+ # 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.yview("insert")
+ self.updaterecentfileslist(filename)
+ return True
+
+ def decode(self, chars):
+ """Create a Unicode string
+
+ If that fails, let Tcl try its best
+ """
+ # Check presence of a UTF-8 signature first
+ if chars.startswith(BOM_UTF8):
+ try:
+ chars = chars[3:].decode("utf-8")
+ except UnicodeError:
+ # has UTF-8 signature, but fails to decode...
+ return chars
+ else:
+ # Indicates that this file originally had a BOM
+ self.fileencoding = BOM_UTF8
+ return chars
+ # Next look for coding specification
+ try:
+ enc = coding_spec(chars)
+ except LookupError as name:
+ tkMessageBox.showerror(
+ title="Error loading the file",
+ message="The encoding '%s' is not known to this Python "\
+ "installation. The file may not display correctly" % name,
+ parent = self.text)
+ enc = None
+ if enc:
+ try:
+ return unicode(chars, enc)
+ except UnicodeError:
+ pass
+ # If it is ASCII, we need not to record anything
+ try:
+ return unicode(chars, 'ascii')
+ except UnicodeError:
+ pass
+ # Finally, try the locale's encoding. This is deprecated;
+ # the user should declare a non-ASCII encoding
+ try:
+ chars = unicode(chars, encoding)
+ self.fileencoding = encoding
+ except UnicodeError:
+ pass
+ return chars
+
+ def maybesave(self):
+ if self.get_saved():
+ return "yes"
+ message = "Do you want to save %s before closing?" % (
+ self.filename or "this untitled document")
+ confirm = tkMessageBox.askyesnocancel(
+ title="Save On Close",
+ message=message,
+ default=tkMessageBox.YES,
+ parent=self.text)
+ if confirm:
+ reply = "yes"
+ self.save(None)
+ if not self.get_saved():
+ reply = "cancel"
+ elif confirm is None:
+ reply = "cancel"
+ else:
+ reply = "no"
+ self.text.focus_set()
+ return reply
+
+ def save(self, event):
+ if not self.filename:
+ self.save_as(event)
+ else:
+ if self.writefile(self.filename):
+ self.set_saved(True)
+ try:
+ self.editwin.store_file_breaks()
+ except AttributeError: # may be a PyShell
+ pass
+ self.text.focus_set()
+ return "break"
+
+ def save_as(self, event):
+ filename = self.asksavefile()
+ if filename:
+ if self.writefile(filename):
+ self.set_filename(filename)
+ self.set_saved(1)
+ try:
+ self.editwin.store_file_breaks()
+ except AttributeError:
+ pass
+ self.text.focus_set()
+ self.updaterecentfileslist(filename)
+ return "break"
+
+ def save_a_copy(self, event):
+ filename = self.asksavefile()
+ if filename:
+ self.writefile(filename)
+ self.text.focus_set()
+ self.updaterecentfileslist(filename)
+ return "break"
+
+ def writefile(self, filename):
+ self.fixlastline()
+ chars = self.encode(self.text.get("1.0", "end-1c"))
+ if self.eol_convention != "\n":
+ chars = chars.replace("\n", self.eol_convention)
+ try:
+ with open(filename, "wb") as f:
+ f.write(chars)
+ return True
+ except IOError as msg:
+ tkMessageBox.showerror("I/O Error", str(msg),
+ parent=self.text)
+ return False
+
+ def encode(self, chars):
+ if isinstance(chars, str):
+ # This is either plain ASCII, or Tk was returning mixed-encoding
+ # text to us. Don't try to guess further.
+ return chars
+ # See whether there is anything non-ASCII in it.
+ # If not, no need to figure out the encoding.
+ try:
+ return chars.encode('ascii')
+ except UnicodeError:
+ pass
+ # If there is an encoding declared, try this first.
+ try:
+ enc = coding_spec(chars)
+ failed = None
+ except LookupError as msg:
+ failed = msg
+ enc = None
+ if enc:
+ try:
+ return chars.encode(enc)
+ except UnicodeError:
+ failed = "Invalid encoding '%s'" % enc
+ if failed:
+ tkMessageBox.showerror(
+ "I/O Error",
+ "%s. Saving as UTF-8" % failed,
+ parent = self.text)
+ # If there was a UTF-8 signature, use that. This should not fail
+ if self.fileencoding == BOM_UTF8 or failed:
+ return BOM_UTF8 + chars.encode("utf-8")
+ # Try the original file encoding next, if any
+ if self.fileencoding:
+ try:
+ return chars.encode(self.fileencoding)
+ except UnicodeError:
+ tkMessageBox.showerror(
+ "I/O Error",
+ "Cannot save this as '%s' anymore. Saving as UTF-8" \
+ % self.fileencoding,
+ parent = self.text)
+ return BOM_UTF8 + chars.encode("utf-8")
+ # Nothing was declared, and we had not determined an encoding
+ # on loading. Recommend an encoding line.
+ config_encoding = idleConf.GetOption("main","EditorWindow",
+ "encoding")
+ if config_encoding == 'utf-8':
+ # User has requested that we save files as UTF-8
+ return BOM_UTF8 + chars.encode("utf-8")
+ ask_user = True
+ try:
+ chars = chars.encode(encoding)
+ enc = encoding
+ if config_encoding == 'locale':
+ ask_user = False
+ except UnicodeError:
+ chars = BOM_UTF8 + chars.encode("utf-8")
+ enc = "utf-8"
+ if not ask_user:
+ return chars
+ dialog = EncodingMessage(self.editwin.top, enc)
+ dialog.go()
+ if dialog.num == 1:
+ # User asked us to edit the file
+ encline = "# -*- coding: %s -*-\n" % enc
+ firstline = self.text.get("1.0", "2.0")
+ if firstline.startswith("#!"):
+ # Insert encoding after #! line
+ self.text.insert("2.0", encline)
+ else:
+ self.text.insert("1.0", encline)
+ return self.encode(self.text.get("1.0", "end-1c"))
+ return chars
+
+ def fixlastline(self):
+ c = self.text.get("end-2c")
+ if c != '\n':
+ self.text.insert("end-1c", "\n")
+
+ def print_window(self, event):
+ confirm = tkMessageBox.askokcancel(
+ title="Print",
+ message="Print to Default Printer",
+ default=tkMessageBox.OK,
+ parent=self.text)
+ if not confirm:
+ self.text.focus_set()
+ return "break"
+ tempfilename = None
+ saved = self.get_saved()
+ if saved:
+ 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)
+ return "break"
+ platform = os.name
+ printPlatform = True
+ if platform == 'posix': #posix platform
+ command = idleConf.GetOption('main','General',
+ 'print-command-posix')
+ command = command + " 2>&1"
+ elif platform == 'nt': #win32 platform
+ command = idleConf.GetOption('main','General','print-command-win')
+ else: #no printing for this platform
+ printPlatform = False
+ if printPlatform: #we can try to print for this platform
+ command = command % pipes.quote(filename)
+ pipe = os.popen(command, "r")
+ # things can get ugly on NT if there is no printer available.
+ output = pipe.read().strip()
+ status = pipe.close()
+ if status:
+ output = "Printing failed (exit status 0x%x)\n" % \
+ status + output
+ if output:
+ output = "Printing command: %s\n" % repr(command) + output
+ tkMessageBox.showerror("Print status", output, parent=self.text)
+ else: #no printing for this platform
+ message = "Printing is not enabled for this platform: %s" % platform
+ tkMessageBox.showinfo("Print status", message, parent=self.text)
+ if tempfilename:
+ os.unlink(tempfilename)
+ return "break"
+
+ opendialog = None
+ savedialog = None
+
+ filetypes = [
+ ("Python files", "*.py *.pyw", "TEXT"),
+ ("Text files", "*.txt", "TEXT"),
+ ("All files", "*"),
+ ]
+
+ defaultextension = '.py' if sys.platform == 'darwin' else ''
+
+ def askopenfile(self):
+ dir, base = self.defaultfilename("open")
+ if not self.opendialog:
+ self.opendialog = tkFileDialog.Open(parent=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"):
+ if self.filename:
+ return os.path.split(self.filename)
+ elif self.dirname:
+ return self.dirname, ""
+ else:
+ try:
+ pwd = os.getcwd()
+ except os.error:
+ pwd = ""
+ return pwd, ""
+
+ def asksavefile(self):
+ dir, base = self.defaultfilename("save")
+ if not self.savedialog:
+ self.savedialog = tkFileDialog.SaveAs(
+ parent=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)
+
+
+def _io_binding(parent): # htest #
+ from Tkinter import Toplevel, Text
+
+ root = Toplevel(parent)
+ root.title("Test IOBinding")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ class MyEditWin:
+ def __init__(self, text):
+ self.text = text
+ self.flist = None
+ self.text.bind("<Control-o>", self.open)
+ self.text.bind('<Control-p>', self.printer)
+ self.text.bind("<Control-s>", self.save)
+ self.text.bind("<Alt-s>", self.saveas)
+ self.text.bind('<Control-c>', self.savecopy)
+ def get_saved(self): return 0
+ def set_saved(self, flag): pass
+ def reset_undo(self): pass
+ def update_recent_files_list(self, filename): pass
+ def open(self, event):
+ self.text.event_generate("<<open-window-from-file>>")
+ def printer(self, event):
+ self.text.event_generate("<<print-window>>")
+ def save(self, event):
+ self.text.event_generate("<<save-window>>")
+ def saveas(self, event):
+ self.text.event_generate("<<save-window-as-file>>")
+ def savecopy(self, event):
+ self.text.event_generate("<<save-copy-of-window-as-file>>")
+
+ text = Text(root)
+ text.pack()
+ text.focus_set()
+ editwin = MyEditWin(text)
+ IOBinding(editwin)
+
+if __name__ == "__main__":
+ from idlelib.idle_test.htest import run
+ run(_io_binding)
diff --git a/lib/python2.7/idlelib/Icons/folder.gif b/lib/python2.7/idlelib/Icons/folder.gif
new file mode 100644
index 0000000..effe8dc
--- /dev/null
+++ b/lib/python2.7/idlelib/Icons/folder.gif
Binary files differ
diff --git a/lib/python2.7/idlelib/Icons/idle.icns b/lib/python2.7/idlelib/Icons/idle.icns
new file mode 100644
index 0000000..f65e313
--- /dev/null
+++ b/lib/python2.7/idlelib/Icons/idle.icns
Binary files differ
diff --git a/lib/python2.7/idlelib/Icons/idle.ico b/lib/python2.7/idlelib/Icons/idle.ico
new file mode 100644
index 0000000..3357aef
--- /dev/null
+++ b/lib/python2.7/idlelib/Icons/idle.ico
Binary files differ
diff --git a/lib/python2.7/idlelib/Icons/idle_16.gif b/lib/python2.7/idlelib/Icons/idle_16.gif
new file mode 100644
index 0000000..9f001b1
--- /dev/null
+++ b/lib/python2.7/idlelib/Icons/idle_16.gif
Binary files differ
diff --git a/lib/python2.7/idlelib/Icons/idle_16.png b/lib/python2.7/idlelib/Icons/idle_16.png
new file mode 100644
index 0000000..6abde0a
--- /dev/null
+++ b/lib/python2.7/idlelib/Icons/idle_16.png
Binary files differ
diff --git a/lib/python2.7/idlelib/Icons/idle_32.gif b/lib/python2.7/idlelib/Icons/idle_32.gif
new file mode 100644
index 0000000..af5b2d5
--- /dev/null
+++ b/lib/python2.7/idlelib/Icons/idle_32.gif
Binary files differ
diff --git a/lib/python2.7/idlelib/Icons/idle_32.png b/lib/python2.7/idlelib/Icons/idle_32.png
new file mode 100644
index 0000000..41b70db
--- /dev/null
+++ b/lib/python2.7/idlelib/Icons/idle_32.png
Binary files differ
diff --git a/lib/python2.7/idlelib/Icons/idle_48.gif b/lib/python2.7/idlelib/Icons/idle_48.gif
new file mode 100644
index 0000000..fc5304f
--- /dev/null
+++ b/lib/python2.7/idlelib/Icons/idle_48.gif
Binary files differ
diff --git a/lib/python2.7/idlelib/Icons/idle_48.png b/lib/python2.7/idlelib/Icons/idle_48.png
new file mode 100644
index 0000000..e5fa928
--- /dev/null
+++ b/lib/python2.7/idlelib/Icons/idle_48.png
Binary files differ
diff --git a/lib/python2.7/idlelib/Icons/minusnode.gif b/lib/python2.7/idlelib/Icons/minusnode.gif
new file mode 100644
index 0000000..c72e46f
--- /dev/null
+++ b/lib/python2.7/idlelib/Icons/minusnode.gif
Binary files differ
diff --git a/lib/python2.7/idlelib/Icons/openfolder.gif b/lib/python2.7/idlelib/Icons/openfolder.gif
new file mode 100644
index 0000000..24aea1b
--- /dev/null
+++ b/lib/python2.7/idlelib/Icons/openfolder.gif
Binary files differ
diff --git a/lib/python2.7/idlelib/Icons/plusnode.gif b/lib/python2.7/idlelib/Icons/plusnode.gif
new file mode 100644
index 0000000..13ace90
--- /dev/null
+++ b/lib/python2.7/idlelib/Icons/plusnode.gif
Binary files differ
diff --git a/lib/python2.7/idlelib/Icons/python.gif b/lib/python2.7/idlelib/Icons/python.gif
new file mode 100644
index 0000000..b189c2c
--- /dev/null
+++ b/lib/python2.7/idlelib/Icons/python.gif
Binary files differ
diff --git a/lib/python2.7/idlelib/Icons/tk.gif b/lib/python2.7/idlelib/Icons/tk.gif
new file mode 100644
index 0000000..a603f5e
--- /dev/null
+++ b/lib/python2.7/idlelib/Icons/tk.gif
Binary files differ
diff --git a/lib/python2.7/idlelib/IdleHistory.py b/lib/python2.7/idlelib/IdleHistory.py
new file mode 100644
index 0000000..078af29
--- /dev/null
+++ b/lib/python2.7/idlelib/IdleHistory.py
@@ -0,0 +1,104 @@
+"Implement Idle Shell history mechanism with History class"
+
+from idlelib.configHandler import idleConf
+
+class History:
+ ''' Implement Idle Shell history mechanism.
+
+ store - Store source statement (called from PyShell.resetoutput).
+ fetch - Fetch stored statement matching prefix already entered.
+ history_next - Bound to <<history-next>> event (default Alt-N).
+ history_prev - Bound to <<history-prev>> event (default Alt-P).
+ '''
+ def __init__(self, text):
+ '''Initialize data attributes and bind event methods.
+
+ .text - Idle wrapper of tk Text widget, with .bell().
+ .history - source statements, possibly with multiple lines.
+ .prefix - source already entered at prompt; filters history list.
+ .pointer - index into history.
+ .cyclic - wrap around history list (or not).
+ '''
+ self.text = text
+ self.history = []
+ self.prefix = None
+ self.pointer = None
+ self.cyclic = idleConf.GetOption("main", "History", "cyclic", 1, "bool")
+ text.bind("<<history-previous>>", self.history_prev)
+ text.bind("<<history-next>>", self.history_next)
+
+ def history_next(self, event):
+ "Fetch later statement; start with ealiest if cyclic."
+ self.fetch(reverse=False)
+ return "break"
+
+ def history_prev(self, event):
+ "Fetch earlier statement; start with most recent."
+ self.fetch(reverse=True)
+ return "break"
+
+ def fetch(self, reverse):
+ '''Fetch statememt and replace current line in text widget.
+
+ Set prefix and pointer as needed for successive fetches.
+ Reset them to None, None when returning to the start line.
+ Sound bell when return to start line or cannot leave a line
+ because cyclic is False.
+ '''
+ nhist = len(self.history)
+ pointer = self.pointer
+ prefix = self.prefix
+ if pointer is not None and prefix is not None:
+ if self.text.compare("insert", "!=", "end-1c") or \
+ self.text.get("iomark", "end-1c") != self.history[pointer]:
+ pointer = prefix = None
+ self.text.mark_set("insert", "end-1c") # != after cursor move
+ if pointer is None or prefix is None:
+ prefix = self.text.get("iomark", "end-1c")
+ if reverse:
+ pointer = nhist # will be decremented
+ else:
+ if self.cyclic:
+ pointer = -1 # will be incremented
+ else: # abort history_next
+ self.text.bell()
+ return
+ nprefix = len(prefix)
+ while 1:
+ pointer += -1 if reverse else 1
+ if pointer < 0 or pointer >= nhist:
+ self.text.bell()
+ if not self.cyclic and pointer < 0: # abort history_prev
+ return
+ else:
+ if self.text.get("iomark", "end-1c") != prefix:
+ self.text.delete("iomark", "end-1c")
+ self.text.insert("iomark", prefix)
+ pointer = prefix = None
+ break
+ item = self.history[pointer]
+ if item[:nprefix] == prefix and len(item) > nprefix:
+ self.text.delete("iomark", "end-1c")
+ self.text.insert("iomark", item)
+ break
+ self.text.see("insert")
+ self.text.tag_remove("sel", "1.0", "end")
+ self.pointer = pointer
+ self.prefix = prefix
+
+ def store(self, source):
+ "Store Shell input statement into history list."
+ source = source.strip()
+ if len(source) > 2:
+ # avoid duplicates
+ try:
+ self.history.remove(source)
+ except ValueError:
+ pass
+ self.history.append(source)
+ self.pointer = None
+ self.prefix = None
+
+if __name__ == "__main__":
+ from unittest import main
+ main('idlelib.idle_test.test_idlehistory', verbosity=2, exit=False)
diff --git a/lib/python2.7/idlelib/MultiCall.py b/lib/python2.7/idlelib/MultiCall.py
new file mode 100644
index 0000000..a157d7a
--- /dev/null
+++ b/lib/python2.7/idlelib/MultiCall.py
@@ -0,0 +1,430 @@
+"""
+MultiCall - a class which inherits its methods from a Tkinter widget (Text, for
+example), but enables multiple calls of functions per virtual event - all
+matching events will be called, not only the most specific one. This is done
+by wrapping the event functions - event_add, event_delete and event_info.
+MultiCall recognizes only a subset of legal event sequences. Sequences which
+are not recognized are treated by the original Tk handling mechanism. A
+more-specific event will be called before a less-specific event.
+
+The recognized sequences are complete one-event sequences (no emacs-style
+Ctrl-X Ctrl-C, no shortcuts like <3>), for all types of events.
+Key/Button Press/Release events can have modifiers.
+The recognized modifiers are Shift, Control, Option and Command for Mac, and
+Control, Alt, Shift, Meta/M for other platforms.
+
+For all events which were handled by MultiCall, a new member is added to the
+event instance passed to the binded functions - mc_type. This is one of the
+event type constants defined in this module (such as MC_KEYPRESS).
+For Key/Button events (which are handled by MultiCall and may receive
+modifiers), another member is added - mc_state. This member gives the state
+of the recognized modifiers, as a combination of the modifier constants
+also defined in this module (for example, MC_SHIFT).
+Using these members is absolutely portable.
+
+The order by which events are called is defined by these rules:
+1. A more-specific event will be called before a less-specific event.
+2. A recently-binded event will be called before a previously-binded event,
+ unless this conflicts with the first rule.
+Each function will be called at most once for each event.
+"""
+
+import sys
+import string
+import re
+import Tkinter
+
+# the event type constants, which define the meaning of mc_type
+MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3;
+MC_ACTIVATE=4; MC_CIRCULATE=5; MC_COLORMAP=6; MC_CONFIGURE=7;
+MC_DEACTIVATE=8; MC_DESTROY=9; MC_ENTER=10; MC_EXPOSE=11; MC_FOCUSIN=12;
+MC_FOCUSOUT=13; MC_GRAVITY=14; MC_LEAVE=15; MC_MAP=16; MC_MOTION=17;
+MC_MOUSEWHEEL=18; MC_PROPERTY=19; MC_REPARENT=20; MC_UNMAP=21; MC_VISIBILITY=22;
+# the modifier state constants, which define the meaning of mc_state
+MC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5
+MC_OPTION = 1<<6; MC_COMMAND = 1<<7
+
+# define the list of modifiers, to be used in complex event types.
+if sys.platform == "darwin":
+ _modifiers = (("Shift",), ("Control",), ("Option",), ("Command",))
+ _modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND)
+else:
+ _modifiers = (("Control",), ("Alt",), ("Shift",), ("Meta", "M"))
+ _modifier_masks = (MC_CONTROL, MC_ALT, MC_SHIFT, MC_META)
+
+# a dictionary to map a modifier name into its number
+_modifier_names = dict([(name, number)
+ for number in range(len(_modifiers))
+ for name in _modifiers[number]])
+
+# A binder is a class which binds functions to one type of event. It has two
+# methods: bind and unbind, which get a function and a parsed sequence, as
+# returned by _parse_sequence(). There are two types of binders:
+# _SimpleBinder handles event types with no modifiers and no detail.
+# No Python functions are called when no events are binded.
+# _ComplexBinder handles event types with modifiers and a detail.
+# A Python function is called each time an event is generated.
+
+class _SimpleBinder:
+ def __init__(self, type, widget, widgetinst):
+ self.type = type
+ self.sequence = '<'+_types[type][0]+'>'
+ self.widget = widget
+ self.widgetinst = widgetinst
+ self.bindedfuncs = []
+ self.handlerid = None
+
+ def bind(self, triplet, func):
+ if not self.handlerid:
+ def handler(event, l = self.bindedfuncs, mc_type = self.type):
+ event.mc_type = mc_type
+ wascalled = {}
+ for i in range(len(l)-1, -1, -1):
+ func = l[i]
+ if func not in wascalled:
+ wascalled[func] = True
+ r = func(event)
+ if r:
+ return r
+ self.handlerid = self.widget.bind(self.widgetinst,
+ self.sequence, handler)
+ self.bindedfuncs.append(func)
+
+ def unbind(self, triplet, func):
+ self.bindedfuncs.remove(func)
+ if not self.bindedfuncs:
+ self.widget.unbind(self.widgetinst, self.sequence, self.handlerid)
+ self.handlerid = None
+
+ def __del__(self):
+ if self.handlerid:
+ self.widget.unbind(self.widgetinst, self.sequence, self.handlerid)
+
+# An int in range(1 << len(_modifiers)) represents a combination of modifiers
+# (if the least significant bit is on, _modifiers[0] is on, and so on).
+# _state_subsets gives for each combination of modifiers, or *state*,
+# a list of the states which are a subset of it. This list is ordered by the
+# number of modifiers is the state - the most specific state comes first.
+_states = range(1 << len(_modifiers))
+_state_names = [''.join(m[0]+'-'
+ for i, m in enumerate(_modifiers)
+ if (1 << i) & s)
+ for s in _states]
+
+def expand_substates(states):
+ '''For each item of states return a list containing all combinations of
+ that item with individual bits reset, sorted by the number of set bits.
+ '''
+ def nbits(n):
+ "number of bits set in n base 2"
+ nb = 0
+ while n:
+ n, rem = divmod(n, 2)
+ nb += rem
+ return nb
+ statelist = []
+ for state in states:
+ substates = list(set(state & x for x in states))
+ substates.sort(key=nbits, reverse=True)
+ statelist.append(substates)
+ return statelist
+
+_state_subsets = expand_substates(_states)
+
+# _state_codes gives for each state, the portable code to be passed as mc_state
+_state_codes = []
+for s in _states:
+ r = 0
+ for i in range(len(_modifiers)):
+ if (1 << i) & s:
+ r |= _modifier_masks[i]
+ _state_codes.append(r)
+
+class _ComplexBinder:
+ # This class binds many functions, and only unbinds them when it is deleted.
+ # self.handlerids is the list of seqs and ids of binded handler functions.
+ # The binded functions sit in a dictionary of lists of lists, which maps
+ # a detail (or None) and a state into a list of functions.
+ # When a new detail is discovered, handlers for all the possible states
+ # are binded.
+
+ def __create_handler(self, lists, mc_type, mc_state):
+ def handler(event, lists = lists,
+ mc_type = mc_type, mc_state = mc_state,
+ ishandlerrunning = self.ishandlerrunning,
+ doafterhandler = self.doafterhandler):
+ ishandlerrunning[:] = [True]
+ event.mc_type = mc_type
+ event.mc_state = mc_state
+ wascalled = {}
+ r = None
+ for l in lists:
+ for i in range(len(l)-1, -1, -1):
+ func = l[i]
+ if func not in wascalled:
+ wascalled[func] = True
+ r = l[i](event)
+ if r:
+ break
+ if r:
+ break
+ ishandlerrunning[:] = []
+ # Call all functions in doafterhandler and remove them from list
+ for f in doafterhandler:
+ f()
+ doafterhandler[:] = []
+ if r:
+ return r
+ return handler
+
+ def __init__(self, type, widget, widgetinst):
+ self.type = type
+ self.typename = _types[type][0]
+ self.widget = widget
+ self.widgetinst = widgetinst
+ self.bindedfuncs = {None: [[] for s in _states]}
+ self.handlerids = []
+ # we don't want to change the lists of functions while a handler is
+ # running - it will mess up the loop and anyway, we usually want the
+ # change to happen from the next event. So we have a list of functions
+ # for the handler to run after it finishes calling the binded functions.
+ # It calls them only once.
+ # ishandlerrunning is a list. An empty one means no, otherwise - yes.
+ # this is done so that it would be mutable.
+ self.ishandlerrunning = []
+ self.doafterhandler = []
+ for s in _states:
+ lists = [self.bindedfuncs[None][i] for i in _state_subsets[s]]
+ handler = self.__create_handler(lists, type, _state_codes[s])
+ seq = '<'+_state_names[s]+self.typename+'>'
+ self.handlerids.append((seq, self.widget.bind(self.widgetinst,
+ seq, handler)))
+
+ def bind(self, triplet, func):
+ if triplet[2] not in self.bindedfuncs:
+ self.bindedfuncs[triplet[2]] = [[] for s in _states]
+ for s in _states:
+ lists = [ self.bindedfuncs[detail][i]
+ for detail in (triplet[2], None)
+ for i in _state_subsets[s] ]
+ handler = self.__create_handler(lists, self.type,
+ _state_codes[s])
+ seq = "<%s%s-%s>"% (_state_names[s], self.typename, triplet[2])
+ self.handlerids.append((seq, self.widget.bind(self.widgetinst,
+ seq, handler)))
+ doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].append(func)
+ if not self.ishandlerrunning:
+ doit()
+ else:
+ self.doafterhandler.append(doit)
+
+ def unbind(self, triplet, func):
+ doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].remove(func)
+ if not self.ishandlerrunning:
+ doit()
+ else:
+ self.doafterhandler.append(doit)
+
+ def __del__(self):
+ for seq, id in self.handlerids:
+ self.widget.unbind(self.widgetinst, seq, id)
+
+# define the list of event types to be handled by MultiEvent. the order is
+# compatible with the definition of event type constants.
+_types = (
+ ("KeyPress", "Key"), ("KeyRelease",), ("ButtonPress", "Button"),
+ ("ButtonRelease",), ("Activate",), ("Circulate",), ("Colormap",),
+ ("Configure",), ("Deactivate",), ("Destroy",), ("Enter",), ("Expose",),
+ ("FocusIn",), ("FocusOut",), ("Gravity",), ("Leave",), ("Map",),
+ ("Motion",), ("MouseWheel",), ("Property",), ("Reparent",), ("Unmap",),
+ ("Visibility",),
+)
+
+# which binder should be used for every event type?
+_binder_classes = (_ComplexBinder,) * 4 + (_SimpleBinder,) * (len(_types)-4)
+
+# A dictionary to map a type name into its number
+_type_names = dict([(name, number)
+ for number in range(len(_types))
+ for name in _types[number]])
+
+_keysym_re = re.compile(r"^\w+$")
+_button_re = re.compile(r"^[1-5]$")
+def _parse_sequence(sequence):
+ """Get a string which should describe an event sequence. If it is
+ successfully parsed as one, return a tuple containing the state (as an int),
+ the event type (as an index of _types), and the detail - None if none, or a
+ string if there is one. If the parsing is unsuccessful, return None.
+ """
+ if not sequence or sequence[0] != '<' or sequence[-1] != '>':
+ return None
+ words = string.split(sequence[1:-1], '-')
+
+ 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
+ else:
+ detail = None
+ else:
+ # _ComplexBinder
+ if type in [_type_names[s] for s in ("KeyPress", "KeyRelease")]:
+ type_re = _keysym_re
+ else:
+ type_re = _button_re
+
+ if not words:
+ detail = None
+ elif len(words) == 1 and type_re.match(words[0]):
+ detail = words[0]
+ else:
+ return None
+
+ return modifiers, type, detail
+
+def _triplet_to_sequence(triplet):
+ if triplet[2]:
+ return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'-'+ \
+ triplet[2]+'>'
+ else:
+ return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'>'
+
+_multicall_dict = {}
+def MultiCallCreator(widget):
+ """Return a MultiCall class which inherits its methods from the
+ given widget class (for example, Tkinter.Text). This is used
+ instead of a templating mechanism.
+ """
+ if widget in _multicall_dict:
+ return _multicall_dict[widget]
+
+ class MultiCall (widget):
+ assert issubclass(widget, Tkinter.Misc)
+
+ def __init__(self, *args, **kwargs):
+ widget.__init__(self, *args, **kwargs)
+ # a dictionary which maps a virtual event to a tuple with:
+ # 0. the function binded
+ # 1. a list of triplets - the sequences it is binded to
+ self.__eventinfo = {}
+ self.__binders = [_binder_classes[i](i, widget, self)
+ for i in range(len(_types))]
+
+ def bind(self, sequence=None, func=None, add=None):
+ #print "bind(%s, %s, %s) called." % (sequence, func, add)
+ if type(sequence) is str and len(sequence) > 2 and \
+ sequence[:2] == "<<" and sequence[-2:] == ">>":
+ if sequence in self.__eventinfo:
+ ei = self.__eventinfo[sequence]
+ if ei[0] is not None:
+ for triplet in ei[1]:
+ self.__binders[triplet[1]].unbind(triplet, ei[0])
+ ei[0] = func
+ if ei[0] is not None:
+ for triplet in ei[1]:
+ self.__binders[triplet[1]].bind(triplet, func)
+ else:
+ self.__eventinfo[sequence] = [func, []]
+ return widget.bind(self, sequence, func, add)
+
+ def unbind(self, sequence, funcid=None):
+ if type(sequence) is str and len(sequence) > 2 and \
+ sequence[:2] == "<<" and sequence[-2:] == ">>" and \
+ sequence in self.__eventinfo:
+ func, triplets = self.__eventinfo[sequence]
+ if func is not None:
+ for triplet in triplets:
+ self.__binders[triplet[1]].unbind(triplet, func)
+ self.__eventinfo[sequence][0] = None
+ return widget.unbind(self, sequence, funcid)
+
+ def event_add(self, virtual, *sequences):
+ #print "event_add(%s,%s) was called"%(repr(virtual),repr(sequences))
+ if virtual not in self.__eventinfo:
+ self.__eventinfo[virtual] = [None, []]
+
+ func, triplets = self.__eventinfo[virtual]
+ for seq in sequences:
+ triplet = _parse_sequence(seq)
+ if triplet is None:
+ #print >> sys.stderr, "Seq. %s was added by Tkinter."%seq
+ widget.event_add(self, virtual, seq)
+ else:
+ if func is not None:
+ self.__binders[triplet[1]].bind(triplet, func)
+ triplets.append(triplet)
+
+ def event_delete(self, virtual, *sequences):
+ if virtual not in self.__eventinfo:
+ return
+ func, triplets = self.__eventinfo[virtual]
+ for seq in sequences:
+ triplet = _parse_sequence(seq)
+ if triplet is None:
+ #print >> sys.stderr, "Seq. %s was deleted by Tkinter."%seq
+ widget.event_delete(self, virtual, seq)
+ else:
+ if func is not None:
+ self.__binders[triplet[1]].unbind(triplet, func)
+ triplets.remove(triplet)
+
+ def event_info(self, virtual=None):
+ if virtual is None or virtual not in self.__eventinfo:
+ return widget.event_info(self, virtual)
+ else:
+ return tuple(map(_triplet_to_sequence,
+ self.__eventinfo[virtual][1])) + \
+ widget.event_info(self, virtual)
+
+ def __del__(self):
+ for virtual in self.__eventinfo:
+ func, triplets = self.__eventinfo[virtual]
+ if func:
+ for triplet in triplets:
+ self.__binders[triplet[1]].unbind(triplet, func)
+
+
+ _multicall_dict[widget] = MultiCall
+ return MultiCall
+
+
+def _multi_call(parent):
+ root = Tkinter.Tk()
+ root.title("Test MultiCall")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ text = MultiCallCreator(Tkinter.Text)(root)
+ text.pack()
+ def bindseq(seq, n=[0]):
+ def handler(event):
+ print seq
+ text.bind("<<handler%d>>"%n[0], handler)
+ text.event_add("<<handler%d>>"%n[0], seq)
+ n[0] += 1
+ bindseq("<Key>")
+ bindseq("<Control-Key>")
+ bindseq("<Alt-Key-a>")
+ bindseq("<Control-Key-a>")
+ bindseq("<Alt-Control-Key-a>")
+ bindseq("<Key-b>")
+ bindseq("<Control-Button-1>")
+ bindseq("<Button-2>")
+ bindseq("<Alt-Button-1>")
+ bindseq("<FocusOut>")
+ bindseq("<Enter>")
+ bindseq("<Leave>")
+ root.mainloop()
+
+if __name__ == "__main__":
+ from idlelib.idle_test.htest import run
+ run(_multi_call)
diff --git a/lib/python2.7/idlelib/MultiStatusBar.py b/lib/python2.7/idlelib/MultiStatusBar.py
new file mode 100644
index 0000000..e3d59ee
--- /dev/null
+++ b/lib/python2.7/idlelib/MultiStatusBar.py
@@ -0,0 +1,47 @@
+from Tkinter import *
+
+class MultiStatusBar(Frame):
+
+ def __init__(self, master=None, **kw):
+ if master is None:
+ master = Tk()
+ Frame.__init__(self, master, **kw)
+ self.labels = {}
+
+ def set_label(self, name, text='', side=LEFT, width=0):
+ if name not in self.labels:
+ label = Label(self, borderwidth=0, anchor=W)
+ label.pack(side=side, pady=0, padx=4)
+ self.labels[name] = label
+ else:
+ label = self.labels[name]
+ if width != 0:
+ label.config(width=width)
+ label.config(text=text)
+
+def _multistatus_bar(parent):
+ root = Tk()
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d" %(x, y + 150))
+ root.title("Test multistatus bar")
+ frame = Frame(root)
+ text = Text(frame)
+ text.pack()
+ msb = MultiStatusBar(frame)
+ msb.set_label("one", "hello")
+ msb.set_label("two", "world")
+ msb.pack(side=BOTTOM, fill=X)
+
+ def change():
+ msb.set_label("one", "foo")
+ msb.set_label("two", "bar")
+
+ button = Button(root, text="Update status", command=change)
+ button.pack(side=BOTTOM)
+ frame.pack()
+ frame.mainloop()
+ root.mainloop()
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_multistatus_bar)
diff --git a/lib/python2.7/idlelib/NEWS.txt b/lib/python2.7/idlelib/NEWS.txt
new file mode 100644
index 0000000..35e2a7e
--- /dev/null
+++ b/lib/python2.7/idlelib/NEWS.txt
@@ -0,0 +1,1165 @@
+What's New in IDLE 2.7.13?
+==========================
+*Release date: 2017-01-01?*
+
+- Issue #27854: Make Help => IDLE Help work again on Windows.
+ Include idlelib/help.html in 2.7 Windows installer.
+
+- Issue #25507: Add back import needed for 2.x encoding warning box.
+ Add pointer to 'Encoding declaration' in Language Reference.
+
+- Issue #15308: Add 'interrupt execution' (^C) to Shell menu.
+ Patch by Roger Serwy, updated by Bayard Randel.
+
+- Issue #27922: Stop IDLE tests from 'flashing' gui widgets on the screen.
+
+- Issue #17642: add larger font sizes for classroom projection.
+
+- Add version to title of IDLE help window.
+
+- Issue #25564: In section on IDLE -- console differences, mention that
+ using exec means that __builtins__ is defined for each statement.
+
+- Issue #27714: text_textview and test_autocomplete now pass when re-run
+ in the same process. This occurs when test_idle fails when run with the
+ -w option but without -jn. Fix warning from test_config.
+
+- Issue #27452: add line counter and crc to IDLE configHandler test dump.
+
+- Issue #27365: Allow non-ascii chars in IDLE NEWS.txt, for contributor names.
+
+- Issue #27245: IDLE: Cleanly delete custom themes and key bindings.
+ Previously, when IDLE was started from a console or by import, a cascade
+ of warnings was emitted. Patch by Serhiy Storchaka.
+
+
+What's New in IDLE 2.7.12?
+==========================
+*Release date: 2015-06-25*
+
+- Issue #5124: Paste with text selected now replaces the selection on X11.
+ This matches how paste works on Windows, Mac, most modern Linux apps,
+ and ttk widgets. Original patch by Serhiy Storchaka.
+
+- Issue #24759: Make clear in idlelib.idle_test.__init__ that the directory
+ is a private implementation of test.test_idle and tool for maintainers.
+
+- Issue #26673: When tk reports font size as 0, change to size 10.
+ Such fonts on Linux prevented the configuration dialog from opening.
+
+- Issue #27044: Add ConfigDialog.remove_var_callbacks to stop memory leaks.
+
+- In the 'IDLE-console differences' section of the IDLE doc, clarify
+ how running with IDLE affects sys.modules and the standard streams.
+
+- Issue #25507: fix incorrect change in IOBinding that prevented printing.
+ Change also prevented saving shell window with non-ascii characters.
+ Augment IOBinding htest to include all major IOBinding functions.
+
+- Issue #25905: Revert unwanted conversion of ' to ’ RIGHT SINGLE QUOTATION
+ MARK in README.txt and open this and NEWS.txt with 'ascii'.
+ Re-encode CREDITS.txt to utf-8 and open it with 'utf-8'.
+
+- Issue #26417: Prevent spurious errors and incorrect defaults when
+ installing IDLE 2.7 on OS X: default configuration settings are
+ no longer installed from OS X specific copies.
+
+
+What's New in IDLE 2.7.11?
+==========================
+*Release date: 2015-12-06*
+
+- Issue 15348: Stop the debugger engine (normally in a user process)
+ before closing the debugger window (running in the IDLE process).
+ This prevents the RuntimeErrors that were being caught and ignored.
+
+- Issue #24455: Prevent IDLE from hanging when a) closing the shell while the
+ debugger is active (15347); b) closing the debugger with the [X] button
+ (15348); and c) activating the debugger when already active (24455).
+ The patch by Mark Roseman does this by making two changes.
+ 1. Suspend and resume the gui.interaction method with the tcl vwait
+ mechanism intended for this purpose (instead of root.mainloop & .quit).
+ 2. In gui.run, allow any existing interaction to terminate first.
+
+- Change 'The program' to 'Your program' in an IDLE 'kill program?' message
+ to make it clearer that the program referred to is the currently running
+ user program, not IDLE itself.
+
+- Issue #24750: Improve the appearance of the IDLE editor window status bar.
+ Patch by Mark Roseman.
+
+- Issue #25313: Change the handling of new built-in text color themes to better
+ address the compatibility problem introduced by the addition of IDLE Dark.
+ Consistently use the revised idleConf.CurrentTheme everywhere in idlelib.
+
+- Issue #24782: Extension configuration is now a tab in the IDLE Preferences
+ dialog rather than a separate dialog. The former tabs are now a sorted
+ list. Patch by Mark Roseman.
+
+- Issue #22726: Re-activate the config dialog help button with some content
+ about the other buttons and the new IDLE Dark theme.
+
+- Issue #24820: IDLE now has an 'IDLE Dark' built-in text color theme.
+ It is more or less IDLE Classic inverted, with a cobalt blue background.
+ Strings, comments, keywords, ... are still green, red, orange, ... .
+ To use it with IDLEs released before November 2015, hit the
+ 'Save as New Custom Theme' button and enter a new name,
+ such as 'Custom Dark'. The custom theme will work with any IDLE
+ release, and can be modified.
+
+- Issue #25224: README.txt is now an idlelib index for IDLE developers and
+ curious users. The previous user content is now in the IDLE doc chapter.
+ 'IDLE' now means 'Integrated Development and Learning Environment'.
+
+- Issue #24820: Users can now set breakpoint colors in
+ Settings -> Custom Highlighting. Original patch by Mark Roseman.
+
+- Issue #24972: Inactive selection background now matches active selection
+ background, as configured by users, on all systems. Found items are now
+ always highlighted on Windows. Initial patch by Mark Roseman.
+
+- Issue #24570: Idle: make calltip and completion boxes appear on Macs
+ affected by a tk regression. Initial patch by Mark Roseman.
+
+- Issue #24988: Idle ScrolledList context menus (used in debugger)
+ now work on Mac Aqua. Patch by Mark Roseman.
+
+- Issue #24801: Make right-click for context menu work on Mac Aqua.
+ Patch by Mark Roseman.
+
+- Issue #25173: Associate tkinter messageboxes with a specific widget.
+ For Mac OSX, make them a 'sheet'. Patch by Mark Roseman.
+
+- Issue #25198: Enhance the initial html viewer now used for Idle Help.
+ * Properly indent fixed-pitch text (patch by Mark Roseman).
+ * Give code snippet a very Sphinx-like light blueish-gray background.
+ * Re-use initial width and height set by users for shell and editor.
+ * When the Table of Contents (TOC) menu is used, put the section header
+ at the top of the screen.
+
+- Issue #25225: Condense and rewrite Idle doc section on text colors.
+
+- Issue #21995: Explain some differences between IDLE and console Python.
+
+- Issue #22820: Explain need for *print* when running file from Idle editor.
+
+- Issue #25224: Doc: augment Idle feature list and no-subprocess section.
+
+- Issue #25219: Update doc for Idle command line options.
+ Some were missing and notes were not correct.
+
+- Issue #24861: Most of idlelib is private and subject to change.
+ Use idleib.idle.* to start Idle. See idlelib.__init__.__doc__.
+
+- Issue #25199: Idle: add synchronization comments for future maintainers.
+
+- Issue #16893: Replace help.txt with help.html for Idle doc display.
+ The new idlelib/help.html is rstripped Doc/build/html/library/idle.html.
+ It looks better than help.txt and will better document Idle as released.
+ The tkinter html viewer that works for this file was written by Mark Roseman.
+ The now unused EditorWindow.HelpDialog class and helt.txt file are deprecated.
+
+- Issue #24199: Deprecate unused idlelib.idlever with possible removal in 3.6.
+
+- Issue #24790: Remove extraneous code (which also create 2 & 3 conflicts).
+
+- Issue #23672: Allow Idle to edit and run files with astral chars in name.
+ Patch by Mohd Sanad Zaki Rizvi.
+
+- Issue 24745: Idle editor default font. Switch from Courier to
+ platform-sensitive TkFixedFont. This should not affect current customized
+ font selections. If there is a problem, edit $HOME/.idlerc/config-main.cfg
+ and remove 'fontxxx' entries from [Editor Window]. Patch by Mark Roseman.
+
+- Issue #21192: Idle editor. When a file is run, put its name in the restart bar.
+ Do not print false prompts. Original patch by Adnan Umer.
+
+- Issue #13884: Idle menus. Remove tearoff lines. Patch by Roger Serwy.
+
+- Issue #15809: IDLE shell now uses locale encoding instead of Latin1 for
+ decoding unicode literals.
+
+
+What's New in IDLE 2.7.10?
+=========================
+*Release date: 2015-05-23*
+
+- Issue #23583: Fixed writing unicode to standard output stream in IDLE.
+
+- Issue #20577: Configuration of the max line length for the FormatParagraph
+ extension has been moved from the General tab of the Idle preferences dialog
+ to the FormatParagraph tab of the Config Extensions dialog.
+ Patch by Tal Einat.
+
+- Issue #16893: Update Idle doc chapter to match current Idle and add new
+ information.
+
+- Issue #23180: Rename IDLE "Windows" menu item to "Window".
+ Patch by Al Sweigart.
+
+
+What's New in IDLE 2.7.9?
+=========================
+*Release date: 2014-12-10*
+
+- Issue #16893: Update Idle doc chapter to match current Idle and add new
+ information.
+
+- Issue #3068: Add Idle extension configuration dialog to Options menu.
+ Changes are written to HOME/.idlerc/config-extensions.cfg.
+ Original patch by Tal Einat.
+
+- Issue #16233: A module browser (File : Class Browser, Alt+C) requires an
+ editor window with a filename. When Class Browser is requested otherwise,
+ from a shell, output window, or 'Untitled' editor, Idle no longer displays
+ an error box. It now pops up an Open Module box (Alt+M). If a valid name
+ is entered and a module is opened, a corresponding browser is also opened.
+
+- Issue #4832: Save As to type Python files automatically adds .py to the
+ name you enter (even if your system does not display it). Some systems
+ automatically add .txt when type is Text files.
+
+- Issue #21986: Code objects are not normally pickled by the pickle module.
+ To match this, they are no longer pickled when running under Idle.
+
+- Issue #22221: IDLE now ignores the source encoding declaration on the second
+ line if the first line contains anything except a comment.
+
+- Issue #17390: Adjust Editor window title; remove 'Python',
+ move version to end.
+
+- Issue #14105: Idle debugger breakpoints no longer disappear
+ when inserting or deleting lines.
+
+
+What's New in IDLE 2.7.8?
+=========================
+*Release date: 2014-06-29*
+
+- Issue #21940: Add unittest for WidgetRedirector. Initial patch by Saimadhav
+ Heblikar.
+
+- Issue #18592: Add unittest for SearchDialogBase. Patch by Phil Webster.
+
+- Issue #21694: Add unittest for ParenMatch. Patch by Saimadhav Heblikar.
+
+- Issue #21686: add unittest for HyperParser. Original patch by Saimadhav
+ Heblikar.
+
+- Issue #12387: Add missing upper(lower)case versions of default Windows key
+ bindings for Idle so Caps Lock does not disable them. Patch by Roger Serwy.
+
+- Issue #21695: Closing a Find-in-files output window while the search is
+ still in progress no longer closes Idle.
+
+- Issue #18910: Add unittest for textView. Patch by Phil Webster.
+
+- Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar.
+
+- Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster.
+
+
+What's New in IDLE 2.7.7?
+=========================
+*Release date: 2014-05-31*
+
+- Issue #18104: Add idlelib/idle_test/htest.py with a few sample tests to begin
+ consolidating and improving human-validated tests of Idle. Change other files
+ as needed to work with htest. Running the module as __main__ runs all tests.
+
+- Issue #21139: Change default paragraph width to 72, the PEP 8 recommendation.
+
+- Issue #21284: Paragraph reformat test passes after user changes reformat width.
+
+- Issue #20406: Use Python application icons for Idle window title bars.
+ Patch mostly by Serhiy Storchaka.
+
+- Issue #21029: Occurrences of "print" are now consistently colored as
+ being a keyword (the colorizer doesn't know if print functions are
+ enabled in the source).
+
+- Issue #17721: Remove non-functional configuration dialog help button until we
+ make it actually gives some help when clicked. Patch by Guilherme Simões.
+
+- Issue #17390: Add Python version to Idle editor window title bar.
+ Original patches by Edmond Burnett and Kent Johnson.
+
+- Issue #20058: sys.stdin.readline() in IDLE now always returns only one line.
+
+- Issue #19481: print() of unicode, str or bytearray subclass instance in IDLE
+ no more hangs.
+
+- Issue #18270: Prevent possible IDLE AttributeError on OS X when no initial
+ shell window is present.
+
+- Issue #17654: Ensure IDLE menus are customized properly on OS X for
+ non-framework builds and for all variants of Tk.
+
+
+What's New in IDLE 2.7.6?
+=========================
+*Release date: 2013-11-10*
+
+- Issue #19426: Fixed the opening of Python source file with specified encoding.
+
+- Issue #18873: IDLE now detects Python source code encoding only in comment
+ lines.
+
+- Issue #18988: The "Tab" key now works when a word is already autocompleted.
+
+- Issue #18489: Add tests for SearchEngine. Original patch by Phil Webster.
+
+- Issue #18429: Format / Format Paragraph, now works when comment blocks
+ are selected. As with text blocks, this works best when the selection
+ only includes complete lines.
+
+- Issue #18226: Add docstrings and unittests for FormatParagraph.py.
+ Original patches by Todd Rovito and Phil Webster.
+
+- Issue #18279: Format - Strip trailing whitespace no longer marks a file as
+ changed when it has not been changed. This fix followed the addition of a
+ test file originally written by Phil Webster (the issue's main goal).
+
+- Issue #18539: Calltips now work for float default arguments.
+
+- Issue #7136: In the Idle File menu, "New Window" is renamed "New File".
+ Patch by Tal Einat, Roget Serwy, and Todd Rovito.
+
+- Issue #8515: Set __file__ when run file in IDLE.
+ Initial patch by Bruce Frederiksen.
+
+- Issue #5492: Avoid traceback when exiting IDLE caused by a race condition.
+
+- Issue #17511: Keep IDLE find dialog open after clicking "Find Next".
+ Original patch by Sarah K.
+
+- Issue #15392: Create a unittest framework for IDLE.
+ Preliminary patch by Rajagopalasarma Jayakrishnan
+ See Lib/idlelib/idle_test/README.txt for how to run Idle tests.
+
+- Issue #14146: Highlight source line while debugging on Windows.
+
+- Issue #17532: Always include Options menu for IDLE on OS X.
+ Patch by Guilherme Simões.
+
+
+What's New in IDLE 2.7.5?
+=========================
+*Release date: 2013-05-12*
+
+- Issue #17838: Allow sys.stdin to be reassigned.
+
+- Issue #14735: Update IDLE docs to omit "Control-z on Windows".
+
+- Issue #17585: Fixed IDLE regression. Now closes when using exit() or quit().
+
+- Issue #17657: Show full Tk version in IDLE's about dialog.
+ Patch by Todd Rovito.
+
+- Issue #17613: Prevent traceback when removing syntax colorizer in IDLE.
+
+- Issue #1207589: Backwards-compatibility patch for right-click menu in IDLE.
+
+- Issue #16887: IDLE now accepts Cancel in tabify/untabify dialog box.
+
+- Issue #14254: IDLE now handles readline correctly across shell restarts.
+
+- Issue #17614: IDLE no longer raises exception when quickly closing a file.
+
+- Issue #6698: IDLE now opens just an editor window when configured to do so.
+
+- Issue #8900: Using keyboard shortcuts in IDLE to open a file no longer
+ raises an exception.
+
+- Issue #6649: Fixed missing exit status in IDLE. Patch by Guilherme Polo.
+
+- Issue #17390: Display Python version on Idle title bar.
+ Initial patch by Edmond Burnett.
+
+
+What's New in IDLE 2.7.4?
+=========================
+*Release date: 2013-04-06*
+
+- Issue #17625: In IDLE, close the replace dialog after it is used.
+
+- IDLE was displaying spurious SystemExit tracebacks when running scripts
+ that terminated by raising SystemExit (i.e. unittest and turtledemo).
+
+- Issue #9290: In IDLE the sys.std* streams now implement io.TextIOBase
+ interface and support all mandatory methods and properties.
+
+- Issue #16829: IDLE printing no longer fails if there are spaces or other
+ special characters in the file path.
+
+- Issue #16819: IDLE method completion now correctly works for unicode literals.
+
+- Issue #16504: IDLE now catches SyntaxErrors raised by tokenizer. Patch by
+ Roger Serwy.
+
+- Issue #1207589: Add Cut/Copy/Paste items to IDLE right click Context Menu
+ Patch by Todd Rovito.
+
+- Issue #13052: Fix IDLE crashing when replace string in Search/Replace dialog
+ ended with '\'. Patch by Roger Serwy.
+
+- Issue #9803: Don't close IDLE on saving if breakpoint is open.
+ Patch by Roger Serwy.
+
+- Issue #14958: Change IDLE systax highlighting to recognize all string and byte
+ literals currently supported in Python 2.7.
+
+- Issue #14962: Update text coloring in IDLE shell window after changing
+ options. Patch by Roger Serwy.
+
+- Issue #10997: Prevent a duplicate entry in IDLE's "Recent Files" menu.
+
+- Issue #12510: Attempting to get invalid tooltip no longer closes IDLE.
+ Original patch by Roger Serwy.
+
+- Issue #10365: File open dialog now works instead of crashing
+ even when parent window is closed. Patch by Roger Serwy.
+
+- Issue #14876: Use user-selected font for highlight configuration.
+ Patch by Roger Serwy.
+
+- Issue #14409: IDLE now properly executes commands in the Shell window
+ when it cannot read the normal config files on startup and
+ has to use the built-in default key bindings.
+ There was previously a bug in one of the defaults.
+
+- Issue #3573: IDLE hangs when passing invalid command line args
+ (directory(ies) instead of file(s)) (Patch by Guilherme Polo)
+
+- Issue #5219: Prevent event handler cascade in IDLE.
+
+- Issue #15318: Prevent writing to sys.stdin.
+
+- Issue #13532, #15319: Check that arguments to sys.stdout.write are strings.
+
+- Issue #10365: File open dialog now works instead of crashing even when
+ parent window is closed while dialog is open.
+
+- Issue #14018: Update checks for unstable system Tcl/Tk versions on OS X
+ to include versions shipped with OS X 10.7 and 10.8 in addition to 10.6.
+
+- Issue #15853: Prevent IDLE crash on OS X when opening Preferences menu
+ with certain versions of Tk 8.5. Initial patch by Kevin Walzer.
+
+
+What's New in IDLE 2.7.3?
+=========================
+*Release date: 2012-04-09*
+
+- Issue #964437 Make IDLE help window non-modal.
+ Patch by Guilherme Polo and Roger Serwy.
+
+- Issue #13933: IDLE auto-complete did not work with some imported
+ module, like hashlib. (Patch by Roger Serwy)
+
+- Issue #13506: Add '' to path for IDLE Shell when started and restarted with Restart Shell.
+ Original patches by Marco Scataglini and Roger Serwy.
+
+- Issue #4625: If IDLE cannot write to its recent file or breakpoint
+ files, display a message popup and continue rather than crash.
+ (original patch by Roger Serwy)
+
+- Issue #8793: Prevent IDLE crash when given strings with invalid hex escape
+ sequences.
+
+- Issue #13296: Fix IDLE to clear compile __future__ flags on shell restart.
+ (Patch by Roger Serwy)
+
+- Issue #14409: IDLE now properly executes commands in the Shell window
+ when it cannot read the normal config files on startup and
+ has to use the built-in default key bindings.
+ There was previously a bug in one of the defaults.
+
+- Issue #3573: IDLE hangs when passing invalid command line args
+ (directory(ies) instead of file(s)).
+
+
+What's New in IDLE 2.7.2?
+=========================
+*Release date: 2011-06-11*
+
+- Issue #11718: IDLE's open module dialog couldn't find the __init__.py
+ file in a package.
+
+- Issue #12590: IDLE editor window now always displays the first line
+ when opening a long file. With Tk 8.5, the first line was hidden.
+
+- Issue #11088: don't crash when using F5 to run a script in IDLE on MacOSX
+ with Tk 8.5.
+
+- Issue #10940: Workaround an IDLE hang on Mac OS X 10.6 when using the
+ menu accelerators for Open Module, Go to Line, and New Indent Width.
+ The accelerators still work but no longer appear in the menu items.
+
+- Issue #10907: Warn OS X 10.6 IDLE users to use ActiveState Tcl/Tk 8.5, rather
+ than the currently problematic Apple-supplied one, when running with the
+ 64-/32-bit installer variant.
+
+- Issue #11052: Correct IDLE menu accelerators on Mac OS X for Save
+ commands.
+
+- Issue #6075: IDLE on Mac OS X now works with both Carbon AquaTk and
+ Cocoa AquaTk.
+
+- Issue #10404: Use ctl-button-1 on OSX for the context menu in Idle.
+
+- Issue #10107: Warn about unsaved files in IDLE on OSX.
+
+- Issue #10406: Enable Rstrip IDLE extension on OSX (just like on other
+ platforms).
+
+- Issue #6378: Further adjust idle.bat to start associated Python
+
+- Issue #11896: Save on Close failed despite selecting "Yes" in dialog.
+
+- Issue #4676: <Home> toggle failing on Tk 8.5, causing IDLE exits and
+ strange selection behavior. Improve selection extension behaviour.
+
+- Issue #3851 <Home> toggle non-functional when NumLock set on Windows.
+
+
+What's New in Python 2.7.1?
+===========================
+*Release date: 2010-11-27*
+
+- Issue #6378: idle.bat now runs with the appropriate Python version rather than
+ the system default. Patch by Sridhar Ratnakumar.
+
+
+What's New in IDLE 2.7?
+=======================
+*Release date: 2010-07-03*
+
+- Issue #5150: IDLE's format menu now has an option to strip trailing
+ whitespace.
+
+- Issue #5847: Remove -n switch on "Edit with IDLE" menu item.
+
+- idle.py modified and simplified to better support developing experimental
+ versions of IDLE which are not installed in the standard location.
+
+- Issue #5559: OutputWindow/PyShell right click menu "Go to file/line"
+ wasn't working with file paths containing spaces.
+
+- Issue #5783: Windows: Version string for the .chm help file changed,
+ file not being accessed Patch by Guilherme Polo/
+
+- Issue #1529142: Allow multiple IDLE GUI/subprocess pairs to exist
+ simultaneously. Thanks to David Scherer for suggesting the use of an
+ ephemeral port for the GUI. Patch by Weeble.
+
+- Remove port spec from run.py and fix bug where subprocess fails to
+ extract port from command line when warnings are present.
+
+- Issue #5129: Tk 8.5 Text widget requires 'wordprocessor' tabstyle attr
+ to handle mixed space/tab properly. Patch by Guilherme Polo.
+
+- Issue #3549: On MacOS the preferences menu was not present
+
+
+What's New in IDLE 2.6?
+=======================
+*Release date: 01-Oct-2008*
+
+- 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.
+
+- Improved AutoCompleteWindow logic. Patch 2062 Tal Einat.
+
+- Autocompletion of filenames now support alternate separators, e.g. the
+ '/' char on Windows. Patch 2061 Tal Einat.
+
+- 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.
+
+- There was an error on exit if no sys.exitfunc was defined. Issue 1647.
+
+- Could not open files in .idlerc directory if latter was hidden on Windows.
+ Issue 1743, Issue 1862.
+
+- Configure Dialog: improved layout for keybinding. Patch 1457 Tal Einat.
+
+- tabpage.py updated: tabbedPages.py now supports multiple dynamic rows
+ of tabs. Patch 1612746 Tal Einat.
+
+- Add confirmation dialog before printing. Patch 1717170 Tal Einat.
+
+- Show paste position if > 80 col. Patch 1659326 Tal Einat.
+
+- Update cursor color without restarting. Patch 1725576 Tal Einat.
+
+- Allow keyboard interrupt only when user code is executing in subprocess.
+ Patch 1225 Tal Einat (reworked from IDLE-Spoon).
+
+- configDialog cleanup. Patch 1730217 Tal Einat.
+
+- textView cleanup. Patch 1718043 Tal Einat.
+
+- Clean up EditorWindow close.
+
+- Patch 1693258: Fix for duplicate "preferences" menu-OS X. Backport of r56204.
+
+- OSX: Avoid crash for those versions of Tcl/Tk which don't have a console
+
+- Bug in idlelib.MultiCall: Options dialog was crashing IDLE if there was an
+ option in config-extensions w/o a value. Patch #1672481, Tal Einat
+
+- Corrected some bugs in AutoComplete. Also, Page Up/Down in ACW implemented;
+ mouse and cursor selection in ACWindow implemented; double Tab inserts
+ current selection and closes ACW (similar to double-click and Return); scroll
+ wheel now works in ACW. Added AutoComplete instructions to IDLE Help.
+
+- AutoCompleteWindow moved below input line, will move above if there
+ isn't enough space. Patch 1621265 Tal Einat
+
+- Calltips now 'handle' tuples in the argument list (display '<tuple>' :)
+ Suggested solution by Christos Georgiou, Bug 791968.
+
+- Add 'raw' support to configHandler. Patch 1650174 Tal Einat.
+
+- Avoid hang when encountering a duplicate in a completion list. Bug 1571112.
+
+- Patch #1362975: Rework CodeContext indentation algorithm to
+ avoid hard-coding pixel widths.
+
+- Bug #813342: Start the IDLE subprocess with -Qnew if the parent
+ is started with that option.
+
+- Honor the "Cancel" action in the save dialog (Debian bug #299092)
+
+- Some syntax errors were being caught by tokenize during the tabnanny
+ check, resulting in obscure error messages. Do the syntax check
+ first. Bug 1562716, 1562719
+
+- IDLE's version number takes a big jump to match the version number of
+ the Python release of which it's a part.
+
+
+What's New in IDLE 1.2?
+=======================
+*Release date: 19-SEP-2006*
+
+- File menu hotkeys: there were three 'p' assignments. Reassign the
+ 'Save Copy As' and 'Print' hotkeys to 'y' and 't'. Change the
+ Shell hotkey from 's' to 'l'.
+
+- IDLE honors new quit() and exit() commands from site.py Quitter() object.
+ Patch 1540892, Jim Jewett
+
+- The 'with' statement is now a Code Context block opener.
+ Patch 1540851, Jim Jewett
+
+- Retrieval of previous shell command was not always preserving indentation
+ (since 1.2a1) Patch 1528468 Tal Einat.
+
+- Changing tokenize (39046) to detect dedent broke tabnanny check (since 1.2a1)
+
+- ToggleTab dialog was setting indent to 8 even if cancelled (since 1.2a1).
+
+- When used w/o subprocess, all exceptions were preceded by an error
+ message claiming they were IDLE internal errors (since 1.2a1).
+
+- Bug #1525817: Don't truncate short lines in IDLE's tool tips.
+
+- Bug #1517990: IDLE keybindings on MacOS X now work correctly
+
+- Bug #1517996: IDLE now longer shows the default Tk menu when a
+ path browser, class browser or debugger is the frontmost window on MacOS X
+
+- EditorWindow.test() was failing. Bug 1417598
+
+- EditorWindow failed when used stand-alone if sys.ps1 not set.
+ Bug 1010370 Dave Florek
+
+- Tooltips failed on new-syle class __init__ args. Bug 1027566 Loren Guthrie
+
+- Avoid occasional failure to detect closing paren properly.
+ Patch 1407280 Tal Einat
+
+- Rebinding Tab key was inserting 'tab' instead of 'Tab'. Bug 1179168.
+
+- Colorizer now handles #<builtin> correctly, also unicode strings and
+ 'as' keyword in comment directly following import command. Closes 1325071.
+ Patch 1479219 Tal Einat
+
+- Patch #1162825: Support non-ASCII characters in IDLE window titles.
+
+- Source file f.flush() after writing; trying to avoid lossage if user
+ kills GUI.
+
+- Options / Keys / Advanced dialog made functional. Also, allow binding
+ of 'movement' keys.
+
+- 'syntax' patch adds improved calltips and a new class attribute listbox.
+ MultiCall module allows binding multiple actions to an event.
+ Patch 906702 Noam Raphael
+
+- Better indentation after first line of string continuation.
+ IDLEfork Patch 681992, Noam Raphael
+
+- Fixed CodeContext alignment problem, following suggestion from Tal Einat.
+
+- Increased performance in CodeContext extension Patch 936169 Noam Raphael
+
+- Mac line endings were incorrect when pasting code from some browsers
+ when using X11 and the Fink distribution. Python Bug 1263656.
+
+- <Enter> when cursor is on a previous command retrieves that command. Instead
+ of replacing the input line, the previous command is now appended to the
+ input line. Indentation is preserved, and undo is enabled.
+ Patch 1196917 Jeff Shute
+
+- Clarify "tab/space" Error Dialog and "Tab Width" Dialog associated with
+ the Untabify command.
+
+- Corrected "tab/space" Error Dialog to show correct menu for Untabify.
+ Patch 1196980 Jeff Shute
+
+- New files are colorized by default, and colorizing is removed when
+ saving as non-Python files. Patch 1196895 Jeff Shute
+ Closes Python Bugs 775012 and 800432, partial fix IDLEfork 763524
+
+- Improve subprocess link error notification.
+
+- run.py: use Queue's blocking feature instead of sleeping in the main
+ loop. Patch # 1190163 Michiel de Hoon
+
+- Add config-main option to make the 'history' feature non-cyclic.
+ Default remains cyclic. Python Patch 914546 Noam Raphael.
+
+- Removed ability to configure tabs indent from Options dialog. This 'feature'
+ has never worked and no one has complained. It is still possible to set a
+ default tabs (v. spaces) indent 'manually' via config-main.def (or to turn on
+ tabs for the current EditorWindow via the Format menu) but IDLE will
+ encourage indentation via spaces.
+
+- Enable setting the indentation width using the Options dialog.
+ Bug # 783877
+
+- Add keybindings for del-word-left and del-word-right.
+
+- Discourage using an indent width other than 8 when using tabs to indent
+ Python code.
+
+- Restore use of EditorWindow.set_indentation_params(), was dead code since
+ Autoindent was merged into EditorWindow. This allows IDLE to conform to the
+ indentation width of a loaded file. (But it still will not switch to tabs
+ even if the file uses tabs.) Any change in indent width is local to that
+ window.
+
+- Add Tabnanny check before Run/F5, not just when Checking module.
+
+- If an extension can't be loaded, print warning and skip it instead of
+ erroring out.
+
+- Improve error handling when .idlerc can't be created (warn and exit).
+
+- The GUI was hanging if the shell window was closed while a raw_input()
+ was pending. Restored the quit() of the readline() mainloop().
+ http://mail.python.org/pipermail/idle-dev/2004-December/002307.html
+
+- The remote procedure call module rpc.py can now access data attributes of
+ remote registered objects. Changes to these attributes are local, however.
+
+
+What's New in IDLE 1.1?
+=======================
+*Release date: 30-NOV-2004*
+
+- On OpenBSD, terminating IDLE with ctrl-c from the command line caused a
+ stuck subprocess MainThread because only the SocketThread was exiting.
+
+- Saving a Keyset w/o making changes (by using the "Save as New Custom Key Set"
+ button) caused IDLE to fail on restart (no new keyset was created in
+ config-keys.cfg). Also true for Theme/highlights. Python Bug 1064535.
+
+- A change to the linecache.py API caused IDLE to exit when an exception was
+ raised while running without the subprocess (-n switch). Python Bug 1063840.
+
+- When paragraph reformat width was made configurable, a bug was
+ introduced that caused reformatting of comment blocks to ignore how
+ far the block was indented, effectively adding the indentation width
+ to the reformat width. This has been repaired, and the reformat
+ width is again a bound on the total width of reformatted lines.
+
+- Improve keyboard focus binding, especially in Windows menu. Improve
+ window raising, especially in the Windows menu and in the debugger.
+ IDLEfork 763524.
+
+- If user passes a non-existent filename on the commandline, just
+ open a new file, don't raise a dialog. IDLEfork 854928.
+
+- EditorWindow.py was not finding the .chm help file on Windows. Typo
+ at Rev 1.54. Python Bug 990954
+
+- checking sys.platform for substring 'win' was breaking IDLE docs on Mac
+ (darwin). Also, Mac Safari browser requires full file:// URIs. SF 900580.
+
+- Redirect the warning stream to the shell during the ScriptBinding check of
+ user code and format the warning similarly to an exception for both that
+ check and for runtime warnings raised in the subprocess.
+
+- CodeContext hint pane visibility state is now persistent across sessions.
+ The pane no longer appears in the shell window. Added capability to limit
+ extensions to shell window or editor windows. Noam Raphael addition
+ to Patch 936169.
+
+- Paragraph reformat width is now a configurable parameter in the
+ Options GUI.
+
+- New Extension: CodeContext. Provides block structuring hints for code
+ which has scrolled above an edit window. Patch 936169 Noam Raphael.
+
+- If nulls somehow got into the strings in recent-files.lst
+ EditorWindow.update_recent_files_list() was failing. Python Bug 931336.
+
+- If the normal background is changed via Configure/Highlighting, it will
+ update immediately, thanks to the previously mentioned patch by Nigel Rowe.
+
+- Add a highlight theme for builtin keywords. Python Patch 805830 Nigel Rowe
+ This also fixed IDLEfork bug [ 693418 ] Normal text background color not
+ refreshed and Python bug [897872 ] Unknown color name on HP-UX
+
+- rpc.py:SocketIO - Large modules were generating large pickles when downloaded
+ to the execution server. The return of the OK response from the subprocess
+ initialization was interfering and causing the sending socket to be not
+ ready. Add an IO ready test to fix this. Moved the polling IO ready test
+ into pollpacket().
+
+- Fix typo in rpc.py, s/b "pickle.PicklingError" not "pickle.UnpicklingError".
+
+- Added a Tk error dialog to run.py inform the user if the subprocess can't
+ connect to the user GUI process. Added a timeout to the GUI's listening
+ socket. Added Tk error dialogs to PyShell.py to announce a failure to bind
+ the port or connect to the subprocess. Clean up error handling during
+ connection initiation phase. This is an update of Python Patch 778323.
+
+- Print correct exception even if source file changed since shell was
+ restarted. IDLEfork Patch 869012 Noam Raphael
+
+- Keybindings with the Shift modifier now work correctly. So do bindings which
+ use the Space key. Limit unmodified user keybindings to the function keys.
+ Python Bug 775353, IDLEfork Bugs 755647, 761557
+
+- After an exception, run.py was not setting the exception vector. Noam
+ Raphael suggested correcting this so pdb's postmortem pm() would work.
+ IDLEfork Patch 844675
+
+- IDLE now does not fail to save the file anymore if the Tk buffer is not a
+ Unicode string, yet eol_convention is. Python Bugs 774680, 788378
+
+- IDLE didn't start correctly when Python was installed in "Program Files" on
+ W2K and XP. Python Bugs 780451, 784183
+
+- config-main.def documentation incorrectly referred to idle- instead of
+ config- filenames. SF 782759 Also added note about .idlerc location.
+
+
+What's New in IDLE 1.0?
+=======================
+*Release date: 29-Jul-2003*
+
+- Calltip error when docstring was None Python Bug 775541
+
+- Updated extend.txt, help.txt, and config-extensions.def to correctly
+ reflect the current status of the configuration system. Python Bug 768469
+
+- Fixed: Call Tip Trimming May Loop Forever. Python Patch 769142 (Daniels)
+
+- Replaced apply(f, args, kwds) with f(*args, **kwargs) to improve performance
+ Python Patch 768187
+
+- Break or continue statements outside a loop were causing IDLE crash
+ Python Bug 767794
+
+- Convert Unicode strings from readline to IOBinding.encoding. Also set
+ sys.std{in|out|err}.encoding, for both the local and the subprocess case.
+ SF IDLEfork patch 682347.
+
+- Extend AboutDialog.ViewFile() to support file encodings. Make the CREDITS
+ file Latin-1.
+
+- Updated the About dialog to reflect re-integration into Python. Provide
+ buttons to display Python's NEWS, License, and Credits, plus additional
+ buttons for IDLE's README and NEWS.
+
+- TextViewer() now has a third parameter which allows inserting text into the
+ viewer instead of reading from a file.
+
+- (Created the .../Lib/idlelib directory in the Python CVS, which is a clone of
+ IDLEfork modified to install in the Python environment. The code in the
+ interrupt module has been moved to thread.interrupt_main(). )
+
+- Printing the Shell window was failing if it was not saved first SF 748975
+
+- When using the Search in Files dialog, if the user had a selection
+ highlighted in his Editor window, insert it into the dialog search field.
+
+- The Python Shell entry was disappearing from the Windows menu.
+
+- Update the Windows file list when a file name change occurs
+
+- Change to File / Open Module: always pop up the dialog, using the current
+ selection as the default value. This is easier to use habitually.
+
+- Avoided a problem with starting the subprocess when 'localhost' doesn't
+ resolve to the user's loopback interface. SF 747772
+
+- Fixed an issue with highlighted errors never de-colorizing. SF 747677. Also
+ improved notification of Tabnanny Token Error.
+
+- File / New will by default save in the directory of the Edit window from
+ which it was initiated. SF 748973 Guido van Rossum patch.
+
+
+What's New in IDLEfork 0.9b1?
+=============================
+*Release date: 02-Jun-2003*
+
+- The current working directory of the execution environment (and shell
+ following completion of execution) is now that of the module being run.
+
+- Added the delete-exitfunc option to config-main.def. (This option is not
+ included in the Options dialog.) Setting this to True (the default) will
+ cause IDLE to not run sys.exitfunc/atexit when the subprocess exits.
+
+- IDLE now preserves the line ending codes when editing a file produced on
+ a different platform. SF 661759, SF 538584
+
+- Reduced default editor font size to 10 point and increased window height
+ to provide a better initial impression on Windows.
+
+- Options / Fonts/Tabs / Set Base Editor Font: List box was not highlighting
+ the default font when first installed on Windows. SF 661676
+
+- Added Autosave feature: when user runs code from edit window, if the file
+ has been modified IDLE will silently save it if Autosave is enabled. The
+ option is set in the Options dialog, and the default is to prompt the
+ user to save the file. SF 661318 Bruce Sherwood patch.
+
+- Improved the RESTART annotation in the shell window when the user restarts
+ the shell while it is generating output. Also improved annotation when user
+ repeatedly hammers the Ctrl-F6 restart.
+
+- Allow IDLE to run when not installed and cwd is not the IDLE directory
+ SF Patch 686254 "Run IDLEfork from any directory without set-up" - Raphael
+
+- When a module is run from an EditorWindow: if its directory is not in
+ sys.path, prepend it. This allows the module to import other modules in
+ the same directory. Do the same for a script run from the command line.
+
+- Correctly restart the subprocess if it is running user code and the user
+ attempts to run some other module or restarts the shell. Do the same if
+ the link is broken and it is possible to restart the subprocess and re-
+ connect to the GUI. SF RFE 661321.
+
+- Improved exception reporting when running commands or scripts from the
+ command line.
+
+- Added a -n command line switch to start IDLE without the subprocess.
+ Removed the Shell menu when running in that mode. Updated help messages.
+
+- Added a comment to the shell startup header to indicate when IDLE is not
+ using the subprocess.
+
+- Restore the ability to run without the subprocess. This can be important for
+ some platforms or configurations. (Running without the subprocess allows the
+ debugger to trace through parts of IDLE itself, which may or may not be
+ desirable, depending on your point of view. In addition, the traditional
+ reload/import tricks must be use if user source code is changed.) This is
+ helpful for developing IDLE using IDLE, because one instance can be used to
+ edit the code and a separate instance run to test changes. (Multiple
+ concurrent IDLE instances with subprocesses is a future feature)
+
+- Improve the error message a user gets when saving a file with non-ASCII
+ characters and no source encoding is specified. Done by adding a dialog
+ 'EncodingMessage', which contains the line to add in a fixed-font entry
+ widget, and which has a button to add that line to the file automatically.
+ Also, add a configuration option 'EditorWindow/encoding', which has three
+ possible values: none, utf-8, and locale. None is the default: IDLE will show
+ this dialog when non-ASCII characters are encountered. utf-8 means that files
+ with non-ASCII characters are saved as utf-8-with-bom. locale means that
+ files are saved in the locale's encoding; the dialog is only displayed if the
+ source contains characters outside the locale's charset. SF 710733 - Loewis
+
+- Improved I/O response by tweaking the wait parameter in various
+ calls to signal.signal().
+
+- Implemented a threaded subprocess which allows interrupting a pass
+ loop in user code using the 'interrupt' extension. User code runs
+ in MainThread, while the RPCServer is handled by SockThread. This is
+ necessary because Windows doesn't support signals.
+
+- Implemented the 'interrupt' extension module, which allows a subthread
+ to raise a KeyboardInterrupt in the main thread.
+
+- Attempting to save the shell raised an error related to saving
+ breakpoints, which are not implemented in the shell
+
+- Provide a correct message when 'exit' or 'quit' are entered at the
+ IDLE command prompt SF 695861
+
+- Eliminate extra blank line in shell output caused by not flushing
+ stdout when user code ends with an unterminated print. SF 695861
+
+- Moved responsibility for exception formatting (i.e. pruning IDLE internal
+ calls) out of rpc.py into the client and server.
+
+- Exit IDLE cleanly even when doing subprocess I/O
+
+- Handle subprocess interrupt with an RPC message.
+
+- Restart the subprocess if it terminates itself. (VPython programs do that)
+
+- Support subclassing of exceptions, including in the shell, by moving the
+ exception formatting to the subprocess.
+
+
+What's New in IDLEfork 0.9 Alpha 2?
+===================================
+*Release date: 27-Jan-2003*
+
+- Updated INSTALL.txt to claify use of the python2 rpm.
+
+- Improved formatting in IDLE Help.
+
+- Run menu: Replace "Run Script" with "Run Module".
+
+- Code encountering an unhandled exception under the debugger now shows
+ the correct traceback, with IDLE internal levels pruned out.
+
+- If an exception occurs entirely in IDLE, don't prune the IDLE internal
+ modules from the traceback displayed.
+
+- Class Browser and Path Browser now use Alt-Key-2 for vertical zoom.
+
+- IDLE icons will now install correctly even when setup.py is run from the
+ build directory
+
+- Class Browser now compatible with Python2.3 version of pyclbr.py
+
+- Left cursor move in presence of selected text now moves from left end
+ of the selection.
+
+- Add Meta keybindings to "IDLE Classic Windows" to handle reversed
+ Alt/Meta on some Linux distros.
+
+- Change default: IDLE now starts with Python Shell.
+
+- Removed the File Path from the Additional Help Sources scrolled list.
+
+- Add capability to access Additional Help Sources on the web if the
+ Help File Path begins with //http or www. (Otherwise local path is
+ validated, as before.)
+
+- Additional Help Sources were not being posted on the Help menu in the
+ order entered. Implement sorting the list by [HelpFiles] 'option'
+ number.
+
+- Add Browse button to New Help Source dialog. Arrange to start in
+ Python/Doc if platform is Windows, otherwise start in current directory.
+
+- Put the Additional Help Sources directly on the Help menu instead of in
+ an Extra Help cascade menu. Rearrange the Help menu so the Additional
+ Help Sources come last. Update help.txt appropriately.
+
+- Fix Tk root pop-ups in configSectionNameDialog.py and configDialog.py
+
+- Uniform capitalization in General tab of ConfigDialog, update the doc string.
+
+- Fix bug in ConfigDialog where SaveAllChangedConfig() was unexpectedly
+ deleting Additional Help Sources from the user's config file.
+
+- Make configHelpSourceEdit OK button the default and bind <Return>
+
+- Fix Tk root pop-ups in configHelpSourceEdit: error dialogs not attached
+ to parents.
+
+- Use os.startfile() to open both Additional Help and Python Help on the
+ Windows platform. The application associated with the file type will act as
+ the viewer. Windows help files (.chm) are now supported via the
+ Settings/General/Additional Help facility.
+
+- If Python Help files are installed locally on Linux, use them instead of
+ accessing python.org.
+
+- Make the methods for finding the Python help docs more robust, and make
+ them work in the installed configuration, also.
+
+- On the Save Before Run dialog, make the OK button the default. One
+ less mouse action!
+
+- Add a method: EditorWindow.get_geometry() for future use in implementing
+ window location persistence.
+
+- Removed the "Help/Advice" menu entry. Thanks, David! We'll remember!
+
+- Change the "Classic Windows" theme's paste key to be <ctrl-v>.
+
+- Rearrange the Shell menu to put Stack Viewer entries adjacent.
+
+- Add the ability to restart the subprocess interpreter from the shell window;
+ add an associated menu entry "Shell/Restart" with binding Control-F6. Update
+ IDLE help.
+
+- Upon a restart, annotate the shell window with a "restart boundary". Add a
+ shell window menu "Shell/View Restart" with binding F6 to jump to the most
+ recent restart boundary.
+
+- Add Shell menu to Python Shell; change "Settings" to "Options".
+
+- Remove incorrect comment in setup.py: IDLEfork is now installed as a package.
+
+- Add INSTALL.txt, HISTORY.txt, NEWS.txt to installed configuration.
+
+- In installer text, fix reference to Visual Python, should be VPython.
+ Properly credit David Scherer.
+
+- Modified idle, idle.py, idle.pyw to improve exception handling.
+
+
+What's New in IDLEfork 0.9 Alpha 1?
+===================================
+*Release date: 31-Dec-2002*
+
+- First release of major new functionality. For further details refer to
+ Idle-dev and/or the Sourceforge CVS.
+
+- Adapted to the Mac platform.
+
+- Overhauled the IDLE startup options and revised the idle -h help message,
+ which provides details of command line usage.
+
+- Multiple bug fixes and usability enhancements.
+
+- Introduced the new RPC implementation, which includes a debugger. The output
+ of user code is to the shell, and the shell may be used to inspect the
+ environment after the run has finished. (In version 0.8.1 the shell
+ environment was separate from the environment of the user code.)
+
+- Introduced the configuration GUI and a new About dialog.
+
+- Removed David Scherer's Remote Procedure Call code and replaced with Guido
+ van Rossum's. GvR code has support for the IDLE debugger and uses the shell
+ to inspect the environment of code Run from an Edit window. Files removed:
+ ExecBinding.py, loader.py, protocol.py, Remote.py, spawn.py
+
+--------------------------------------------------------------------
+Refer to HISTORY.txt for additional information on earlier releases.
+--------------------------------------------------------------------
+
+
+
+
+
diff --git a/lib/python2.7/idlelib/ObjectBrowser.py b/lib/python2.7/idlelib/ObjectBrowser.py
new file mode 100644
index 0000000..e69365c
--- /dev/null
+++ b/lib/python2.7/idlelib/ObjectBrowser.py
@@ -0,0 +1,156 @@
+# XXX TO DO:
+# - popup menu
+# - support partial or total redisplay
+# - more doc strings
+# - tooltips
+
+# object browser
+
+# XXX TO DO:
+# - for classes/modules, add "open source" to object browser
+
+import re
+
+from idlelib.TreeWidget import TreeItem, TreeNode, ScrolledCanvas
+
+from repr import Repr
+
+myrepr = Repr()
+myrepr.maxstring = 100
+myrepr.maxother = 100
+
+class ObjectTreeItem(TreeItem):
+ def __init__(self, labeltext, object, setfunction=None):
+ self.labeltext = labeltext
+ self.object = object
+ self.setfunction = setfunction
+ def GetLabelText(self):
+ return self.labeltext
+ def GetText(self):
+ return myrepr.repr(self.object)
+ def GetIconName(self):
+ if not self.IsExpandable():
+ return "python"
+ def IsEditable(self):
+ return self.setfunction is not None
+ def SetText(self, text):
+ try:
+ value = eval(text)
+ self.setfunction(value)
+ except:
+ pass
+ else:
+ self.object = value
+ def IsExpandable(self):
+ return not not dir(self.object)
+ def GetSubList(self):
+ keys = dir(self.object)
+ sublist = []
+ for key in keys:
+ try:
+ value = getattr(self.object, key)
+ except AttributeError:
+ continue
+ item = make_objecttreeitem(
+ str(key) + " =",
+ value,
+ lambda value, key=key, object=self.object:
+ setattr(object, key, value))
+ 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
+ def GetSubList(self):
+ sublist = ObjectTreeItem.GetSubList(self)
+ if len(self.object.__bases__) == 1:
+ item = make_objecttreeitem("__bases__[0] =",
+ self.object.__bases__[0])
+ else:
+ item = make_objecttreeitem("__bases__ =", self.object.__bases__)
+ sublist.insert(0, item)
+ return sublist
+
+class AtomicObjectTreeItem(ObjectTreeItem):
+ def IsExpandable(self):
+ return 0
+
+class SequenceTreeItem(ObjectTreeItem):
+ def IsExpandable(self):
+ return len(self.object) > 0
+ def keys(self):
+ return range(len(self.object))
+ def GetSubList(self):
+ sublist = []
+ for key in self.keys():
+ try:
+ value = self.object[key]
+ except KeyError:
+ continue
+ def setfunction(value, key=key, object=self.object):
+ object[key] = value
+ item = make_objecttreeitem("%r:" % (key,), value, setfunction)
+ sublist.append(item)
+ return sublist
+
+class DictTreeItem(SequenceTreeItem):
+ def keys(self):
+ keys = 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,
+}
+
+def make_objecttreeitem(labeltext, object, setfunction=None):
+ t = type(object)
+ if t in dispatch:
+ c = dispatch[t]
+ else:
+ c = ObjectTreeItem
+ return c(labeltext, object, setfunction)
+
+
+def _object_browser(parent):
+ import sys
+ from Tkinter import Tk
+ root = Tk()
+ root.title("Test ObjectBrowser")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ root.configure(bd=0, bg="yellow")
+ root.focus_set()
+ sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1)
+ sc.frame.pack(expand=1, fill="both")
+ item = make_objecttreeitem("sys", sys)
+ node = TreeNode(sc.canvas, None, item)
+ node.update()
+ root.mainloop()
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_object_browser)
diff --git a/lib/python2.7/idlelib/OutputWindow.py b/lib/python2.7/idlelib/OutputWindow.py
new file mode 100644
index 0000000..63dc737
--- /dev/null
+++ b/lib/python2.7/idlelib/OutputWindow.py
@@ -0,0 +1,149 @@
+from Tkinter import *
+from idlelib.EditorWindow import EditorWindow
+import re
+import tkMessageBox
+from idlelib import IOBinding
+
+class OutputWindow(EditorWindow):
+
+ """An editor window that can serve as an output file.
+
+ Also the future base class for the Python shell window.
+ This class has no input facilities.
+ """
+
+ def __init__(self, *args):
+ EditorWindow.__init__(self, *args)
+ self.text.bind("<<goto-file-line>>", self.goto_file_line)
+
+ # Customize EditorWindow
+
+ def ispythonsource(self, filename):
+ # No colorization needed
+ return 0
+
+ def short_title(self):
+ return "Output"
+
+ def maybesave(self):
+ # Override base class method -- don't ask any questions
+ if self.get_saved():
+ return "yes"
+ else:
+ return "no"
+
+ # 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
+ self.text.insert(mark, s, tags)
+ self.text.see(mark)
+ self.text.update()
+
+ def writelines(self, lines):
+ for line in lines:
+ self.write(line)
+
+ def flush(self):
+ pass
+
+ # Our own right-button menu
+
+ rmenu_specs = [
+ ("Cut", "<<cut>>", "rmenu_check_cut"),
+ ("Copy", "<<copy>>", "rmenu_check_copy"),
+ ("Paste", "<<paste>>", "rmenu_check_paste"),
+ (None, None, None),
+ ("Go to file/line", "<<goto-file-line>>", None),
+ ]
+
+ file_line_pats = [
+ # order of patterns matters
+ r'file "([^"]*)", line (\d+)',
+ r'([^\s]+)\((\d+)\)',
+ r'^(\s*\S.*?):\s*(\d+):', # Win filename, maybe starting with spaces
+ r'([^\s]+):\s*(\d+):', # filename or path, ltrim
+ r'^\s*(\S.*?):\s*(\d+):', # Win abs path with embedded spaces, ltrim
+ ]
+
+ file_line_progs = None
+
+ def goto_file_line(self, event=None):
+ if self.file_line_progs is None:
+ l = []
+ for pat in self.file_line_pats:
+ l.append(re.compile(pat, re.IGNORECASE))
+ self.file_line_progs = l
+ # x, y = self.event.x, self.event.y
+ # self.text.mark_set("insert", "@%d,%d" % (x, y))
+ line = self.text.get("insert linestart", "insert lineend")
+ result = self._file_line_helper(line)
+ if not result:
+ # Try the previous line. This is handy e.g. in tracebacks,
+ # where you tend to right-click on the displayed source line
+ line = self.text.get("insert -1line linestart",
+ "insert -1line lineend")
+ result = self._file_line_helper(line)
+ if not result:
+ tkMessageBox.showerror(
+ "No special line",
+ "The line you point at doesn't look like "
+ "a valid file name followed by a line number.",
+ parent=self.text)
+ return
+ filename, lineno = result
+ edit = self.flist.open(filename)
+ edit.gotoline(lineno)
+
+ def _file_line_helper(self, line):
+ for prog in self.file_line_progs:
+ match = prog.search(line)
+ if match:
+ filename, lineno = match.group(1, 2)
+ try:
+ f = open(filename, "r")
+ f.close()
+ break
+ except IOError:
+ continue
+ else:
+ return None
+ try:
+ return filename, int(lineno)
+ except TypeError:
+ return None
+
+# These classes are currently not used but might come in handy
+
+class OnDemandOutputWindow:
+
+ tagdefs = {
+ # XXX Should use IdlePrefs.ColorPrefs
+ "stdout": {"foreground": "blue"},
+ "stderr": {"foreground": "#007700"},
+ }
+
+ def __init__(self, flist):
+ self.flist = flist
+ self.owin = None
+
+ def write(self, s, tags, mark):
+ if not self.owin:
+ self.setup()
+ self.owin.write(s, tags, mark)
+
+ def setup(self):
+ self.owin = owin = OutputWindow(self.flist)
+ text = owin.text
+ for tag, cnf in self.tagdefs.items():
+ if cnf:
+ text.tag_configure(tag, **cnf)
+ text.tag_raise('sel')
+ self.write = self.owin.write
diff --git a/lib/python2.7/idlelib/ParenMatch.py b/lib/python2.7/idlelib/ParenMatch.py
new file mode 100644
index 0000000..47e10f3
--- /dev/null
+++ b/lib/python2.7/idlelib/ParenMatch.py
@@ -0,0 +1,178 @@
+"""ParenMatch -- An IDLE extension for parenthesis matching.
+
+When you hit a right paren, the cursor should move briefly to the left
+paren. Paren here is used generically; the matching applies to
+parentheses, square brackets, and curly braces.
+"""
+
+from idlelib.HyperParser import HyperParser
+from idlelib.configHandler import idleConf
+
+_openers = {')':'(',']':'[','}':'{'}
+CHECK_DELAY = 100 # milliseconds
+
+class ParenMatch:
+ """Highlight matching parentheses
+
+ There are three supported style of paren matching, based loosely
+ on the Emacs options. The style is select based on the
+ HILITE_STYLE attribute; it can be changed used the set_style
+ method.
+
+ The supported styles are:
+
+ default -- When a right paren is typed, highlight the matching
+ left paren for 1/2 sec.
+
+ expression -- When a right paren is typed, highlight the entire
+ expression from the left paren to the right paren.
+
+ TODO:
+ - extend IDLE with configuration dialog to change options
+ - implement rest of Emacs highlight styles (see below)
+ - print mismatch warning in IDLE status window
+
+ Note: In Emacs, there are several styles of highlight where the
+ matching paren is highlighted whenever the cursor is immediately
+ to the right of a right paren. I don't know how to do that in Tk,
+ so I haven't bothered.
+ """
+ menudefs = [
+ ('edit', [
+ ("Show surrounding parens", "<<flash-paren>>"),
+ ])
+ ]
+ STYLE = idleConf.GetOption('extensions','ParenMatch','style',
+ default='expression')
+ FLASH_DELAY = idleConf.GetOption('extensions','ParenMatch','flash-delay',
+ type='int',default=500)
+ HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(),'hilite')
+ BELL = idleConf.GetOption('extensions','ParenMatch','bell',
+ type='bool',default=1)
+
+ RESTORE_VIRTUAL_EVENT_NAME = "<<parenmatch-check-restore>>"
+ # We want the restore event be called before the usual return and
+ # backspace events.
+ RESTORE_SEQUENCES = ("<KeyPress>", "<ButtonPress>",
+ "<Key-Return>", "<Key-BackSpace>")
+
+ def __init__(self, editwin):
+ self.editwin = editwin
+ self.text = editwin.text
+ # Bind the check-restore event to the function restore_event,
+ # so that we can then use activate_restore (which calls event_add)
+ # and deactivate_restore (which calls event_delete).
+ editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME,
+ self.restore_event)
+ self.counter = 0
+ self.is_restore_active = 0
+ self.set_style(self.STYLE)
+
+ def activate_restore(self):
+ if not self.is_restore_active:
+ for seq in self.RESTORE_SEQUENCES:
+ self.text.event_add(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
+ self.is_restore_active = True
+
+ def deactivate_restore(self):
+ if self.is_restore_active:
+ for seq in self.RESTORE_SEQUENCES:
+ self.text.event_delete(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
+ self.is_restore_active = False
+
+ def set_style(self, style):
+ self.STYLE = style
+ if style == "default":
+ self.create_tag = self.create_tag_default
+ self.set_timeout = self.set_timeout_last
+ elif style == "expression":
+ self.create_tag = self.create_tag_expression
+ self.set_timeout = self.set_timeout_none
+
+ def flash_paren_event(self, event):
+ indices = (HyperParser(self.editwin, "insert")
+ .get_surrounding_brackets())
+ if indices is None:
+ self.warn_mismatched()
+ return
+ self.activate_restore()
+ self.create_tag(indices)
+ self.set_timeout_last()
+
+ def paren_closed_event(self, event):
+ # If it was a shortcut and not really a closing paren, quit.
+ closer = self.text.get("insert-1c")
+ if closer not in _openers:
+ return
+ hp = HyperParser(self.editwin, "insert-1c")
+ if not hp.is_in_code():
+ return
+ indices = hp.get_surrounding_brackets(_openers[closer], True)
+ if indices is None:
+ self.warn_mismatched()
+ return
+ self.activate_restore()
+ self.create_tag(indices)
+ self.set_timeout()
+
+ def restore_event(self, event=None):
+ self.text.tag_delete("paren")
+ self.deactivate_restore()
+ self.counter += 1 # disable the last timer, if there is one.
+
+ def handle_restore_timer(self, timer_count):
+ if timer_count == self.counter:
+ self.restore_event()
+
+ def warn_mismatched(self):
+ if self.BELL:
+ self.text.bell()
+
+ # any one of the create_tag_XXX methods can be used depending on
+ # the style
+
+ def create_tag_default(self, indices):
+ """Highlight the single paren that matches"""
+ self.text.tag_add("paren", indices[0])
+ self.text.tag_config("paren", self.HILITE_CONFIG)
+
+ def create_tag_expression(self, indices):
+ """Highlight the entire expression"""
+ if self.text.get(indices[1]) in (')', ']', '}'):
+ rightindex = indices[1]+"+1c"
+ else:
+ rightindex = indices[1]
+ self.text.tag_add("paren", indices[0], rightindex)
+ self.text.tag_config("paren", self.HILITE_CONFIG)
+
+ # any one of the set_timeout_XXX methods can be used depending on
+ # the style
+
+ def set_timeout_none(self):
+ """Highlight will remain until user input turns it off
+ or the insert has moved"""
+ # After CHECK_DELAY, call a function which disables the "paren" tag
+ # if the event is for the most recent timer and the insert has changed,
+ # or schedules another call for itself.
+ self.counter += 1
+ def callme(callme, self=self, c=self.counter,
+ index=self.text.index("insert")):
+ if index != self.text.index("insert"):
+ self.handle_restore_timer(c)
+ else:
+ self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
+ self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
+
+ def set_timeout_last(self):
+ """The last highlight created will be removed after .5 sec"""
+ # associate a counter with an event; only disable the "paren"
+ # tag if the event is for the most recent timer.
+ self.counter += 1
+ self.editwin.text_frame.after(
+ self.FLASH_DELAY,
+ lambda self=self, c=self.counter: self.handle_restore_timer(c))
+
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main('idlelib.idle_test.test_parenmatch', verbosity=2)
diff --git a/lib/python2.7/idlelib/PathBrowser.py b/lib/python2.7/idlelib/PathBrowser.py
new file mode 100644
index 0000000..ae26714
--- /dev/null
+++ b/lib/python2.7/idlelib/PathBrowser.py
@@ -0,0 +1,105 @@
+import os
+import sys
+import imp
+
+from idlelib.TreeWidget import TreeItem
+from idlelib.ClassBrowser import ClassBrowser, ModuleBrowserTreeItem
+from idlelib.PyShell import PyShellFileList
+
+
+class PathBrowser(ClassBrowser):
+
+ def __init__(self, flist, _htest=False):
+ """
+ _htest - bool, change box location when running htest
+ """
+ self._htest = _htest
+ self.init(flist)
+
+ def settitle(self):
+ "Set window titles."
+ self.top.wm_title("Path Browser")
+ self.top.wm_iconname("Path Browser")
+
+ def rootnode(self):
+ return PathBrowserTreeItem()
+
+class PathBrowserTreeItem(TreeItem):
+
+ def GetText(self):
+ return "sys.path"
+
+ def GetSubList(self):
+ sublist = []
+ for dir in sys.path:
+ item = DirBrowserTreeItem(dir)
+ sublist.append(item)
+ return sublist
+
+class DirBrowserTreeItem(TreeItem):
+
+ def __init__(self, dir, packages=[]):
+ self.dir = dir
+ self.packages = packages
+
+ def GetText(self):
+ if not self.packages:
+ return self.dir
+ else:
+ return self.packages[-1] + ": package"
+
+ def GetSubList(self):
+ try:
+ names = os.listdir(self.dir or os.curdir)
+ except os.error:
+ return []
+ packages = []
+ for name in names:
+ file = os.path.join(self.dir, name)
+ if self.ispackagedir(file):
+ nn = os.path.normcase(name)
+ packages.append((nn, name, file))
+ packages.sort()
+ sublist = []
+ for nn, name, file in packages:
+ item = DirBrowserTreeItem(file, self.packages + [name])
+ sublist.append(item)
+ for nn, name in self.listmodules(names):
+ item = ModuleBrowserTreeItem(os.path.join(self.dir, name))
+ sublist.append(item)
+ return sublist
+
+ def ispackagedir(self, file):
+ if not os.path.isdir(file):
+ return False
+ init = os.path.join(file, "__init__.py")
+ return os.path.exists(init)
+
+ def listmodules(self, allnames):
+ modules = {}
+ suffixes = imp.get_suffixes()
+ sorted = []
+ for suff, mode, flag in suffixes:
+ i = -len(suff)
+ for name in allnames[:]:
+ normed_name = os.path.normcase(name)
+ if normed_name[i:] == suff:
+ mod_name = name[:i]
+ if mod_name not in modules:
+ modules[mod_name] = None
+ sorted.append((normed_name, name))
+ allnames.remove(name)
+ sorted.sort()
+ return sorted
+
+def _path_browser(parent): # htest #
+ flist = PyShellFileList(parent)
+ PathBrowser(flist, _htest=True)
+ parent.mainloop()
+
+if __name__ == "__main__":
+ from unittest import main
+ main('idlelib.idle_test.test_pathbrowser', verbosity=2, exit=False)
+
+ from idlelib.idle_test.htest import run
+ run(_path_browser)
diff --git a/lib/python2.7/idlelib/Percolator.py b/lib/python2.7/idlelib/Percolator.py
new file mode 100644
index 0000000..e0e8cad
--- /dev/null
+++ b/lib/python2.7/idlelib/Percolator.py
@@ -0,0 +1,103 @@
+from idlelib.WidgetRedirector import WidgetRedirector
+from idlelib.Delegator import Delegator
+
+class Percolator:
+
+ def __init__(self, text):
+ # XXX would be nice to inherit from Delegator
+ self.text = text
+ self.redir = WidgetRedirector(text)
+ self.top = self.bottom = Delegator(text)
+ self.bottom.insert = self.redir.register("insert", self.insert)
+ self.bottom.delete = self.redir.register("delete", self.delete)
+ self.filters = []
+
+ def close(self):
+ while self.top is not self.bottom:
+ self.removefilter(self.top)
+ self.top = None
+ self.bottom.setdelegate(None); self.bottom = None
+ self.redir.close(); self.redir = None
+ self.text = None
+
+ def insert(self, index, chars, tags=None):
+ # Could go away if inheriting from Delegator
+ self.top.insert(index, chars, tags)
+
+ def delete(self, index1, index2=None):
+ # Could go away if inheriting from Delegator
+ self.top.delete(index1, index2)
+
+ def insertfilter(self, filter):
+ # Perhaps rename to pushfilter()?
+ assert isinstance(filter, Delegator)
+ assert filter.delegate is None
+ filter.setdelegate(self.top)
+ self.top = filter
+
+ def removefilter(self, filter):
+ # XXX Perhaps should only support popfilter()?
+ assert isinstance(filter, Delegator)
+ assert filter.delegate is not None
+ f = self.top
+ if f is filter:
+ self.top = filter.delegate
+ filter.setdelegate(None)
+ else:
+ while f.delegate is not filter:
+ assert f is not self.bottom
+ f.resetcache()
+ f = f.delegate
+ f.setdelegate(filter.delegate)
+ filter.setdelegate(None)
+
+
+def _percolator(parent):
+ import Tkinter as tk
+ import re
+ class Tracer(Delegator):
+ def __init__(self, name):
+ self.name = name
+ Delegator.__init__(self, None)
+ def insert(self, *args):
+ print self.name, ": insert", args
+ self.delegate.insert(*args)
+ def delete(self, *args):
+ print self.name, ": delete", args
+ self.delegate.delete(*args)
+ root = tk.Tk()
+ root.title("Test Percolator")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ text = tk.Text(root)
+ p = Percolator(text)
+ t1 = Tracer("t1")
+ t2 = Tracer("t2")
+
+ def toggle1():
+ if var1.get() == 0:
+ var1.set(1)
+ p.insertfilter(t1)
+ elif var1.get() == 1:
+ var1.set(0)
+ p.removefilter(t1)
+
+ def toggle2():
+ if var2.get() == 0:
+ var2.set(1)
+ p.insertfilter(t2)
+ elif var2.get() == 1:
+ var2.set(0)
+ p.removefilter(t2)
+
+ text.pack()
+ var1 = tk.IntVar()
+ cb1 = tk.Checkbutton(root, text="Tracer1", command=toggle1, variable=var1)
+ cb1.pack()
+ var2 = tk.IntVar()
+ cb2 = tk.Checkbutton(root, text="Tracer2", command=toggle2, variable=var2)
+ cb2.pack()
+
+if __name__ == "__main__":
+ from idlelib.idle_test.htest import run
+ run(_percolator)
diff --git a/lib/python2.7/idlelib/PyParse.py b/lib/python2.7/idlelib/PyParse.py
new file mode 100644
index 0000000..1a9db67
--- /dev/null
+++ b/lib/python2.7/idlelib/PyParse.py
@@ -0,0 +1,594 @@
+import re
+import sys
+
+# Reason last stmt is continued (or C_NONE if it's not).
+(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE,
+ C_STRING_NEXT_LINES, C_BRACKET) = range(5)
+
+if 0: # for throwaway debugging output
+ def dump(*stuff):
+ sys.__stdout__.write(" ".join(map(str, stuff)) + "\n")
+
+# Find what looks like the start of a popular stmt.
+
+_synchre = re.compile(r"""
+ ^
+ [ \t]*
+ (?: while
+ | else
+ | def
+ | return
+ | assert
+ | break
+ | class
+ | continue
+ | elif
+ | try
+ | except
+ | raise
+ | import
+ | yield
+ )
+ \b
+""", re.VERBOSE | re.MULTILINE).search
+
+# Match blank line or non-indenting comment line.
+
+_junkre = re.compile(r"""
+ [ \t]*
+ (?: \# \S .* )?
+ \n
+""", re.VERBOSE).match
+
+# Match any flavor of string; the terminating quote is optional
+# so that we're robust in the face of incomplete program text.
+
+_match_stringre = re.compile(r"""
+ \""" [^"\\]* (?:
+ (?: \\. | "(?!"") )
+ [^"\\]*
+ )*
+ (?: \""" )?
+
+| " [^"\\\n]* (?: \\. [^"\\\n]* )* "?
+
+| ''' [^'\\]* (?:
+ (?: \\. | '(?!'') )
+ [^'\\]*
+ )*
+ (?: ''' )?
+
+| ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
+""", re.VERBOSE | re.DOTALL).match
+
+# Match a line that starts with something interesting;
+# used to find the first item of a bracket structure.
+
+_itemre = re.compile(r"""
+ [ \t]*
+ [^\s#\\] # if we match, m.end()-1 is the interesting char
+""", re.VERBOSE).match
+
+# Match start of stmts that should be followed by a dedent.
+
+_closere = re.compile(r"""
+ \s*
+ (?: return
+ | break
+ | continue
+ | raise
+ | pass
+ )
+ \b
+""", re.VERBOSE).match
+
+# Chew up non-special chars as quickly as possible. If match is
+# successful, m.end() less 1 is the index of the last boring char
+# matched. If match is unsuccessful, the string starts with an
+# interesting char.
+
+_chew_ordinaryre = re.compile(r"""
+ [^[\](){}#'"\\]+
+""", re.VERBOSE).match
+
+# Build translation table to map uninteresting chars to "x", open
+# brackets to "(", and close brackets to ")".
+
+_tran = ['x'] * 256
+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
+
+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:
+ # 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
+ for raw in map(ord, uniphooey):
+ push(raw < 127 and chr(raw) or "x")
+ str = "".join(str)
+ self.str = str
+ self.study_level = 0
+
+ # Return index of a good place to begin parsing, as close to the
+ # end of the string as possible. This will be the start of some
+ # popular stmt like "if" or "def". Return None if none found:
+ # the caller should pass more prior context then, if possible, or
+ # if not (the entire program text up until the point of interest
+ # has already been tried) pass 0 to set_lo.
+ #
+ # This will be reliable iff given a reliable is_char_in_string
+ # function, meaning that when it says "no", it's absolutely
+ # guaranteed that the char is not in a string.
+
+ def find_good_parse_start(self, is_char_in_string=None,
+ _synchre=_synchre):
+ str, pos = self.str, None
+
+ if not is_char_in_string:
+ # no clue -- make the caller pass everything
+ return None
+
+ # Peek back from the end for a good place to start,
+ # but don't try too often; pos will be left None, or
+ # bumped to a legitimate synch point.
+ limit = len(str)
+ for tries in range(5):
+ i = str.rfind(":\n", 0, limit)
+ if i < 0:
+ break
+ i = str.rfind('\n', 0, i) + 1 # start of colon line
+ m = _synchre(str, i, limit)
+ if m and not is_char_in_string(m.start()):
+ pos = m.start()
+ break
+ limit = i
+ if pos is None:
+ # Nothing looks like a block-opener, or stuff does
+ # but is_char_in_string keeps returning true; most likely
+ # we're in or near a giant string, the colorizer hasn't
+ # caught up enough to be helpful, or there simply *aren't*
+ # any interesting stmts. In any of these cases we're
+ # going to have to parse the whole thing to be sure, so
+ # give it one last try from the start, but stop wasting
+ # time here regardless of the outcome.
+ m = _synchre(str)
+ if m and not is_char_in_string(m.start()):
+ pos = m.start()
+ return pos
+
+ # Peeking back worked; look forward until _synchre no longer
+ # matches.
+ i = pos + 1
+ while 1:
+ m = _synchre(str, i)
+ if m:
+ s, i = m.span()
+ if not is_char_in_string(s):
+ pos = s
+ else:
+ break
+ return pos
+
+ # Throw away the start of the string. Intended to be called with
+ # find_good_parse_start's result.
+
+ def set_lo(self, lo):
+ assert lo == 0 or self.str[lo-1] == '\n'
+ if lo > 0:
+ self.str = self.str[lo:]
+
+ # As quickly as humanly possible <wink>, find the line numbers (0-
+ # based) of the non-continuation lines.
+ # Creates self.{goodlines, continuation}.
+
+ def _study1(self):
+ if self.study_level >= 1:
+ return
+ self.study_level = 1
+
+ # Map all uninteresting characters to "x", all open brackets
+ # to "(", all close brackets to ")", then collapse runs of
+ # uninteresting characters. This can cut the number of chars
+ # by a factor of 10-40, and so greatly speed the following loop.
+ str = self.str
+ str = str.translate(_tran)
+ str = str.replace('xxxxxxxx', 'x')
+ str = str.replace('xxxx', 'x')
+ str = str.replace('xx', 'x')
+ str = str.replace('xx', 'x')
+ str = str.replace('\nx', '\n')
+ # note that replacing x\n with \n would be incorrect, because
+ # x may be preceded by a backslash
+
+ # March over the squashed version of the program, accumulating
+ # the line numbers of non-continued stmts, and determining
+ # whether & why the last stmt is a continuation.
+ continuation = C_NONE
+ level = lno = 0 # level is nesting level; lno is line number
+ self.goodlines = goodlines = [0]
+ push_good = goodlines.append
+ i, n = 0, len(str)
+ while i < n:
+ ch = str[i]
+ i = i+1
+
+ # cases are checked in decreasing order of frequency
+ if ch == 'x':
+ continue
+
+ if ch == '\n':
+ lno = lno + 1
+ if level == 0:
+ push_good(lno)
+ # else we're in an unclosed bracket structure
+ continue
+
+ if ch == '(':
+ level = level + 1
+ continue
+
+ if ch == ')':
+ if level:
+ level = level - 1
+ # else the program is invalid, but we can't complain
+ continue
+
+ if ch == '"' or ch == "'":
+ # consume the string
+ quote = ch
+ if str[i-1:i+2] == quote * 3:
+ quote = quote * 3
+ firstlno = lno
+ w = len(quote) - 1
+ i = i+w
+ while i < n:
+ ch = str[i]
+ i = i+1
+
+ if ch == 'x':
+ continue
+
+ if str[i-1:i+w] == quote:
+ i = i+w
+ break
+
+ if ch == '\n':
+ lno = lno + 1
+ if w == 0:
+ # unterminated single-quoted string
+ if level == 0:
+ push_good(lno)
+ break
+ continue
+
+ if ch == '\\':
+ assert i < n
+ if str[i] == '\n':
+ lno = lno + 1
+ i = i+1
+ continue
+
+ # else comment char or paren inside string
+
+ else:
+ # didn't break out of the loop, so we're still
+ # inside a string
+ if (lno - 1) == firstlno:
+ # before the previous \n in str, we were in the first
+ # line of the string
+ continuation = C_STRING_FIRST_LINE
+ else:
+ continuation = C_STRING_NEXT_LINES
+ continue # with outer loop
+
+ if ch == '#':
+ # consume the comment
+ i = str.find('\n', i)
+ assert i >= 0
+ continue
+
+ assert ch == '\\'
+ assert i < n
+ if str[i] == '\n':
+ lno = lno + 1
+ if i+1 == n:
+ continuation = C_BACKSLASH
+ i = i+1
+
+ # The last stmt may be continued for all 3 reasons.
+ # String continuation takes precedence over bracket
+ # continuation, which beats backslash continuation.
+ if (continuation != C_STRING_FIRST_LINE
+ and continuation != C_STRING_NEXT_LINES and level > 0):
+ continuation = C_BRACKET
+ self.continuation = continuation
+
+ # Push the final line number as a sentinel value, regardless of
+ # whether it's continued.
+ assert (continuation == C_NONE) == (goodlines[-1] == lno)
+ if goodlines[-1] != lno:
+ push_good(lno)
+
+ def get_continuation_type(self):
+ self._study1()
+ return self.continuation
+
+ # study1 was sufficient to determine the continuation status,
+ # but doing more requires looking at every character. study2
+ # does this for the last interesting statement in the block.
+ # Creates:
+ # self.stmt_start, stmt_end
+ # slice indices of last interesting stmt
+ # self.stmt_bracketing
+ # the bracketing structure of the last interesting stmt;
+ # for example, for the statement "say(boo) or die", stmt_bracketing
+ # will be [(0, 0), (3, 1), (8, 0)]. Strings and comments are
+ # treated as brackets, for the matter.
+ # self.lastch
+ # last non-whitespace character before optional trailing
+ # comment
+ # self.lastopenbracketpos
+ # if continuation is C_BRACKET, index of last open bracket
+
+ def _study2(self):
+ if self.study_level >= 2:
+ return
+ self._study1()
+ self.study_level = 2
+
+ # Set p and q to slice indices of last interesting stmt.
+ str, goodlines = self.str, self.goodlines
+ i = len(goodlines) - 1
+ p = len(str) # index of newest line
+ while i:
+ assert p
+ # p is the index of the stmt at line number goodlines[i].
+ # Move p back to the stmt at line number goodlines[i-1].
+ q = p
+ for nothing in range(goodlines[i-1], goodlines[i]):
+ # tricky: sets p to 0 if no preceding newline
+ p = str.rfind('\n', 0, p-1) + 1
+ # The stmt str[p:q] isn't a continuation, but may be blank
+ # or a non-indenting comment line.
+ if _junkre(str, p):
+ i = i-1
+ else:
+ break
+ if i == 0:
+ # nothing but junk!
+ assert p == 0
+ q = p
+ self.stmt_start, self.stmt_end = p, q
+
+ # Analyze this stmt, to find the last open bracket (if any)
+ # and last interesting character (if any).
+ lastch = ""
+ stack = [] # stack of open bracket indices
+ push_stack = stack.append
+ bracketing = [(p, 0)]
+ while p < q:
+ # suck up all except ()[]{}'"#\\
+ m = _chew_ordinaryre(str, p, q)
+ if m:
+ # we skipped at least one boring char
+ newp = m.end()
+ # back up over totally boring whitespace
+ i = newp - 1 # index of last boring char
+ while i >= p and str[i] in " \t\n":
+ i = i-1
+ if i >= p:
+ lastch = str[i]
+ p = newp
+ if p >= q:
+ break
+
+ ch = str[p]
+
+ if ch in "([{":
+ push_stack(p)
+ bracketing.append((p, len(stack)))
+ lastch = ch
+ p = p+1
+ continue
+
+ if ch in ")]}":
+ if stack:
+ del stack[-1]
+ lastch = ch
+ p = p+1
+ bracketing.append((p, len(stack)))
+ continue
+
+ if ch == '"' or ch == "'":
+ # consume string
+ # Note that study1 did this with a Python loop, but
+ # we use a regexp here; the reason is speed in both
+ # cases; the string may be huge, but study1 pre-squashed
+ # strings to a couple of characters per line. study1
+ # also needed to keep track of newlines, and we don't
+ # have to.
+ bracketing.append((p, len(stack)+1))
+ lastch = ch
+ p = _match_stringre(str, p, q).end()
+ bracketing.append((p, len(stack)))
+ continue
+
+ if ch == '#':
+ # consume comment and trailing newline
+ bracketing.append((p, len(stack)+1))
+ p = str.find('\n', p, q) + 1
+ assert p > 0
+ bracketing.append((p, len(stack)))
+ continue
+
+ assert ch == '\\'
+ p = p+1 # beyond backslash
+ assert p < q
+ if str[p] != '\n':
+ # the program is invalid, but can't complain
+ lastch = ch + str[p]
+ p = p+1 # beyond escaped char
+
+ # end while p < q:
+
+ self.lastch = lastch
+ if stack:
+ self.lastopenbracketpos = stack[-1]
+ self.stmt_bracketing = tuple(bracketing)
+
+ # Assuming continuation is C_BRACKET, return the number
+ # of spaces the next line should be indented.
+
+ def compute_bracket_indent(self):
+ self._study2()
+ assert self.continuation == C_BRACKET
+ j = self.lastopenbracketpos
+ str = self.str
+ n = len(str)
+ origi = i = str.rfind('\n', 0, j) + 1
+ j = j+1 # one beyond open bracket
+ # find first list item; set i to start of its line
+ while j < n:
+ m = _itemre(str, j)
+ if m:
+ j = m.end() - 1 # index of first interesting char
+ extra = 0
+ break
+ else:
+ # this line is junk; advance to next line
+ i = j = str.find('\n', j) + 1
+ else:
+ # nothing interesting follows the bracket;
+ # reproduce the bracket line's indentation + a level
+ j = i = origi
+ while str[j] in " \t":
+ j = j+1
+ extra = self.indentwidth
+ return len(str[i:j].expandtabs(self.tabwidth)) + extra
+
+ # Return number of physical lines in last stmt (whether or not
+ # it's an interesting stmt! this is intended to be called when
+ # continuation is C_BACKSLASH).
+
+ def get_num_lines_in_stmt(self):
+ self._study1()
+ goodlines = self.goodlines
+ return goodlines[-1] - goodlines[-2]
+
+ # Assuming continuation is C_BACKSLASH, return the number of spaces
+ # the next line should be indented. Also assuming the new line is
+ # the first one following the initial line of the stmt.
+
+ def compute_backslash_indent(self):
+ self._study2()
+ assert self.continuation == C_BACKSLASH
+ str = self.str
+ i = self.stmt_start
+ while str[i] in " \t":
+ i = i+1
+ startpos = i
+
+ # See whether the initial line starts an assignment stmt; i.e.,
+ # look for an = operator
+ endpos = str.find('\n', startpos) + 1
+ found = level = 0
+ while i < endpos:
+ ch = str[i]
+ if ch in "([{":
+ level = level + 1
+ i = i+1
+ elif ch in ")]}":
+ if level:
+ level = level - 1
+ i = i+1
+ elif ch == '"' or ch == "'":
+ i = _match_stringre(str, i, endpos).end()
+ elif ch == '#':
+ break
+ elif level == 0 and ch == '=' and \
+ (i == 0 or str[i-1] not in "=<>!") and \
+ str[i+1] != '=':
+ found = 1
+ break
+ else:
+ i = i+1
+
+ if found:
+ # found a legit =, but it may be the last interesting
+ # thing on the line
+ i = i+1 # move beyond the =
+ found = re.match(r"\s*\\", str[i:endpos]) is None
+
+ if not found:
+ # oh well ... settle for moving beyond the first chunk
+ # of non-whitespace chars
+ i = startpos
+ while str[i] not in " \t\n":
+ i = i+1
+
+ return len(str[self.stmt_start:i].expandtabs(\
+ self.tabwidth)) + 1
+
+ # Return the leading whitespace on the initial line of the last
+ # interesting stmt.
+
+ def get_base_indent_string(self):
+ self._study2()
+ i, n = self.stmt_start, self.stmt_end
+ j = i
+ str = self.str
+ while j < n and str[j] in " \t":
+ j = j + 1
+ return str[i:j]
+
+ # Did the last interesting stmt open a block?
+
+ def is_block_opener(self):
+ self._study2()
+ return self.lastch == ':'
+
+ # Did the last interesting stmt close a block?
+
+ def is_block_closer(self):
+ self._study2()
+ return _closere(self.str, self.stmt_start) is not None
+
+ # index of last open bracket ({[, or None if none
+ lastopenbracketpos = None
+
+ def get_last_open_bracket_pos(self):
+ self._study2()
+ return self.lastopenbracketpos
+
+ # the structure of the bracketing of the last interesting statement,
+ # in the format defined in _study2, or None if the text didn't contain
+ # anything
+ stmt_bracketing = None
+
+ def get_last_stmt_bracketing(self):
+ self._study2()
+ return self.stmt_bracketing
diff --git a/lib/python2.7/idlelib/PyShell.py b/lib/python2.7/idlelib/PyShell.py
new file mode 100644
index 0000000..41b3a5e
--- /dev/null
+++ b/lib/python2.7/idlelib/PyShell.py
@@ -0,0 +1,1641 @@
+#!/usr/bin/env python2
+from __future__ import print_function
+
+import os
+import os.path
+import sys
+import string
+import getopt
+import re
+import socket
+import time
+import threading
+import io
+
+import linecache
+from code import InteractiveInterpreter
+from platform import python_version, system
+
+try:
+ from Tkinter import *
+except ImportError:
+ print("** IDLE can't import Tkinter.\n"
+ "Your Python may not be configured for Tk. **", file=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 rpc
+from idlelib import Debugger
+from idlelib import RemoteDebugger
+from idlelib import macosxSupport
+from idlelib import IOBinding
+
+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
+# checking user's code.
+warning_stream = sys.__stderr__ # None, at least on Windows, if no console.
+import warnings
+
+def idle_formatwarning(message, category, filename, lineno, line=None):
+ """Format warnings the IDLE way."""
+
+ s = "\nWarning (from warnings module):\n"
+ s += ' File \"%s\", line %s\n' % (filename, lineno)
+ if line is None:
+ line = linecache.getline(filename, lineno)
+ line = line.strip()
+ if line:
+ s += " %s\n" % line
+ s += "%s: %s\n" % (category.__name__, message)
+ return s
+
+def idle_showwarning(
+ message, category, filename, lineno, file=None, line=None):
+ """Show Idle-format warning (after replacing warnings.showwarning).
+
+ The differences are the formatter called, the file=None replacement,
+ which can be None, the capture of the consequence AttributeError,
+ and the output of a hard-coded prompt.
+ """
+ if file is None:
+ file = warning_stream
+ try:
+ file.write(idle_formatwarning(
+ message, category, filename, lineno, line=line))
+ file.write(">>> ")
+ except (AttributeError, IOError):
+ pass # if file (probably __stderr__) is invalid, skip warning.
+
+_warnings_showwarning = None
+
+def capture_warnings(capture):
+ "Replace warning.showwarning with idle_showwarning, or reverse."
+
+ global _warnings_showwarning
+ if capture:
+ if _warnings_showwarning is None:
+ _warnings_showwarning = warnings.showwarning
+ warnings.showwarning = idle_showwarning
+ else:
+ if _warnings_showwarning is not None:
+ warnings.showwarning = _warnings_showwarning
+ _warnings_showwarning = None
+
+capture_warnings(True)
+
+def extended_linecache_checkcache(filename=None,
+ orig_checkcache=linecache.checkcache):
+ """Extend linecache.checkcache to preserve the <pyshell#...> entries
+
+ Rather than repeating the linecache code, patch it to save the
+ <pyshell#...> entries, call the original linecache.checkcache()
+ (skipping them), and then restore the saved entries.
+
+ orig_checkcache is bound at definition time to the original
+ method, allowing it to be patched.
+ """
+ cache = linecache.cache
+ save = {}
+ for key in list(cache):
+ if key[:1] + key[-1:] == '<>':
+ save[key] = cache.pop(key)
+ orig_checkcache(filename)
+ cache.update(save)
+
+# Patch linecache.checkcache():
+linecache.checkcache = extended_linecache_checkcache
+
+
+class PyShellEditorWindow(EditorWindow):
+ "Regular text edit window in IDLE, supports breakpoints"
+
+ def __init__(self, *args):
+ self.breakpoints = []
+ EditorWindow.__init__(self, *args)
+ self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)
+ self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here)
+ self.text.bind("<<open-python-shell>>", self.flist.open_shell)
+
+ self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(),
+ 'breakpoints.lst')
+ # whenever a file is changed, restore breakpoints
+ def filename_changed_hook(old_hook=self.io.filename_change_hook,
+ self=self):
+ self.restore_file_breaks()
+ old_hook()
+ self.io.set_filename_change_hook(filename_changed_hook)
+ if self.io.filename:
+ self.restore_file_breaks()
+ self.color_breakpoint_text()
+
+ rmenu_specs = [
+ ("Cut", "<<cut>>", "rmenu_check_cut"),
+ ("Copy", "<<copy>>", "rmenu_check_copy"),
+ ("Paste", "<<paste>>", "rmenu_check_paste"),
+ ("Set Breakpoint", "<<set-breakpoint-here>>", None),
+ ("Clear Breakpoint", "<<clear-breakpoint-here>>", None)
+ ]
+
+ def color_breakpoint_text(self, color=True):
+ "Turn colorizing of breakpoint text on or off"
+ if self.io is None:
+ # possible due to update in restore_file_breaks
+ return
+ if color:
+ theme = idleConf.CurrentTheme()
+ cfg = idleConf.GetHighlight(theme, "break")
+ else:
+ cfg = {'foreground': '', 'background': ''}
+ self.text.tag_config('BREAK', cfg)
+
+ def set_breakpoint(self, lineno):
+ text = self.text
+ filename = self.io.filename
+ text.tag_add("BREAK", "%d.0" % lineno, "%d.0" % (lineno+1))
+ try:
+ self.breakpoints.index(lineno)
+ except ValueError: # only add if missing, i.e. do once
+ self.breakpoints.append(lineno)
+ try: # update the subprocess debugger
+ debug = self.flist.pyshell.interp.debugger
+ debug.set_breakpoint_here(filename, lineno)
+ except: # but debugger may not be active right now....
+ pass
+
+ def set_breakpoint_here(self, event=None):
+ text = self.text
+ filename = self.io.filename
+ if not filename:
+ text.bell()
+ return
+ lineno = int(float(text.index("insert")))
+ self.set_breakpoint(lineno)
+
+ def clear_breakpoint_here(self, event=None):
+ text = self.text
+ filename = self.io.filename
+ if not filename:
+ text.bell()
+ return
+ lineno = int(float(text.index("insert")))
+ try:
+ self.breakpoints.remove(lineno)
+ except:
+ pass
+ text.tag_remove("BREAK", "insert linestart",\
+ "insert lineend +1char")
+ try:
+ debug = self.flist.pyshell.interp.debugger
+ debug.clear_breakpoint_here(filename, lineno)
+ except:
+ pass
+
+ def clear_file_breaks(self):
+ if self.breakpoints:
+ text = self.text
+ filename = self.io.filename
+ if not filename:
+ text.bell()
+ return
+ self.breakpoints = []
+ text.tag_remove("BREAK", "1.0", END)
+ try:
+ debug = self.flist.pyshell.interp.debugger
+ debug.clear_file_breaks(filename)
+ except:
+ pass
+
+ def store_file_breaks(self):
+ "Save breakpoints when file is saved"
+ # XXX 13 Dec 2002 KBK Currently the file must be saved before it can
+ # be run. The breaks are saved at that time. If we introduce
+ # a temporary file save feature the save breaks functionality
+ # needs to be re-verified, since the breaks at the time the
+ # temp file is created may differ from the breaks at the last
+ # permanent save of the file. Currently, a break introduced
+ # after a save will be effective, but not persistent.
+ # This is necessary to keep the saved breaks synched with the
+ # saved file.
+ #
+ # Breakpoints are set as tagged ranges in the text.
+ # Since a modified file has to be saved before it is
+ # run, and since self.breakpoints (from which the subprocess
+ # debugger is loaded) is updated during the save, the visible
+ # breaks stay synched with the subprocess even if one of these
+ # unexpected breakpoint deletions occurs.
+ breaks = self.breakpoints
+ filename = self.io.filename
+ try:
+ with open(self.breakpointPath,"r") as old_file:
+ lines = old_file.readlines()
+ except IOError:
+ lines = []
+ try:
+ with open(self.breakpointPath,"w") as new_file:
+ for line in lines:
+ if not line.startswith(filename + '='):
+ new_file.write(line)
+ self.update_breakpoints()
+ breaks = self.breakpoints
+ if breaks:
+ new_file.write(filename + '=' + str(breaks) + '\n')
+ except IOError as err:
+ if not getattr(self.root, "breakpoint_error_displayed", False):
+ self.root.breakpoint_error_displayed = True
+ tkMessageBox.showerror(title='IDLE Error',
+ message='Unable to update breakpoint list:\n%s'
+ % str(err),
+ parent=self.text)
+
+ def restore_file_breaks(self):
+ self.text.update() # this enables setting "BREAK" tags to be visible
+ if self.io is None:
+ # can happen if IDLE closes due to the .update() call
+ return
+ filename = self.io.filename
+ if filename is None:
+ return
+ if os.path.isfile(self.breakpointPath):
+ lines = open(self.breakpointPath,"r").readlines()
+ for line in lines:
+ if line.startswith(filename + '='):
+ breakpoint_linenumbers = eval(line[len(filename)+1:])
+ for breakpoint_linenumber in breakpoint_linenumbers:
+ self.set_breakpoint(breakpoint_linenumber)
+
+ def update_breakpoints(self):
+ "Retrieves all the breakpoints in the current window"
+ text = self.text
+ ranges = text.tag_ranges("BREAK")
+ linenumber_list = self.ranges_to_linenumbers(ranges)
+ self.breakpoints = linenumber_list
+
+ def ranges_to_linenumbers(self, ranges):
+ lines = []
+ for index in range(0, len(ranges), 2):
+ lineno = int(float(ranges[index].string))
+ end = int(float(ranges[index+1].string))
+ while lineno < end:
+ lines.append(lineno)
+ lineno += 1
+ return lines
+
+# XXX 13 Dec 2002 KBK Not used currently
+# def saved_change_hook(self):
+# "Extend base method - clear breaks if module is modified"
+# if not self.get_saved():
+# self.clear_file_breaks()
+# EditorWindow.saved_change_hook(self)
+
+ def _close(self):
+ "Extend base method - clear breaks when module is closed"
+ self.clear_file_breaks()
+ EditorWindow._close(self)
+
+
+class PyShellFileList(FileList):
+ "Extend base class: IDLE supports a shell and breakpoints"
+
+ # override FileList's class variable, instances return PyShellEditorWindow
+ # instead of EditorWindow when new edit windows are created.
+ EditorWindow = PyShellEditorWindow
+
+ pyshell = None
+
+ def open_shell(self, event=None):
+ if self.pyshell:
+ self.pyshell.top.wakeup()
+ else:
+ self.pyshell = PyShell(self)
+ if self.pyshell:
+ if not self.pyshell.begin():
+ return None
+ return self.pyshell
+
+
+class ModifiedColorDelegator(ColorDelegator):
+ "Extend base class: colorizer for the shell window itself"
+
+ def __init__(self):
+ ColorDelegator.__init__(self)
+ self.LoadTagDefs()
+
+ def recolorize_main(self):
+ self.tag_remove("TODO", "1.0", "iomark")
+ self.tag_add("SYNC", "1.0", "iomark")
+ ColorDelegator.recolorize_main(self)
+
+ def LoadTagDefs(self):
+ ColorDelegator.LoadTagDefs(self)
+ theme = idleConf.CurrentTheme()
+ self.tagdefs.update({
+ "stdin": {'background':None,'foreground':None},
+ "stdout": idleConf.GetHighlight(theme, "stdout"),
+ "stderr": idleConf.GetHighlight(theme, "stderr"),
+ "console": idleConf.GetHighlight(theme, "console"),
+ })
+
+ def removecolors(self):
+ # Don't remove shell color tags before "iomark"
+ for tag in self.tagdefs:
+ self.tag_remove(tag, "iomark", "end")
+
+class ModifiedUndoDelegator(UndoDelegator):
+ "Extend base class: forbid insert/delete before the I/O mark"
+
+ def insert(self, index, chars, tags=None):
+ try:
+ if self.delegate.compare(index, "<", "iomark"):
+ self.delegate.bell()
+ return
+ except TclError:
+ pass
+ UndoDelegator.insert(self, index, chars, tags)
+
+ def delete(self, index1, index2=None):
+ try:
+ if self.delegate.compare(index1, "<", "iomark"):
+ self.delegate.bell()
+ return
+ except TclError:
+ pass
+ UndoDelegator.delete(self, index1, index2)
+
+
+class MyRPCClient(rpc.RPCClient):
+
+ def handle_EOF(self):
+ "Override the base class - just re-raise EOFError"
+ raise EOFError
+
+
+class ModifiedInterpreter(InteractiveInterpreter):
+
+ def __init__(self, tkconsole):
+ self.tkconsole = tkconsole
+ locals = sys.modules['__main__'].__dict__
+ InteractiveInterpreter.__init__(self, locals=locals)
+ self.save_warnings_filters = None
+ self.restarting = False
+ self.subprocess_arglist = None
+ self.port = PORT
+ self.original_compiler_flags = self.compile.compiler.flags
+
+ _afterid = None
+ rpcclt = None
+ rpcpid = None
+
+ def spawn_subprocess(self):
+ if self.subprocess_arglist is None:
+ self.subprocess_arglist = self.build_subprocess_arglist()
+ args = self.subprocess_arglist
+ self.rpcpid = os.spawnv(os.P_NOWAIT, sys.executable, args)
+
+ def build_subprocess_arglist(self):
+ assert (self.port!=0), (
+ "Socket should have been assigned a port number.")
+ w = ['-W' + s for s in sys.warnoptions]
+ if 1/2 > 0: # account for new division
+ w.append('-Qnew')
+ # 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.
+ del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc',
+ default=False, type='bool')
+ if __name__ == 'idlelib.PyShell':
+ command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,)
+ else:
+ command = "__import__('run').main(%r)" % (del_exitf,)
+ if sys.platform[:3] == 'win' and ' ' in sys.executable:
+ # handle embedded space in path by quoting the argument
+ decorated_exec = '"%s"' % sys.executable
+ else:
+ decorated_exec = sys.executable
+ return [decorated_exec] + w + ["-c", command, str(self.port)]
+
+ def start_subprocess(self):
+ addr = (HOST, self.port)
+ # GUI makes several attempts to acquire socket, listens for connection
+ for i in range(3):
+ time.sleep(i)
+ try:
+ self.rpcclt = MyRPCClient(addr)
+ break
+ except socket.error:
+ pass
+ else:
+ self.display_port_binding_error()
+ return None
+ # if PORT was 0, system will assign an 'ephemeral' port. Find it out:
+ self.port = self.rpcclt.listening_sock.getsockname()[1]
+ # if PORT was not 0, probably working with a remote execution server
+ if PORT != 0:
+ # To allow reconnection within the 2MSL wait (cf. Stevens TCP
+ # V1, 18.6), set SO_REUSEADDR. Note that this can be problematic
+ # on Windows since the implementation allows two active sockets on
+ # the same address!
+ self.rpcclt.listening_sock.setsockopt(socket.SOL_SOCKET,
+ socket.SO_REUSEADDR, 1)
+ self.spawn_subprocess()
+ #time.sleep(20) # test to simulate GUI not accepting connection
+ # Accept the connection from the Python execution server
+ self.rpcclt.listening_sock.settimeout(10)
+ try:
+ self.rpcclt.accept()
+ except socket.timeout:
+ self.display_no_subprocess_error()
+ return None
+ self.rpcclt.register("console", self.tkconsole)
+ self.rpcclt.register("stdin", self.tkconsole.stdin)
+ self.rpcclt.register("stdout", self.tkconsole.stdout)
+ self.rpcclt.register("stderr", self.tkconsole.stderr)
+ self.rpcclt.register("flist", self.tkconsole.flist)
+ self.rpcclt.register("linecache", linecache)
+ self.rpcclt.register("interp", self)
+ self.transfer_path(with_cwd=True)
+ self.poll_subprocess()
+ return self.rpcclt
+
+ def restart_subprocess(self, with_cwd=False, filename=''):
+ if self.restarting:
+ return self.rpcclt
+ self.restarting = True
+ # close only the subprocess debugger
+ debug = self.getdebugger()
+ if debug:
+ try:
+ # Only close subprocess debugger, don't unregister gui_adap!
+ RemoteDebugger.close_subprocess_debugger(self.rpcclt)
+ except:
+ pass
+ # Kill subprocess, spawn a new one, accept connection.
+ self.rpcclt.close()
+ self.unix_terminate()
+ console = self.tkconsole
+ was_executing = console.executing
+ console.executing = False
+ self.spawn_subprocess()
+ try:
+ self.rpcclt.accept()
+ except socket.timeout:
+ self.display_no_subprocess_error()
+ return None
+ self.transfer_path(with_cwd=with_cwd)
+ console.stop_readline()
+ # annotate restart in shell window and mark it
+ console.text.delete("iomark", "end-1c")
+ tag = 'RESTART: ' + (filename if filename else 'Shell')
+ halfbar = ((int(console.width) -len(tag) - 4) // 2) * '='
+ console.write("\n{0} {1} {0}".format(halfbar, tag))
+ console.text.mark_set("restart", "end-1c")
+ console.text.mark_gravity("restart", "left")
+ if not filename:
+ console.showprompt()
+ # restart subprocess debugger
+ if debug:
+ # Restarted debugger connects to current instance of debug GUI
+ RemoteDebugger.restart_subprocess_debugger(self.rpcclt)
+ # reload remote debugger breakpoints for all PyShellEditWindows
+ debug.load_breakpoints()
+ self.compile.compiler.flags = self.original_compiler_flags
+ self.restarting = False
+ return self.rpcclt
+
+ def __request_interrupt(self):
+ self.rpcclt.remotecall("exec", "interrupt_the_server", (), {})
+
+ def interrupt_subprocess(self):
+ threading.Thread(target=self.__request_interrupt).start()
+
+ def kill_subprocess(self):
+ if self._afterid is not None:
+ self.tkconsole.text.after_cancel(self._afterid)
+ try:
+ self.rpcclt.close()
+ except AttributeError: # no socket
+ pass
+ self.unix_terminate()
+ self.tkconsole.executing = False
+ self.rpcclt = None
+
+ def unix_terminate(self):
+ "UNIX: make sure subprocess is terminated and collect status"
+ if hasattr(os, 'kill'):
+ try:
+ os.kill(self.rpcpid, SIGTERM)
+ except OSError:
+ # process already terminated:
+ return
+ else:
+ try:
+ os.waitpid(self.rpcpid, 0)
+ except OSError:
+ return
+
+ def transfer_path(self, with_cwd=False):
+ if with_cwd: # Issue 13506
+ path = [''] # include Current Working Directory
+ path.extend(sys.path)
+ else:
+ path = sys.path
+
+ self.runcommand("""if 1:
+ import sys as _sys
+ _sys.path = %r
+ del _sys
+ \n""" % (path,))
+
+ active_seq = None
+
+ def poll_subprocess(self):
+ clt = self.rpcclt
+ if clt is None:
+ return
+ try:
+ response = clt.pollresponse(self.active_seq, wait=0.05)
+ except (EOFError, IOError, KeyboardInterrupt):
+ # lost connection or subprocess terminated itself, restart
+ # [the KBI is from rpc.SocketIO.handle_EOF()]
+ if self.tkconsole.closing:
+ return
+ response = None
+ self.restart_subprocess()
+ if response:
+ self.tkconsole.resetoutput()
+ self.active_seq = None
+ how, what = response
+ console = self.tkconsole.console
+ if how == "OK":
+ if what is not None:
+ print(repr(what), file=console)
+ elif how == "EXCEPTION":
+ if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
+ self.remote_stack_viewer()
+ elif how == "ERROR":
+ errmsg = "PyShell.ModifiedInterpreter: Subprocess ERROR:\n"
+ print(errmsg, what, file=sys.__stderr__)
+ print(errmsg, what, file=console)
+ # we received a response to the currently active seq number:
+ try:
+ self.tkconsole.endexecuting()
+ except AttributeError: # shell may have closed
+ pass
+ # Reschedule myself
+ if not self.tkconsole.closing:
+ self._afterid = self.tkconsole.text.after(
+ self.tkconsole.pollinterval, self.poll_subprocess)
+
+ debugger = None
+
+ def setdebugger(self, debugger):
+ self.debugger = debugger
+
+ def getdebugger(self):
+ return self.debugger
+
+ def open_remote_stack_viewer(self):
+ """Initiate the remote stack viewer from a separate thread.
+
+ 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
+ the RPC mechanism.
+
+ """
+ self.tkconsole.text.after(300, self.remote_stack_viewer)
+ return
+
+ def remote_stack_viewer(self):
+ from idlelib 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
+ top = Toplevel(self.tkconsole.root)
+ theme = idleConf.CurrentTheme()
+ background = idleConf.GetHighlight(theme, 'normal')['background']
+ sc = ScrolledCanvas(top, bg=background, highlightthickness=0)
+ sc.frame.pack(expand=1, fill="both")
+ node = TreeNode(sc.canvas, None, item)
+ node.expand()
+ # XXX Should GC the remote tree when closing the window
+
+ gid = 0
+
+ def execsource(self, source):
+ "Like runsource() but assumes complete exec source"
+ filename = self.stuffsource(source)
+ self.execfile(filename, source)
+
+ def execfile(self, filename, source=None):
+ "Execute an existing file"
+ if source is None:
+ source = open(filename, "r").read()
+ try:
+ code = compile(source, filename, "exec", dont_inherit=True)
+ except (OverflowError, SyntaxError):
+ self.tkconsole.resetoutput()
+ print('*** Error in script or command!\n'
+ 'Traceback (most recent call last):',
+ file=self.tkconsole.stderr)
+ InteractiveInterpreter.showsyntaxerror(self, filename)
+ self.tkconsole.showprompt()
+ else:
+ self.runcode(code)
+
+ def runsource(self, source):
+ "Extend base class method: Stuff the source in the line cache first"
+ filename = self.stuffsource(source)
+ self.more = 0
+ self.save_warnings_filters = warnings.filters[:]
+ warnings.filterwarnings(action="error", category=SyntaxWarning)
+ if isinstance(source, unicode) and IOBinding.encoding != 'utf-8':
+ try:
+ source = '# -*- coding: %s -*-\n%s' % (
+ IOBinding.encoding,
+ 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)
+ return InteractiveInterpreter.runsource(self, source, filename)
+ finally:
+ if self.save_warnings_filters is not None:
+ warnings.filters[:] = self.save_warnings_filters
+ self.save_warnings_filters = None
+
+ def stuffsource(self, source):
+ "Stuff source in the filename cache"
+ filename = "<pyshell#%d>" % self.gid
+ self.gid = self.gid + 1
+ lines = source.split("\n")
+ linecache.cache[filename] = len(source)+1, 0, lines, filename
+ return filename
+
+ def prepend_syspath(self, filename):
+ "Prepend sys.path with file's directory if not already included"
+ self.runcommand("""if 1:
+ _filename = %r
+ import sys as _sys
+ from os.path import dirname as _dirname
+ _dir = _dirname(_filename)
+ if not _dir in _sys.path:
+ _sys.path.insert(0, _dir)
+ del _filename, _sys, _dirname, _dir
+ \n""" % (filename,))
+
+ def showsyntaxerror(self, filename=None):
+ """Extend base class method: Add 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):
+ 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
+ else:
+ return None
+
+ def showtraceback(self):
+ "Extend base class method to reset output properly"
+ self.tkconsole.resetoutput()
+ self.checklinecache()
+ InteractiveInterpreter.showtraceback(self)
+ if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
+ self.tkconsole.open_stack_viewer()
+
+ def checklinecache(self):
+ c = linecache.cache
+ for key in c.keys():
+ if key[:1] + key[-1:] != "<>":
+ del c[key]
+
+ def runcommand(self, code):
+ "Run the code without invoking the debugger"
+ # The code better not raise an exception!
+ if self.tkconsole.executing:
+ self.display_executing_dialog()
+ return 0
+ if self.rpcclt:
+ self.rpcclt.remotequeue("exec", "runcode", (code,), {})
+ else:
+ exec code in self.locals
+ return 1
+
+ def runcode(self, code):
+ "Override base class method"
+ if self.tkconsole.executing:
+ self.interp.restart_subprocess()
+ self.checklinecache()
+ if self.save_warnings_filters is not None:
+ warnings.filters[:] = self.save_warnings_filters
+ self.save_warnings_filters = None
+ debugger = self.debugger
+ try:
+ self.tkconsole.beginexecuting()
+ if not debugger and self.rpcclt is not None:
+ self.active_seq = self.rpcclt.asyncqueue("exec", "runcode",
+ (code,), {})
+ elif debugger:
+ debugger.run(code, self.locals)
+ else:
+ exec code in self.locals
+ except SystemExit:
+ if not self.tkconsole.closing:
+ if tkMessageBox.askyesno(
+ "Exit?",
+ "Do you want to exit altogether?",
+ default="yes",
+ parent=self.tkconsole.text):
+ raise
+ else:
+ self.showtraceback()
+ else:
+ raise
+ except:
+ if use_subprocess:
+ print("IDLE internal error in runcode()",
+ file=self.tkconsole.stderr)
+ self.showtraceback()
+ self.tkconsole.endexecuting()
+ else:
+ if self.tkconsole.canceled:
+ self.tkconsole.canceled = False
+ print("KeyboardInterrupt", file=self.tkconsole.stderr)
+ else:
+ self.showtraceback()
+ finally:
+ if not use_subprocess:
+ try:
+ self.tkconsole.endexecuting()
+ except AttributeError: # shell may have closed
+ pass
+
+ def write(self, s):
+ "Override base class method"
+ self.tkconsole.stderr.write(s)
+
+ def display_port_binding_error(self):
+ tkMessageBox.showerror(
+ "Port Binding Error",
+ "IDLE can't bind to a TCP/IP port, which is necessary to "
+ "communicate with its Python execution server. This might be "
+ "because no networking is installed on this computer. "
+ "Run IDLE with the -n command line switch to start without a "
+ "subprocess and refer to Help/IDLE Help 'Running without a "
+ "subprocess' for further details.",
+ parent=self.tkconsole.text)
+
+ def display_no_subprocess_error(self):
+ tkMessageBox.showerror(
+ "Subprocess Startup Error",
+ "IDLE's subprocess didn't make connection. Either IDLE can't "
+ "start a subprocess or personal firewall software is blocking "
+ "the connection.",
+ parent=self.tkconsole.text)
+
+ def display_executing_dialog(self):
+ tkMessageBox.showerror(
+ "Already executing",
+ "The Python Shell window is already executing a command; "
+ "please wait until it is finished.",
+ parent=self.tkconsole.text)
+
+
+class PyShell(OutputWindow):
+
+ shell_title = "Python " + python_version() + " Shell"
+
+ # Override classes
+ ColorDelegator = ModifiedColorDelegator
+ UndoDelegator = ModifiedUndoDelegator
+
+ # Override menus
+ menu_specs = [
+ ("file", "_File"),
+ ("edit", "_Edit"),
+ ("debug", "_Debug"),
+ ("options", "_Options"),
+ ("windows", "_Window"),
+ ("help", "_Help"),
+ ]
+
+
+ # New classes
+ from idlelib.IdleHistory import History
+
+ def __init__(self, flist=None):
+ if use_subprocess:
+ ms = self.menu_specs
+ if ms[2][0] != "shell":
+ ms.insert(2, ("shell", "She_ll"))
+ self.interp = ModifiedInterpreter(self)
+ if flist is None:
+ root = Tk()
+ fixwordbreaks(root)
+ root.withdraw()
+ flist = PyShellFileList(root)
+ #
+ OutputWindow.__init__(self, flist, None, None)
+ #
+## self.config(usetabs=1, indentwidth=8, context_use_ps1=1)
+ self.usetabs = True
+ # indentwidth must be 8 when using tabs. See note in EditorWindow:
+ self.indentwidth = 8
+ self.context_use_ps1 = True
+ #
+ text = self.text
+ text.configure(wrap="char")
+ text.bind("<<newline-and-indent>>", self.enter_callback)
+ text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
+ text.bind("<<interrupt-execution>>", self.cancel_callback)
+ text.bind("<<end-of-file>>", self.eof_callback)
+ text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
+ text.bind("<<toggle-debugger>>", self.toggle_debugger)
+ text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
+ if use_subprocess:
+ text.bind("<<view-restart>>", self.view_restart_mark)
+ text.bind("<<restart-shell>>", self.restart_shell)
+ #
+ self.save_stdout = sys.stdout
+ self.save_stderr = sys.stderr
+ self.save_stdin = sys.stdin
+ from idlelib import IOBinding
+ self.stdin = PseudoInputFile(self, "stdin", IOBinding.encoding)
+ self.stdout = PseudoOutputFile(self, "stdout", IOBinding.encoding)
+ self.stderr = PseudoOutputFile(self, "stderr", IOBinding.encoding)
+ self.console = PseudoOutputFile(self, "console", IOBinding.encoding)
+ if not use_subprocess:
+ sys.stdout = self.stdout
+ sys.stderr = self.stderr
+ sys.stdin = self.stdin
+ #
+ self.history = self.History(self.text)
+ #
+ self.pollinterval = 50 # millisec
+
+ def get_standard_extension_names(self):
+ return idleConf.GetExtensions(shell_only=True)
+
+ reading = False
+ executing = False
+ canceled = False
+ endoffile = False
+ closing = False
+ _stop_readline_flag = False
+
+ def set_warning_stream(self, stream):
+ global warning_stream
+ warning_stream = stream
+
+ def get_warning_stream(self):
+ return warning_stream
+
+ def toggle_debugger(self, event=None):
+ if self.executing:
+ tkMessageBox.showerror("Don't debug now",
+ "You can only toggle the debugger when idle",
+ parent=self.text)
+ self.set_debugger_indicator()
+ return "break"
+ else:
+ db = self.interp.getdebugger()
+ if db:
+ self.close_debugger()
+ else:
+ self.open_debugger()
+
+ def set_debugger_indicator(self):
+ db = self.interp.getdebugger()
+ self.setvar("<<toggle-debugger>>", not not db)
+
+ def toggle_jit_stack_viewer(self, event=None):
+ pass # All we need is the variable
+
+ def close_debugger(self):
+ db = self.interp.getdebugger()
+ if db:
+ self.interp.setdebugger(None)
+ db.close()
+ if self.interp.rpcclt:
+ RemoteDebugger.close_remote_debugger(self.interp.rpcclt)
+ self.resetoutput()
+ self.console.write("[DEBUG OFF]\n")
+ sys.ps1 = ">>> "
+ self.showprompt()
+ self.set_debugger_indicator()
+
+ def open_debugger(self):
+ if self.interp.rpcclt:
+ dbg_gui = RemoteDebugger.start_remote_debugger(self.interp.rpcclt,
+ self)
+ else:
+ dbg_gui = Debugger.Debugger(self)
+ self.interp.setdebugger(dbg_gui)
+ dbg_gui.load_breakpoints()
+ sys.ps1 = "[DEBUG ON]\n>>> "
+ self.showprompt()
+ self.set_debugger_indicator()
+
+ def beginexecuting(self):
+ "Helper for ModifiedInterpreter"
+ self.resetoutput()
+ self.executing = 1
+
+ def endexecuting(self):
+ "Helper for ModifiedInterpreter"
+ self.executing = 0
+ self.canceled = 0
+ self.showprompt()
+
+ def close(self):
+ "Extend EditorWindow.close()"
+ if self.executing:
+ response = tkMessageBox.askokcancel(
+ "Kill?",
+ "Your program is still running!\n Do you want to kill it?",
+ default="ok",
+ parent=self.text)
+ if response is False:
+ return "cancel"
+ self.stop_readline()
+ self.canceled = True
+ self.closing = True
+ return EditorWindow.close(self)
+
+ def _close(self):
+ "Extend EditorWindow._close(), shut down debugger and execution server"
+ self.close_debugger()
+ if use_subprocess:
+ self.interp.kill_subprocess()
+ # Restore std streams
+ sys.stdout = self.save_stdout
+ sys.stderr = self.save_stderr
+ sys.stdin = self.save_stdin
+ # Break cycles
+ self.interp = None
+ self.console = None
+ self.flist.pyshell = None
+ self.history = None
+ EditorWindow._close(self)
+
+ def ispythonsource(self, filename):
+ "Override EditorWindow method: never remove the colorizer"
+ return True
+
+ def short_title(self):
+ return self.shell_title
+
+ COPYRIGHT = \
+ 'Type "copyright", "credits" or "license()" for more information.'
+
+ def begin(self):
+ self.resetoutput()
+ if use_subprocess:
+ nosub = ''
+ client = self.interp.start_subprocess()
+ if not client:
+ self.close()
+ return False
+ else:
+ nosub = "==== No Subprocess ===="
+ self.write("Python %s on %s\n%s\n%s" %
+ (sys.version, sys.platform, self.COPYRIGHT, nosub))
+ self.text.focus_force()
+ self.showprompt()
+ import Tkinter
+ Tkinter._default_root = None # 03Jan04 KBK What's this?
+ return True
+
+ def stop_readline(self):
+ if not self.reading: # no nested mainloop to exit.
+ return
+ self._stop_readline_flag = True
+ self.top.quit()
+
+ def readline(self):
+ save = self.reading
+ try:
+ self.reading = 1
+ self.top.mainloop() # nested mainloop()
+ finally:
+ self.reading = save
+ if self._stop_readline_flag:
+ self._stop_readline_flag = False
+ return ""
+ 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
+ if not use_subprocess:
+ raise KeyboardInterrupt
+ if self.endoffile:
+ self.endoffile = 0
+ line = ""
+ return line
+
+ def isatty(self):
+ return True
+
+ def cancel_callback(self, event=None):
+ try:
+ if self.text.compare("sel.first", "!=", "sel.last"):
+ return # Active selection -- always use default binding
+ except:
+ pass
+ if not (self.executing or self.reading):
+ self.resetoutput()
+ self.interp.write("KeyboardInterrupt\n")
+ self.showprompt()
+ return "break"
+ self.endoffile = 0
+ self.canceled = 1
+ if (self.executing and self.interp.rpcclt):
+ if self.interp.getdebugger():
+ self.interp.restart_subprocess()
+ else:
+ self.interp.interrupt_subprocess()
+ if self.reading:
+ self.top.quit() # exit the nested mainloop() in readline()
+ return "break"
+
+ def eof_callback(self, event):
+ if self.executing and not self.reading:
+ return # Let the default binding (delete next char) take over
+ if not (self.text.compare("iomark", "==", "insert") and
+ self.text.compare("insert", "==", "end-1c")):
+ return # Let the default binding (delete next char) take over
+ if not self.executing:
+ self.resetoutput()
+ self.close()
+ else:
+ self.canceled = 0
+ self.endoffile = 1
+ self.top.quit()
+ return "break"
+
+ def linefeed_callback(self, event):
+ # Insert a linefeed without entering anything (still autoindented)
+ if self.reading:
+ self.text.insert("insert", "\n")
+ self.text.see("insert")
+ else:
+ self.newline_and_indent_event(event)
+ return "break"
+
+ def enter_callback(self, event):
+ if self.executing and not self.reading:
+ return # Let the default binding (insert '\n') take over
+ # If some text is selected, recall the selection
+ # (but only if this before the I/O mark)
+ try:
+ sel = self.text.get("sel.first", "sel.last")
+ if sel:
+ if self.text.compare("sel.last", "<=", "iomark"):
+ self.recall(sel, event)
+ return "break"
+ except:
+ pass
+ # If we're strictly before the line containing iomark, recall
+ # the current line, less a leading prompt, less leading or
+ # trailing whitespace
+ if self.text.compare("insert", "<", "iomark linestart"):
+ # Check if there's a relevant stdin range -- if so, use it
+ prev = self.text.tag_prevrange("stdin", "insert")
+ if prev and self.text.compare("insert", "<", prev[1]):
+ self.recall(self.text.get(prev[0], prev[1]), event)
+ return "break"
+ next = self.text.tag_nextrange("stdin", "insert")
+ if next and self.text.compare("insert lineend", ">=", next[0]):
+ self.recall(self.text.get(next[0], next[1]), event)
+ return "break"
+ # No stdin mark -- just get the current line, less any prompt
+ indices = self.text.tag_nextrange("console", "insert linestart")
+ if indices and \
+ self.text.compare(indices[0], "<=", "insert linestart"):
+ self.recall(self.text.get(indices[1], "insert lineend"), event)
+ else:
+ self.recall(self.text.get("insert linestart", "insert lineend"), event)
+ return "break"
+ # If we're between the beginning of the line and the iomark, i.e.
+ # in the prompt area, move to the end of the prompt
+ if self.text.compare("insert", "<", "iomark"):
+ self.text.mark_set("insert", "iomark")
+ # If we're in the current input and there's only whitespace
+ # beyond the cursor, erase that whitespace first
+ s = self.text.get("insert", "end-1c")
+ if s and not s.strip():
+ self.text.delete("insert", "end-1c")
+ # If we're in the current input before its last line,
+ # insert a newline right at the insert point
+ if self.text.compare("insert", "<", "end-1c linestart"):
+ self.newline_and_indent_event(event)
+ return "break"
+ # We're in the last line; append a newline and submit it
+ self.text.mark_set("insert", "end-1c")
+ if self.reading:
+ self.text.insert("insert", "\n")
+ self.text.see("insert")
+ else:
+ self.newline_and_indent_event(event)
+ 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()
+ else:
+ self.runit()
+ return "break"
+
+ def recall(self, s, event):
+ # remove leading and trailing empty or whitespace lines
+ s = re.sub(r'^\s*\n', '' , s)
+ s = re.sub(r'\n\s*$', '', s)
+ lines = s.split('\n')
+ self.text.undo_block_start()
+ try:
+ self.text.tag_remove("sel", "1.0", "end")
+ self.text.mark_set("insert", "end-1c")
+ prefix = self.text.get("insert linestart", "insert")
+ if prefix.rstrip().endswith(':'):
+ self.newline_and_indent_event(event)
+ prefix = self.text.get("insert linestart", "insert")
+ self.text.insert("insert", lines[0].strip())
+ if len(lines) > 1:
+ orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0)
+ new_base_indent = re.search(r'^([ \t]*)', prefix).group(0)
+ for line in lines[1:]:
+ if line.startswith(orig_base_indent):
+ # replace orig base indentation with new indentation
+ line = new_base_indent + line[len(orig_base_indent):]
+ self.text.insert('insert', '\n'+line.rstrip())
+ finally:
+ self.text.see("insert")
+ self.text.undo_block_stop()
+
+ def runit(self):
+ line = self.text.get("iomark", "end-1c")
+ # Strip off last newline and surrounding whitespace.
+ # (To allow you to hit return twice to end a statement.)
+ i = len(line)
+ while i > 0 and line[i-1] in " \t":
+ i = i-1
+ if i > 0 and line[i-1] == "\n":
+ i = i-1
+ while i > 0 and line[i-1] in " \t":
+ i = i-1
+ line = line[:i]
+ self.interp.runsource(line)
+
+ def open_stack_viewer(self, event=None):
+ if self.interp.rpcclt:
+ return self.interp.remote_stack_viewer()
+ try:
+ sys.last_traceback
+ except:
+ tkMessageBox.showerror("No stack trace",
+ "There is no stack trace yet.\n"
+ "(sys.last_traceback is not defined)",
+ parent=self.text)
+ return
+ from idlelib.StackViewer import StackBrowser
+ StackBrowser(self.root, self.flist)
+
+ def view_restart_mark(self, event=None):
+ self.text.see("iomark")
+ self.text.see("restart")
+
+ def restart_shell(self, event=None):
+ "Callback for Run/Restart Shell Cntl-F6"
+ self.interp.restart_subprocess(with_cwd=True)
+
+ def showprompt(self):
+ self.resetoutput()
+ try:
+ s = str(sys.ps1)
+ except:
+ s = ""
+ self.console.write(s)
+ self.text.mark_set("insert", "end-1c")
+ self.set_line_and_column()
+ self.io.reset_undo()
+
+ def resetoutput(self):
+ source = self.text.get("iomark", "end-1c")
+ if self.history:
+ self.history.store(source)
+ if self.text.get("end-2c") != "\n":
+ 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:
+ self.text.mark_gravity("iomark", "right")
+ OutputWindow.write(self, s, tags, "iomark")
+ self.text.mark_gravity("iomark", "left")
+ except:
+ pass
+ if self.canceled:
+ self.canceled = 0
+ if not use_subprocess:
+ raise KeyboardInterrupt
+
+ def rmenu_check_cut(self):
+ try:
+ if self.text.compare('sel.first', '<', 'iomark'):
+ return 'disabled'
+ except TclError: # no selection, so the index 'sel.first' doesn't exist
+ return 'disabled'
+ return super(PyShell, self).rmenu_check_cut()
+
+ def rmenu_check_paste(self):
+ if self.text.compare('insert', '<', 'iomark'):
+ return 'disabled'
+ return super(PyShell, self).rmenu_check_paste()
+
+class PseudoFile(io.TextIOBase):
+
+ def __init__(self, shell, tags, encoding=None):
+ self.shell = shell
+ self.tags = tags
+ self.softspace = 0
+ self._encoding = encoding
+
+ @property
+ def encoding(self):
+ return self._encoding
+
+ @property
+ def name(self):
+ return '<%s>' % self.tags
+
+ def isatty(self):
+ return True
+
+
+class PseudoOutputFile(PseudoFile):
+
+ def writable(self):
+ return True
+
+ def write(self, s):
+ if self.closed:
+ raise ValueError("write to closed file")
+ if type(s) not in (unicode, str, bytearray):
+ # See issue #19481
+ if isinstance(s, unicode):
+ s = unicode.__getitem__(s, slice(None))
+ elif isinstance(s, str):
+ s = str.__str__(s)
+ elif isinstance(s, bytearray):
+ s = bytearray.__str__(s)
+ else:
+ raise TypeError('must be string, not ' + type(s).__name__)
+ return self.shell.write(s, self.tags)
+
+
+class PseudoInputFile(PseudoFile):
+
+ def __init__(self, shell, tags, encoding=None):
+ PseudoFile.__init__(self, shell, tags, encoding)
+ self._line_buffer = ''
+
+ def readable(self):
+ return True
+
+ def read(self, size=-1):
+ if self.closed:
+ raise ValueError("read from closed file")
+ if size is None:
+ size = -1
+ elif not isinstance(size, int):
+ raise TypeError('must be int, not ' + type(size).__name__)
+ result = self._line_buffer
+ self._line_buffer = ''
+ if size < 0:
+ while True:
+ line = self.shell.readline()
+ if not line: break
+ result += line
+ else:
+ while len(result) < size:
+ line = self.shell.readline()
+ if not line: break
+ result += line
+ self._line_buffer = result[size:]
+ result = result[:size]
+ return result
+
+ def readline(self, size=-1):
+ if self.closed:
+ raise ValueError("read from closed file")
+ if size is None:
+ size = -1
+ elif not isinstance(size, int):
+ raise TypeError('must be int, not ' + type(size).__name__)
+ line = self._line_buffer or self.shell.readline()
+ if size < 0:
+ size = len(line)
+ eol = line.find('\n', 0, size)
+ if eol >= 0:
+ size = eol + 1
+ self._line_buffer = line[size:]
+ return line[:size]
+
+ def close(self):
+ self.shell.close()
+
+
+def fix_x11_paste(root):
+ "Make paste replace selection on x11. See issue #5124."
+ if root._windowingsystem == 'x11':
+ for cls in 'Text', 'Entry', 'Spinbox':
+ root.bind_class(
+ cls,
+ '<<Paste>>',
+ 'catch {%W delete sel.first sel.last}\n' +
+ root.bind_class(cls, '<<Paste>>'))
+
+
+usage_msg = """\
+
+USAGE: idle [-deins] [-t title] [file]*
+ idle [-dns] [-t title] (-c cmd | -r file) [arg]*
+ idle [-dns] [-t title] - [arg]*
+
+ -h print this help message and exit
+ -n run IDLE without a subprocess (see Help/IDLE Help for details)
+
+The following options will override the IDLE 'settings' configuration:
+
+ -e open an edit window
+ -i open a shell window
+
+The following options imply -i and will open a shell:
+
+ -c cmd run the command in a shell, or
+ -r file run script from file
+
+ -d enable the debugger
+ -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
+ -t title set title of shell window
+
+A default edit window will be bypassed when -c, -r, or - are used.
+
+[arg]* are passed to the command (-c) or script (-r) in sys.argv[1:].
+
+Examples:
+
+idle
+ Open an edit window or shell depending on IDLE's configuration.
+
+idle foo.py foobar.py
+ Edit the files, also open a shell if configured to start with shell.
+
+idle -est "Baz" foo.py
+ Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell
+ window with the title "Baz".
+
+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].
+
+idle -d -s -r foo.py "Hello World"
+ Open a shell window, run a startup script, enable the debugger, and
+ 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"
+ Open a shell window, run the script piped in, passing '' in sys.argv[0]
+ and "foobar" in sys.argv[1].
+"""
+
+def main():
+ global flist, root, use_subprocess
+
+ capture_warnings(True)
+ use_subprocess = True
+ enable_shell = False
+ enable_edit = False
+ debug = False
+ cmd = None
+ script = None
+ startup = False
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:")
+ except getopt.error as msg:
+ print("Error: %s\n%s" % (msg, usage_msg), file=sys.stderr)
+ sys.exit(2)
+ for o, a in opts:
+ if o == '-c':
+ cmd = a
+ enable_shell = True
+ if o == '-d':
+ debug = True
+ enable_shell = True
+ if o == '-e':
+ enable_edit = True
+ if o == '-h':
+ sys.stdout.write(usage_msg)
+ sys.exit()
+ if o == '-i':
+ enable_shell = True
+ if o == '-n':
+ use_subprocess = False
+ if o == '-r':
+ script = a
+ if os.path.isfile(script):
+ pass
+ else:
+ print("No script file: ", script, file=sys.stderr)
+ sys.exit()
+ enable_shell = True
+ if o == '-s':
+ startup = True
+ enable_shell = True
+ if o == '-t':
+ PyShell.shell_title = a
+ enable_shell = True
+ if args and args[0] == '-':
+ cmd = sys.stdin.read()
+ enable_shell = True
+ # process sys.argv and sys.path:
+ for i in range(len(sys.path)):
+ sys.path[i] = os.path.abspath(sys.path[i])
+ if args and args[0] == '-':
+ sys.argv = [''] + args[1:]
+ elif cmd:
+ sys.argv = ['-c'] + args
+ elif script:
+ sys.argv = [script] + args
+ elif args:
+ enable_edit = True
+ pathx = []
+ for filename in args:
+ pathx.append(os.path.dirname(filename))
+ for dir in pathx:
+ dir = os.path.abspath(dir)
+ if dir not in sys.path:
+ sys.path.insert(0, dir)
+ else:
+ dir = os.getcwd()
+ if not dir 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 enable_edit
+
+ # start editor and/or shell windows:
+ root = Tk(className="Idle")
+ root.withdraw()
+
+ # set application icon
+ icondir = os.path.join(os.path.dirname(__file__), 'Icons')
+ if system() == 'Windows':
+ iconfile = os.path.join(icondir, 'idle.ico')
+ root.wm_iconbitmap(default=iconfile)
+ elif TkVersion >= 8.5:
+ ext = '.png' if TkVersion >= 8.6 else '.gif'
+ iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext))
+ for size in (16, 32, 48)]
+ icons = [PhotoImage(file=iconfile) for iconfile in iconfiles]
+ root.tk.call('wm', 'iconphoto', str(root), "-default", *icons)
+
+ fixwordbreaks(root)
+ fix_x11_paste(root)
+ flist = PyShellFileList(root)
+ macosxSupport.setupApp(root, flist)
+
+ if macosxSupport.isAquaTk():
+ # There are some screwed up <2> class bindings for text
+ # widgets defined in Tk which we need to do away with.
+ # See issue #24801.
+ root.unbind_class('Text', '<B2>')
+ root.unbind_class('Text', '<B2-Motion>')
+ root.unbind_class('Text', '<<PasteSelection>>')
+
+ if enable_edit:
+ if not (cmd or script):
+ 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:
+ shell = flist.open_shell()
+ if not shell:
+ return # couldn't open shell
+ if macosxSupport.isAquaTk() and flist.dict:
+ # On OSX: when the user has double-clicked on a file that causes
+ # IDLE to be launched the shell window will open just in front of
+ # the file she wants to see. Lower the interpreter window when
+ # there are open files.
+ shell.top.lower()
+ else:
+ shell = flist.pyshell
+
+ # Handle remaining options. If any of these are set, enable_shell
+ # was set also, so shell must be true to reach here.
+ if debug:
+ shell.open_debugger()
+ if startup:
+ filename = os.environ.get("IDLESTARTUP") or \
+ os.environ.get("PYTHONSTARTUP")
+ if filename and os.path.isfile(filename):
+ shell.interp.execfile(filename)
+ if cmd or script:
+ shell.interp.runcommand("""if 1:
+ import sys as _sys
+ _sys.argv = %r
+ del _sys
+ \n""" % (sys.argv,))
+ if cmd:
+ shell.interp.execsource(cmd)
+ elif script:
+ shell.interp.prepend_syspath(script)
+ shell.interp.execfile(script)
+ elif shell:
+ # If there is a shell window and no cmd or script in progress,
+ # check for problematic OS X Tk versions and print a warning
+ # message in the IDLE shell window; this is less intrusive
+ # than always opening a separate window.
+ tkversionwarning = macosxSupport.tkVersionWarning(root)
+ if tkversionwarning:
+ shell.interp.runcommand("print('%s')" % tkversionwarning)
+
+ while flist.inversedict: # keep IDLE running while files are open.
+ root.mainloop()
+ root.destroy()
+ capture_warnings(False)
+
+if __name__ == "__main__":
+ sys.modules['PyShell'] = sys.modules['__main__']
+ main()
+
+capture_warnings(False) # Make sure turned off; see issue 18081
diff --git a/lib/python2.7/idlelib/README.txt b/lib/python2.7/idlelib/README.txt
new file mode 100644
index 0000000..bc169c8
--- /dev/null
+++ b/lib/python2.7/idlelib/README.txt
@@ -0,0 +1,230 @@
+README.txt: an index to idlelib files and the IDLE menu.
+
+IDLE is Python's Integrated Development and Learning
+Environment. The user documentation is part of the Library Reference and
+is available in IDLE by selecting Help => IDLE Help. This README documents
+idlelib for IDLE developers and curious users.
+
+IDLELIB FILES lists files alphabetically by category,
+with a short description of each.
+
+IDLE MENU show the menu tree, annotated with the module
+or module object that implements the corresponding function.
+
+This file is descriptive, not prescriptive, and may have errors
+and omissions and lag behind changes in idlelib.
+
+
+IDLELIB FILES
+Implemetation files not in IDLE MENU are marked (nim).
+Deprecated files and objects are listed separately as the end.
+
+Startup
+-------
+__init__.py # import, does nothing
+__main__.py # -m, starts IDLE
+idle.bat
+idle.py
+idle.pyw
+
+Implementation
+--------------
+AutoComplete.py # Complete attribute names or filenames.
+AutoCompleteWindow.py # Display completions.
+AutoExpand.py # Expand word with previous word in file.
+Bindings.py # Define most of IDLE menu.
+CallTipWindow.py # Display calltip.
+CallTips.py # Create calltip text.
+ClassBrowser.py # Create module browser window.
+CodeContext.py # Show compound statement headers otherwise not visible.
+ColorDelegator.py # Colorize text (nim).
+Debugger.py # Debug code run from editor; show window.
+Delegator.py # Define base class for delegators (nim).
+EditorWindow.py # Define most of editor and utility functions.
+FileList.py # Open files and manage list of open windows (nim).
+FormatParagraph.py# Re-wrap multiline strings and comments.
+GrepDialog.py # Find all occurrences of pattern in multiple files.
+HyperParser.py # Parse code around a given index.
+IOBinding.py # Open, read, and write files
+IdleHistory.py # Get previous or next user input in shell (nim)
+MultiCall.py # Wrap tk widget to allow multiple calls per event (nim).
+MultiStatusBar.py # Define status bar for windows (nim).
+ObjectBrowser.py # Define class used in StackViewer (nim).
+OutputWindow.py # Create window for grep output.
+ParenMatch.py # Match fenceposts: (), [], and {}.
+PathBrowser.py # Create path browser window.
+Percolator.py # Manage delegator stack (nim).
+PyParse.py # Give information on code indentation
+PyShell.py # Start IDLE, manage shell, complete editor window
+RemoteDebugger.py # Debug code run in remote process.
+RemoteObjectBrowser.py # Communicate objects between processes with rpc (nim).
+ReplaceDialog.py # Search and replace pattern in text.
+RstripExtension.py# Strip trailing whitespace
+ScriptBinding.py # Check and run user code.
+ScrolledList.py # Define ScrolledList widget for IDLE (nim).
+SearchDialog.py # Search for pattern in text.
+SearchDialogBase.py # Define base for search, replace, and grep dialogs.
+SearchEngine.py # Define engine for all 3 search dialogs.
+StackViewer.py # View stack after exception.
+TreeWidget.py # Define tree widger, used in browsers (nim).
+UndoDelegator.py # Manage undo stack.
+WidgetRedirector.py # Intercept widget subcommands (for percolator) (nim).
+WindowList.py # Manage window list and define listed top level.
+ZoomHeight.py # Zoom window to full height of screen.
+aboutDialog.py # Display About IDLE dialog.
+configDialog.py # Display user configuration dialogs.
+configHandler.py # Load, fetch, and save configuration (nim).
+configHelpSourceEdit.py # Specify help source.
+configSectionNameDialog.py # Spefify user config section name
+dynOptionMenuWidget.py # define mutable OptionMenu widget (nim).
+help.py # Display IDLE's html doc.
+keybindingDialog.py # Change keybindings.
+macosxSupport.py # Help IDLE run on Macs (nim).
+rpc.py # Commuicate between idle and user processes (nim).
+run.py # Manage user code execution subprocess.
+tabbedpages.py # Define tabbed pages widget (nim).
+textView.py # Define read-only text widget (nim).
+
+Configuration
+-------------
+config-extensions.def # Defaults for extensions
+config-highlight.def # Defaults for colorizing
+config-keys.def # Defaults for key bindings
+config-main.def # Defai;ts fpr font and geneal
+
+Text
+----
+CREDITS.txt # not maintained, displayed by About IDLE
+HISTORY.txt # NEWS up to July 2001
+NEWS.txt # commits, displayed by About IDLE
+README.txt # this file, displeyed by About IDLE
+TODO.txt # needs review
+extend.txt # about writing extensions
+help.html # copy of idle.html in docs, displayed by IDLE Help
+
+Subdirectories
+--------------
+Icons # small image files
+idle_test # files for human test and automated unit tests
+
+Unused and Deprecated files and objects (nim)
+---------------------------------------------
+EditorWindow.py: Helpdialog and helpDialog
+ToolTip.py: unused.
+help.txt
+idlever.py
+
+
+IDLE MENUS
+Top level items and most submenu items are defined in Bindings.
+Extenstions add submenu items when active. The names given are
+found, quoted, in one of these modules, paired with a '<<pseudoevent>>'.
+Each pseudoevent is bound to an event handler. Some event handlers
+call another function that does the actual work. The annotations below
+are intended to at least give the module where the actual work is done.
+
+File # IOBindig except as noted
+ New File
+ Open... # IOBinding.open
+ Open Module
+ Recent Files
+ Class Browser # Class Browser
+ Path Browser # Path Browser
+ ---
+ Save # IDBinding.save
+ Save As... # IOBinding.save_as
+ Save Copy As... # IOBindling.save_a_copy
+ ---
+ Print Window # IOBinding.print_window
+ ---
+ Close
+ Exit
+
+Edit
+ Undo # undoDelegator
+ Redo # undoDelegator
+ ---
+ Cut
+ Copy
+ Paste
+ Select All
+ --- # Next 5 items use SearchEngine; dialogs use SearchDialogBase
+ Find # Search Dialog
+ Find Again
+ Find Selection
+ Find in Files... # GrepDialog
+ Replace... # ReplaceDialog
+ Go to Line
+ Show Completions # AutoComplete extension and AutoCompleteWidow (&HP)
+ Expand Word # AutoExpand extension
+ Show call tip # Calltips extension and CalltipWindow (& Hyperparser)
+ Show surrounding parens # ParenMatch (& Hyperparser)
+
+Shell # PyShell
+ View Last Restart # PyShell.PyShell.view_restart_mark
+ Restart Shell # PyShell.PyShell.restart_shell
+ Interrupt Execution # pyshell.PyShell.cancel_callback
+
+Debug (Shell only)
+ Go to File/Line
+ Debugger # Debugger, RemoteDebugger, PyShell.toggle_debuger
+ Stack Viewer # StackViewer, PyShell.open_stack_viewer
+ Auto-open Stack Viewer # StackViewer
+
+Format (Editor only)
+ Indent Region
+ Dedent Region
+ Comment Out Region
+ Uncomment Region
+ Tabify Region
+ Untabify Region
+ Toggle Tabs
+ New Indent Width
+ Format Paragraph # FormatParagraph extension
+ ---
+ Strip tailing whitespace # RstripExtension extension
+
+Run (Editor only)
+ Python Shell # PyShell
+ ---
+ Check Module # ScriptBinding
+ Run Module # ScriptBinding
+
+Options
+ Configure IDLE # configDialog
+ (tabs in the dialog)
+ Font tab # onfig-main.def
+ Highlight tab # configSectionNameDialog, config-highlight.def
+ Keys tab # keybindingDialog, configSectionNameDialog, onfig-keus.def
+ General tab # configHelpSourceEdit, config-main.def
+ Configure Extensions # configDialog
+ Xyz tab # xyz.py, config-extensions.def
+ ---
+ Code Context (editor only) # CodeContext extension
+
+Window
+ Zoomheight # ZoomHeight extension
+ ---
+ <open windows> # WindowList
+
+Help
+ About IDLE # aboutDialog
+ ---
+ IDLE Help # help
+ Python Doc
+ Turtle Demo
+ ---
+ <other help sources>
+
+<Context Menu> (right click)
+Defined in EditorWindow, PyShell, Output
+ Cut
+ Copy
+ Paste
+ ---
+ Go to file/line (shell and output only)
+ Set Breakpoint (editor only)
+ Clear Breakpoint (editor only)
+ Defined in Debugger
+ Go to source line
+ Show stack frame
diff --git a/lib/python2.7/idlelib/RemoteDebugger.py b/lib/python2.7/idlelib/RemoteDebugger.py
new file mode 100644
index 0000000..8c71a21
--- /dev/null
+++ b/lib/python2.7/idlelib/RemoteDebugger.py
@@ -0,0 +1,379 @@
+"""Support for remote Python debugging.
+
+Some ASCII art to describe the structure:
+
+ IN PYTHON SUBPROCESS # IN IDLE PROCESS
+ #
+ # oid='gui_adapter'
+ +----------+ # +------------+ +-----+
+ | GUIProxy |--remote#call-->| GUIAdapter |--calls-->| GUI |
++-----+--calls-->+----------+ # +------------+ +-----+
+| Idb | # /
++-----+<-calls--+------------+ # +----------+<--calls-/
+ | IdbAdapter |<--remote#call--| IdbProxy |
+ +------------+ # +----------+
+ oid='idb_adapter' #
+
+The purpose of the Proxy and Adapter classes is to translate certain
+arguments and return values that cannot be transported through the RPC
+barrier, in particular frame and traceback objects.
+
+"""
+
+import types
+from idlelib import Debugger
+
+debugging = 0
+
+idb_adap_oid = "idb_adapter"
+gui_adap_oid = "gui_adapter"
+
+#=======================================
+#
+# In the PYTHON subprocess:
+
+frametable = {}
+dicttable = {}
+codetable = {}
+tracebacktable = {}
+
+def wrap_frame(frame):
+ fid = id(frame)
+ frametable[fid] = frame
+ return fid
+
+def wrap_info(info):
+ "replace info[2], a traceback instance, by its ID"
+ if info is None:
+ return None
+ else:
+ traceback = info[2]
+ assert isinstance(traceback, types.TracebackType)
+ traceback_id = id(traceback)
+ tracebacktable[traceback_id] = traceback
+ modified_info = (info[0], info[1], traceback_id)
+ return modified_info
+
+class GUIProxy:
+
+ def __init__(self, conn, gui_adap_oid):
+ self.conn = conn
+ self.oid = gui_adap_oid
+
+ def interaction(self, message, frame, info=None):
+ # calls rpc.SocketIO.remotecall() via run.MyHandler instance
+ # pass frame and traceback object IDs instead of the objects themselves
+ self.conn.remotecall(self.oid, "interaction",
+ (message, wrap_frame(frame), wrap_info(info)),
+ {})
+
+class IdbAdapter:
+
+ def __init__(self, idb):
+ self.idb = idb
+
+ #----------called by an IdbProxy----------
+
+ def set_step(self):
+ self.idb.set_step()
+
+ def set_quit(self):
+ self.idb.set_quit()
+
+ def set_continue(self):
+ self.idb.set_continue()
+
+ def set_next(self, fid):
+ frame = frametable[fid]
+ self.idb.set_next(frame)
+
+ def set_return(self, fid):
+ frame = frametable[fid]
+ 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(frame2), k) for frame2, k in stack]
+ ##print >>sys.__stderr__, "get_stack() ->", stack
+ return stack, i
+
+ def run(self, cmd):
+ import __main__
+ self.idb.run(cmd, __main__.__dict__)
+
+ def set_break(self, filename, lineno):
+ msg = self.idb.set_break(filename, lineno)
+ return msg
+
+ def clear_break(self, filename, lineno):
+ msg = self.idb.clear_break(filename, lineno)
+ return msg
+
+ def clear_all_file_breaks(self, filename):
+ msg = self.idb.clear_all_file_breaks(filename)
+ return msg
+
+ #----------called by a FrameProxy----------
+
+ def frame_attr(self, fid, name):
+ frame = frametable[fid]
+ return getattr(frame, name)
+
+ def frame_globals(self, fid):
+ frame = frametable[fid]
+ dict = frame.f_globals
+ did = id(dict)
+ dicttable[did] = dict
+ return did
+
+ def frame_locals(self, fid):
+ frame = frametable[fid]
+ dict = frame.f_locals
+ did = id(dict)
+ dicttable[did] = dict
+ return did
+
+ def frame_code(self, fid):
+ frame = frametable[fid]
+ code = frame.f_code
+ cid = id(code)
+ codetable[cid] = code
+ return cid
+
+ #----------called by a CodeProxy----------
+
+ def code_name(self, cid):
+ code = codetable[cid]
+ return code.co_name
+
+ def code_filename(self, cid):
+ code = codetable[cid]
+ return code.co_filename
+
+ #----------called by a DictProxy----------
+
+ def dict_keys(self, did):
+ dict = dicttable[did]
+ return dict.keys()
+
+ def dict_item(self, did, key):
+ dict = dicttable[did]
+ value = dict[key]
+ value = repr(value)
+ return value
+
+#----------end class IdbAdapter----------
+
+
+def start_debugger(rpchandler, gui_adap_oid):
+ """Start the debugger and its RPC link in the Python subprocess
+
+ Start the subprocess side of the split debugger and set up that side of the
+ RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter
+ objects and linking them together. Register the IdbAdapter with the
+ RPCServer to handle RPC requests from the split debugger GUI via the
+ IdbProxy.
+
+ """
+ gui_proxy = GUIProxy(rpchandler, gui_adap_oid)
+ idb = Debugger.Idb(gui_proxy)
+ idb_adap = IdbAdapter(idb)
+ rpchandler.register(idb_adap_oid, idb_adap)
+ return idb_adap_oid
+
+
+#=======================================
+#
+# In the IDLE process:
+
+
+class FrameProxy:
+
+ def __init__(self, conn, fid):
+ self._conn = conn
+ self._fid = fid
+ self._oid = "idb_adapter"
+ self._dictcache = {}
+
+ def __getattr__(self, name):
+ if name[:1] == "_":
+ raise AttributeError, name
+ if name == "f_code":
+ return self._get_f_code()
+ if name == "f_globals":
+ return self._get_f_globals()
+ if name == "f_locals":
+ return self._get_f_locals()
+ return self._conn.remotecall(self._oid, "frame_attr",
+ (self._fid, name), {})
+
+ def _get_f_code(self):
+ cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
+ return CodeProxy(self._conn, self._oid, cid)
+
+ def _get_f_globals(self):
+ did = self._conn.remotecall(self._oid, "frame_globals",
+ (self._fid,), {})
+ return self._get_dict_proxy(did)
+
+ def _get_f_locals(self):
+ did = self._conn.remotecall(self._oid, "frame_locals",
+ (self._fid,), {})
+ return self._get_dict_proxy(did)
+
+ def _get_dict_proxy(self, did):
+ if did in self._dictcache:
+ return self._dictcache[did]
+ dp = DictProxy(self._conn, self._oid, did)
+ self._dictcache[did] = dp
+ return dp
+
+
+class CodeProxy:
+
+ def __init__(self, conn, oid, cid):
+ self._conn = conn
+ self._oid = oid
+ self._cid = cid
+
+ def __getattr__(self, name):
+ if name == "co_name":
+ return self._conn.remotecall(self._oid, "code_name",
+ (self._cid,), {})
+ if name == "co_filename":
+ return self._conn.remotecall(self._oid, "code_filename",
+ (self._cid,), {})
+
+
+class DictProxy:
+
+ def __init__(self, conn, oid, did):
+ self._conn = conn
+ self._oid = oid
+ self._did = did
+
+ def keys(self):
+ return self._conn.remotecall(self._oid, "dict_keys", (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
+
+
+class GUIAdapter:
+
+ def __init__(self, conn, gui):
+ self.conn = conn
+ self.gui = gui
+
+ def interaction(self, 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)
+
+
+class IdbProxy:
+
+ def __init__(self, conn, shell, oid):
+ self.oid = oid
+ self.conn = conn
+ self.shell = shell
+
+ def call(self, 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)
+ return value
+
+ def run(self, cmd, locals):
+ # Ignores locals on purpose!
+ seq = self.conn.asyncqueue(self.oid, "run", (cmd,), {})
+ self.shell.interp.active_seq = seq
+
+ def get_stack(self, frame, tbid):
+ # passing frame and traceback IDs, not the objects themselves
+ stack, i = self.call("get_stack", frame._fid, tbid)
+ stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
+ return stack, i
+
+ def set_continue(self):
+ self.call("set_continue")
+
+ def set_step(self):
+ self.call("set_step")
+
+ def set_next(self, frame):
+ self.call("set_next", frame._fid)
+
+ def set_return(self, frame):
+ self.call("set_return", frame._fid)
+
+ def set_quit(self):
+ self.call("set_quit")
+
+ def set_break(self, filename, lineno):
+ msg = self.call("set_break", filename, lineno)
+ return msg
+
+ def clear_break(self, filename, lineno):
+ msg = self.call("clear_break", filename, lineno)
+ return msg
+
+ def clear_all_file_breaks(self, filename):
+ msg = self.call("clear_all_file_breaks", filename)
+ return msg
+
+def start_remote_debugger(rpcclt, pyshell):
+ """Start the subprocess debugger, initialize the debugger GUI and RPC link
+
+ Request the RPCServer start the Python subprocess debugger and link. Set
+ up the Idle side of the split debugger by instantiating the IdbProxy,
+ debugger GUI, and debugger GUIAdapter objects and linking them together.
+
+ Register the GUIAdapter with the RPCClient to handle debugger GUI
+ interaction requests coming from the subprocess debugger via the GUIProxy.
+
+ The IdbAdapter will pass execution and environment requests coming from the
+ Idle debugger GUI to the subprocess debugger via the IdbProxy.
+
+ """
+ global idb_adap_oid
+
+ idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\
+ (gui_adap_oid,), {})
+ idb_proxy = IdbProxy(rpcclt, pyshell, idb_adap_oid)
+ gui = Debugger.Debugger(pyshell, idb_proxy)
+ gui_adap = GUIAdapter(rpcclt, gui)
+ rpcclt.register(gui_adap_oid, gui_adap)
+ return gui
+
+def close_remote_debugger(rpcclt):
+ """Shut down subprocess debugger and Idle side of debugger RPC link
+
+ Request that the RPCServer shut down the subprocess debugger and link.
+ Unregister the GUIAdapter, which will cause a GC on the Idle process
+ debugger and RPC link objects. (The second reference to the debugger GUI
+ is deleted in PyShell.close_remote_debugger().)
+
+ """
+ close_subprocess_debugger(rpcclt)
+ rpcclt.unregister(gui_adap_oid)
+
+def close_subprocess_debugger(rpcclt):
+ rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})
+
+def restart_subprocess_debugger(rpcclt):
+ idb_adap_oid_ret = rpcclt.remotecall("exec", "start_the_debugger",\
+ (gui_adap_oid,), {})
+ assert idb_adap_oid_ret == idb_adap_oid, 'Idb restarted with different oid'
diff --git a/lib/python2.7/idlelib/RemoteObjectBrowser.py b/lib/python2.7/idlelib/RemoteObjectBrowser.py
new file mode 100644
index 0000000..43e2c68
--- /dev/null
+++ b/lib/python2.7/idlelib/RemoteObjectBrowser.py
@@ -0,0 +1,36 @@
+from idlelib import rpc
+
+def remote_object_tree_item(item):
+ wrapper = WrappedObjectTreeItem(item)
+ oid = id(wrapper)
+ rpc.objecttable[oid] = wrapper
+ return oid
+
+class WrappedObjectTreeItem:
+ # Lives in PYTHON subprocess
+
+ def __init__(self, item):
+ self.__item = item
+
+ def __getattr__(self, name):
+ value = getattr(self.__item, name)
+ return value
+
+ def _GetSubList(self):
+ list = self.__item._GetSubList()
+ return map(remote_object_tree_item, list)
+
+class StubObjectTreeItem:
+ # Lives in IDLE process
+
+ def __init__(self, sockio, oid):
+ self.sockio = sockio
+ self.oid = oid
+
+ def __getattr__(self, name):
+ value = rpc.MethodProxy(self.sockio, self.oid, name)
+ return value
+
+ def _GetSubList(self):
+ list = self.sockio.remotecall(self.oid, "_GetSubList", (), {})
+ return [StubObjectTreeItem(self.sockio, oid) for oid in list]
diff --git a/lib/python2.7/idlelib/ReplaceDialog.py b/lib/python2.7/idlelib/ReplaceDialog.py
new file mode 100644
index 0000000..66a871a
--- /dev/null
+++ b/lib/python2.7/idlelib/ReplaceDialog.py
@@ -0,0 +1,220 @@
+from Tkinter import *
+
+from idlelib import SearchEngine
+from idlelib.SearchDialogBase import SearchDialogBase
+import re
+
+
+def replace(text):
+ root = text._root()
+ engine = SearchEngine.get(root)
+ if not hasattr(engine, "_replacedialog"):
+ engine._replacedialog = ReplaceDialog(root, engine)
+ dialog = engine._replacedialog
+ dialog.open(text)
+
+
+class ReplaceDialog(SearchDialogBase):
+
+ title = "Replace Dialog"
+ icon = "Replace"
+
+ def __init__(self, root, engine):
+ SearchDialogBase.__init__(self, root, engine)
+ self.replvar = StringVar(root)
+
+ def open(self, text):
+ SearchDialogBase.open(self, text)
+ try:
+ first = text.index("sel.first")
+ except TclError:
+ first = None
+ try:
+ last = text.index("sel.last")
+ except TclError:
+ last = None
+ first = first or text.index("insert")
+ last = last or first
+ self.show_hit(first, last)
+ self.ok = 1
+
+ def create_entries(self):
+ SearchDialogBase.create_entries(self)
+ self.replent = self.make_entry("Replace with:", self.replvar)[0]
+
+ def create_command_buttons(self):
+ SearchDialogBase.create_command_buttons(self)
+ self.make_button("Find", self.find_it)
+ self.make_button("Replace", self.replace_it)
+ self.make_button("Replace+Find", self.default_command, 1)
+ self.make_button("Replace All", self.replace_all)
+
+ def find_it(self, event=None):
+ self.do_find(0)
+
+ def replace_it(self, event=None):
+ if self.do_find(self.ok):
+ self.do_replace()
+
+ def default_command(self, event=None):
+ if self.do_find(self.ok):
+ if self.do_replace(): # Only find next match if replace succeeded.
+ # A bad re can cause it to fail.
+ self.do_find(0)
+
+ def _replace_expand(self, m, repl):
+ """ Helper function for expanding a regular expression
+ in the replace field, if needed. """
+ if self.engine.isre():
+ try:
+ new = m.expand(repl)
+ except re.error:
+ self.engine.report_error(repl, 'Invalid Replace Expression')
+ new = None
+ else:
+ new = repl
+ return new
+
+ def replace_all(self, event=None):
+ prog = self.engine.getprog()
+ if not prog:
+ return
+ repl = self.replvar.get()
+ text = self.text
+ res = self.engine.search_text(text, prog)
+ if not res:
+ text.bell()
+ return
+ text.tag_remove("sel", "1.0", "end")
+ text.tag_remove("hit", "1.0", "end")
+ line = res[0]
+ col = res[1].start()
+ if self.engine.iswrap():
+ line = 1
+ col = 0
+ ok = 1
+ first = last = None
+ # XXX ought to replace circular instead of top-to-bottom when wrapping
+ text.undo_block_start()
+ while 1:
+ res = self.engine.search_forward(text, prog, line, col, 0, ok)
+ if not res:
+ break
+ line, m = res
+ chars = text.get("%d.0" % line, "%d.0" % (line+1))
+ orig = m.group()
+ new = self._replace_expand(m, repl)
+ if new is None:
+ break
+ i, j = m.span()
+ first = "%d.%d" % (line, i)
+ last = "%d.%d" % (line, j)
+ if new == orig:
+ text.mark_set("insert", last)
+ else:
+ text.mark_set("insert", first)
+ if first != last:
+ text.delete(first, last)
+ if new:
+ text.insert(first, new)
+ col = i + len(new)
+ ok = 0
+ text.undo_block_stop()
+ if first and last:
+ self.show_hit(first, last)
+ self.close()
+
+ def do_find(self, ok=0):
+ if not self.engine.getprog():
+ return False
+ text = self.text
+ res = self.engine.search_text(text, None, ok)
+ if not res:
+ text.bell()
+ return False
+ line, m = res
+ i, j = m.span()
+ first = "%d.%d" % (line, i)
+ last = "%d.%d" % (line, j)
+ self.show_hit(first, last)
+ self.ok = 1
+ return True
+
+ def do_replace(self):
+ prog = self.engine.getprog()
+ if not prog:
+ return False
+ text = self.text
+ try:
+ first = pos = text.index("sel.first")
+ last = text.index("sel.last")
+ except TclError:
+ pos = None
+ if not pos:
+ first = last = pos = text.index("insert")
+ line, col = SearchEngine.get_line_col(pos)
+ chars = text.get("%d.0" % line, "%d.0" % (line+1))
+ m = prog.match(chars, col)
+ if not prog:
+ return False
+ new = self._replace_expand(m, self.replvar.get())
+ if new is None:
+ return False
+ text.mark_set("insert", first)
+ text.undo_block_start()
+ if m.group():
+ text.delete(first, last)
+ if new:
+ text.insert(first, new)
+ text.undo_block_stop()
+ self.show_hit(first, text.index("insert"))
+ self.ok = 0
+ return True
+
+ def show_hit(self, first, last):
+ text = self.text
+ text.mark_set("insert", first)
+ text.tag_remove("sel", "1.0", "end")
+ text.tag_add("sel", first, last)
+ text.tag_remove("hit", "1.0", "end")
+ if first == last:
+ text.tag_add("hit", first)
+ else:
+ text.tag_add("hit", first, last)
+ text.see("insert")
+ text.update_idletasks()
+
+ def close(self, event=None):
+ SearchDialogBase.close(self, event)
+ self.text.tag_remove("hit", "1.0", "end")
+
+def _replace_dialog(parent):
+ root = Tk()
+ root.title("Test ReplaceDialog")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+
+ # mock undo delegator methods
+ def undo_block_start():
+ pass
+
+ def undo_block_stop():
+ pass
+
+ text = Text(root)
+ text.undo_block_start = undo_block_start
+ text.undo_block_stop = undo_block_stop
+ text.pack()
+ text.insert("insert","This is a sample string.\n"*10)
+
+ def show_replace():
+ text.tag_add(SEL, "1.0", END)
+ replace(text)
+ text.tag_remove(SEL, "1.0", END)
+
+ button = Button(root, text="Replace", command=show_replace)
+ button.pack()
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_replace_dialog)
diff --git a/lib/python2.7/idlelib/RstripExtension.py b/lib/python2.7/idlelib/RstripExtension.py
new file mode 100644
index 0000000..2ce3c7e
--- /dev/null
+++ b/lib/python2.7/idlelib/RstripExtension.py
@@ -0,0 +1,33 @@
+'Provides "Strip trailing whitespace" under the "Format" menu.'
+
+class RstripExtension:
+
+ menudefs = [
+ ('format', [None, ('Strip trailing whitespace', '<<do-rstrip>>'), ] ), ]
+
+ def __init__(self, editwin):
+ self.editwin = editwin
+ self.editwin.text.bind("<<do-rstrip>>", self.do_rstrip)
+
+ def do_rstrip(self, event=None):
+
+ text = self.editwin.text
+ undo = self.editwin.undo
+
+ undo.undo_block_start()
+
+ end_line = int(float(text.index('end')))
+ for cur in range(1, end_line):
+ txt = text.get('%i.0' % cur, '%i.end' % cur)
+ raw = len(txt)
+ cut = len(txt.rstrip())
+ # Since text.delete() marks file as changed, even if not,
+ # only call it when needed to actually delete something.
+ if cut < raw:
+ text.delete('%i.%i' % (cur, cut), '%i.end' % cur)
+
+ undo.undo_block_stop()
+
+if __name__ == "__main__":
+ import unittest
+ unittest.main('idlelib.idle_test.test_rstrip', verbosity=2, exit=False)
diff --git a/lib/python2.7/idlelib/ScriptBinding.py b/lib/python2.7/idlelib/ScriptBinding.py
new file mode 100644
index 0000000..0309a8a
--- /dev/null
+++ b/lib/python2.7/idlelib/ScriptBinding.py
@@ -0,0 +1,222 @@
+"""Extension to execute code outside the Python shell window.
+
+This adds the following commands:
+
+- Check module does a full syntax check of the current module.
+ It also runs the tabnanny to catch any inconsistent tabs.
+
+- Run module executes the module's code in the __main__ namespace. The window
+ must have been saved previously. The module is added to sys.modules, and is
+ also added to the __main__ namespace.
+
+XXX GvR Redesign this interface (yet again) as follows:
+
+- Present a dialog box for ``Run Module''
+
+- Allow specify command line arguments in the dialog box
+
+"""
+
+import os
+import re
+import string
+import tabnanny
+import tokenize
+import tkMessageBox
+from idlelib import PyShell
+
+from idlelib.configHandler import idleConf
+from idlelib import macosxSupport
+
+IDENTCHARS = string.ascii_letters + string.digits + "_"
+
+indent_message = """Error: Inconsistent indentation detected!
+
+1) Your indentation is outright incorrect (easy to fix), OR
+
+2) Your indentation mixes tabs and spaces.
+
+To fix case 2, change all tabs to spaces by using Edit->Select All followed \
+by Format->Untabify Region and specify the number of columns used by each tab.
+"""
+
+class ScriptBinding:
+
+ menudefs = [
+ ('run', [None,
+ ('Check Module', '<<check-module>>'),
+ ('Run Module', '<<run-module>>'), ]), ]
+
+ def __init__(self, editwin):
+ self.editwin = editwin
+ # Provide instance variables referenced by Debugger
+ # XXX This should be done differently
+ self.flist = self.editwin.flist
+ self.root = self.editwin.root
+
+ if macosxSupport.isCocoaTk():
+ self.editwin.text_frame.bind('<<run-module-event-2>>', self._run_module_event)
+
+ def check_module_event(self, event):
+ filename = self.getfilename()
+ if not filename:
+ return 'break'
+ if not self.checksyntax(filename):
+ return 'break'
+ if not self.tabnanny(filename):
+ return 'break'
+
+ def tabnanny(self, filename):
+ f = open(filename, 'r')
+ try:
+ tabnanny.process_tokens(tokenize.generate_tokens(f.readline))
+ except tokenize.TokenError as msg:
+ msgtxt, (lineno, start) = msg.args
+ self.editwin.gotoline(lineno)
+ self.errorbox("Tabnanny Tokenizing Error",
+ "Token Error: %s" % msgtxt)
+ return False
+ except tabnanny.NannyNag as nag:
+ # The error messages from tabnanny are too confusing...
+ self.editwin.gotoline(nag.get_lineno())
+ self.errorbox("Tab/space error", indent_message)
+ return False
+ return True
+
+ def checksyntax(self, filename):
+ self.shell = shell = self.flist.open_shell()
+ saved_stream = shell.get_warning_stream()
+ shell.set_warning_stream(shell.stderr)
+ with open(filename, 'r') as f:
+ source = f.read()
+ 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
+ text.tag_remove("ERROR", "1.0", "end")
+ try:
+ try:
+ # If successful, return the compiled code
+ return compile(source, filename, "exec")
+ except (SyntaxError, OverflowError, ValueError) as 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
+ 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.
+
+ First check the syntax. If OK, make sure the shell is active and
+ then transfer the arguments, set the run environment's working
+ directory to the directory of the module being executed and also
+ add that directory to its sys.path if not already included.
+
+ """
+ filename = self.getfilename()
+ if not filename:
+ return 'break'
+ code = self.checksyntax(filename)
+ if not code:
+ return 'break'
+ if not self.tabnanny(filename):
+ return 'break'
+ interp = self.shell.interp
+ if PyShell.use_subprocess:
+ interp.restart_subprocess(with_cwd=False, filename=code.co_filename)
+ dirname = os.path.dirname(filename)
+ # XXX Too often this discards arguments the user just set...
+ interp.runcommand("""if 1:
+ __file__ = {filename!r}
+ import sys as _sys
+ from os.path import basename as _basename
+ if (not _sys.argv or
+ _basename(_sys.argv[0]) != _basename(__file__)):
+ _sys.argv = [__file__]
+ import os as _os
+ _os.chdir({dirname!r})
+ del _sys, _basename, _os
+ \n""".format(filename=filename, dirname=dirname))
+ interp.prepend_syspath(filename)
+ # 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)
+ return 'break'
+
+ if macosxSupport.isCocoaTk():
+ # Tk-Cocoa in MacOSX is broken until at least
+ # Tk 8.5.9, and without this rather
+ # crude workaround IDLE would hang when a user
+ # tries to run a module using the keyboard shortcut
+ # (the menu item works fine).
+ _run_module_event = run_module_event
+
+ def run_module_event(self, event):
+ self.editwin.text_frame.after(200,
+ lambda: self.editwin.text_frame.event_generate('<<run-module-event-2>>'))
+ return 'break'
+
+ def getfilename(self):
+ """Get source filename. If not saved, offer to save (or create) file
+
+ The debugger requires a source file. Make sure there is one, and that
+ the current version of the source buffer has been saved. If the user
+ declines to save or cancels the Save As dialog, return None.
+
+ If the user has configured IDLE for Autosave, the file will be
+ silently saved if it already exists and is dirty.
+
+ """
+ filename = self.editwin.io.filename
+ if not self.editwin.get_saved():
+ autosave = idleConf.GetOption('main', 'General',
+ 'autosave', type='bool')
+ if autosave and filename:
+ self.editwin.io.save(None)
+ else:
+ confirm = self.ask_save_dialog()
+ self.editwin.text.focus_set()
+ if confirm:
+ self.editwin.io.save(None)
+ filename = self.editwin.io.filename
+ else:
+ filename = None
+ return filename
+
+ def ask_save_dialog(self):
+ msg = "Source Must Be Saved\n" + 5*' ' + "OK to Save?"
+ confirm = tkMessageBox.askokcancel(title="Save Before Run or Check",
+ message=msg,
+ default=tkMessageBox.OK,
+ parent=self.editwin.text)
+ return confirm
+
+ def errorbox(self, title, message):
+ # XXX This should really be a function of EditorWindow...
+ tkMessageBox.showerror(title, message, parent=self.editwin.text)
+ self.editwin.text.focus_set()
diff --git a/lib/python2.7/idlelib/ScrolledList.py b/lib/python2.7/idlelib/ScrolledList.py
new file mode 100644
index 0000000..fd9f0ff
--- /dev/null
+++ b/lib/python2.7/idlelib/ScrolledList.py
@@ -0,0 +1,145 @@
+from Tkinter import *
+from idlelib import macosxSupport
+
+class ScrolledList:
+
+ default = "(None)"
+
+ def __init__(self, master, **options):
+ # Create top frame, with scrollbar and listbox
+ self.master = master
+ self.frame = frame = Frame(master)
+ self.frame.pack(fill="both", expand=1)
+ self.vbar = vbar = Scrollbar(frame, name="vbar")
+ self.vbar.pack(side="right", fill="y")
+ self.listbox = listbox = Listbox(frame, exportselection=0,
+ background="white")
+ if options:
+ listbox.configure(options)
+ listbox.pack(expand=1, fill="both")
+ # Tie listbox and scrollbar together
+ vbar["command"] = listbox.yview
+ listbox["yscrollcommand"] = vbar.set
+ # Bind events to the list box
+ listbox.bind("<ButtonRelease-1>", self.click_event)
+ listbox.bind("<Double-ButtonRelease-1>", self.double_click_event)
+ if macosxSupport.isAquaTk():
+ listbox.bind("<ButtonPress-2>", self.popup_event)
+ listbox.bind("<Control-Button-1>", self.popup_event)
+ else:
+ listbox.bind("<ButtonPress-3>", self.popup_event)
+ listbox.bind("<Key-Up>", self.up_event)
+ listbox.bind("<Key-Down>", self.down_event)
+ # Mark as empty
+ self.clear()
+
+ def close(self):
+ self.frame.destroy()
+
+ def clear(self):
+ self.listbox.delete(0, "end")
+ self.empty = 1
+ self.listbox.insert("end", self.default)
+
+ def append(self, item):
+ if self.empty:
+ self.listbox.delete(0, "end")
+ self.empty = 0
+ self.listbox.insert("end", str(item))
+
+ def get(self, index):
+ return self.listbox.get(index)
+
+ def click_event(self, event):
+ self.listbox.activate("@%d,%d" % (event.x, event.y))
+ index = self.listbox.index("active")
+ self.select(index)
+ self.on_select(index)
+ return "break"
+
+ def double_click_event(self, event):
+ index = self.listbox.index("active")
+ self.select(index)
+ self.on_double(index)
+ return "break"
+
+ menu = None
+
+ def popup_event(self, event):
+ if not self.menu:
+ self.make_menu()
+ menu = self.menu
+ self.listbox.activate("@%d,%d" % (event.x, event.y))
+ index = self.listbox.index("active")
+ self.select(index)
+ menu.tk_popup(event.x_root, event.y_root)
+
+ def make_menu(self):
+ menu = Menu(self.listbox, tearoff=0)
+ self.menu = menu
+ self.fill_menu()
+
+ def up_event(self, event):
+ index = self.listbox.index("active")
+ if self.listbox.selection_includes(index):
+ index = index - 1
+ else:
+ index = self.listbox.size() - 1
+ if index < 0:
+ self.listbox.bell()
+ else:
+ self.select(index)
+ self.on_select(index)
+ return "break"
+
+ def down_event(self, event):
+ index = self.listbox.index("active")
+ if self.listbox.selection_includes(index):
+ index = index + 1
+ else:
+ index = 0
+ if index >= self.listbox.size():
+ self.listbox.bell()
+ else:
+ self.select(index)
+ self.on_select(index)
+ return "break"
+
+ def select(self, index):
+ self.listbox.focus_set()
+ self.listbox.activate(index)
+ self.listbox.selection_clear(0, "end")
+ self.listbox.selection_set(index)
+ self.listbox.see(index)
+
+ # Methods to override for specific actions
+
+ def fill_menu(self):
+ pass
+
+ def on_select(self, index):
+ pass
+
+ def on_double(self, index):
+ pass
+
+
+def _scrolled_list(parent):
+ root = Tk()
+ root.title("Test ScrolledList")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ class MyScrolledList(ScrolledList):
+ def fill_menu(self): self.menu.add_command(label="right click")
+ def on_select(self, index): print "select", self.get(index)
+ def on_double(self, index): print "double", self.get(index)
+
+ scrolled_list = MyScrolledList(root)
+ for i in range(30):
+ scrolled_list.append("Item %02d" % i)
+
+ root.mainloop()
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_scrolled_list)
diff --git a/lib/python2.7/idlelib/SearchDialog.py b/lib/python2.7/idlelib/SearchDialog.py
new file mode 100644
index 0000000..043168a
--- /dev/null
+++ b/lib/python2.7/idlelib/SearchDialog.py
@@ -0,0 +1,89 @@
+from Tkinter import *
+
+from idlelib import SearchEngine
+from idlelib.SearchDialogBase import SearchDialogBase
+
+def _setup(text):
+ root = text._root()
+ engine = SearchEngine.get(root)
+ if not hasattr(engine, "_searchdialog"):
+ engine._searchdialog = SearchDialog(root, engine)
+ return engine._searchdialog
+
+def find(text):
+ pat = text.get("sel.first", "sel.last")
+ return _setup(text).open(text,pat)
+
+def find_again(text):
+ return _setup(text).find_again(text)
+
+def find_selection(text):
+ return _setup(text).find_selection(text)
+
+class SearchDialog(SearchDialogBase):
+
+ def create_widgets(self):
+ SearchDialogBase.create_widgets(self)
+ self.make_button("Find Next", self.default_command, 1)
+
+ def default_command(self, event=None):
+ if not self.engine.getprog():
+ return
+ self.find_again(self.text)
+
+ def find_again(self, text):
+ if not self.engine.getpat():
+ self.open(text)
+ return False
+ if not self.engine.getprog():
+ return False
+ res = self.engine.search_text(text)
+ if res:
+ line, m = res
+ i, j = m.span()
+ first = "%d.%d" % (line, i)
+ last = "%d.%d" % (line, j)
+ try:
+ selfirst = text.index("sel.first")
+ sellast = text.index("sel.last")
+ if selfirst == first and sellast == last:
+ text.bell()
+ return False
+ except TclError:
+ pass
+ text.tag_remove("sel", "1.0", "end")
+ text.tag_add("sel", first, last)
+ text.mark_set("insert", self.engine.isback() and first or last)
+ text.see("insert")
+ return True
+ else:
+ text.bell()
+ return False
+
+ def find_selection(self, text):
+ pat = text.get("sel.first", "sel.last")
+ if pat:
+ self.engine.setcookedpat(pat)
+ return self.find_again(text)
+
+def _search_dialog(parent):
+ root = Tk()
+ root.title("Test SearchDialog")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ text = Text(root)
+ text.pack()
+ text.insert("insert","This is a sample string.\n"*10)
+
+ def show_find():
+ text.tag_add(SEL, "1.0", END)
+ s = _setup(text)
+ s.open(text)
+ text.tag_remove(SEL, "1.0", END)
+
+ button = Button(root, text="Search", command=show_find)
+ button.pack()
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_search_dialog)
diff --git a/lib/python2.7/idlelib/SearchDialogBase.py b/lib/python2.7/idlelib/SearchDialogBase.py
new file mode 100644
index 0000000..651e7f4
--- /dev/null
+++ b/lib/python2.7/idlelib/SearchDialogBase.py
@@ -0,0 +1,184 @@
+'''Define SearchDialogBase used by Search, Replace, and Grep dialogs.'''
+
+from Tkinter import (Toplevel, Frame, Entry, Label, Button,
+ Checkbutton, Radiobutton)
+
+class SearchDialogBase:
+ '''Create most of a 3 or 4 row, 3 column search dialog.
+
+ The left and wide middle column contain:
+ 1 or 2 labeled text entry lines (make_entry, create_entries);
+ a row of standard Checkbuttons (make_frame, create_option_buttons),
+ each of which corresponds to a search engine Variable;
+ a row of dialog-specific Check/Radiobuttons (create_other_buttons).
+
+ The narrow right column contains command buttons
+ (make_button, create_command_buttons).
+ These are bound to functions that execute the command.
+
+ Except for command buttons, this base class is not limited to items
+ common to all three subclasses. Rather, it is the Find dialog minus
+ the "Find Next" command, its execution function, and the
+ default_command attribute needed in create_widgets. The other
+ dialogs override attributes and methods, the latter to replace and
+ add widgets.
+ '''
+
+ title = "Search Dialog" # replace in subclasses
+ icon = "Search"
+ needwrapbutton = 1 # not in Find in Files
+
+ def __init__(self, root, engine):
+ '''Initialize root, engine, and top attributes.
+
+ top (level widget): set in create_widgets() called from open().
+ text (Text searched): set in open(), only used in subclasses().
+ ent (ry): created in make_entry() called from create_entry().
+ row (of grid): 0 in create_widgets(), +1 in make_entry/frame().
+ default_command: set in subclasses, used in create_widgers().
+
+ title (of dialog): class attribute, override in subclasses.
+ icon (of dialog): ditto, use unclear if cannot minimize dialog.
+ '''
+ self.root = root
+ self.engine = engine
+ self.top = None
+
+ def open(self, text, searchphrase=None):
+ "Make dialog visible on top of others and ready to use."
+ self.text = text
+ if not self.top:
+ self.create_widgets()
+ else:
+ self.top.deiconify()
+ self.top.tkraise()
+ if searchphrase:
+ self.ent.delete(0,"end")
+ self.ent.insert("end",searchphrase)
+ self.ent.focus_set()
+ self.ent.selection_range(0, "end")
+ self.ent.icursor(0)
+ self.top.grab_set()
+
+ def close(self, event=None):
+ "Put dialog away for later use."
+ if self.top:
+ self.top.grab_release()
+ self.top.withdraw()
+
+ def create_widgets(self):
+ '''Create basic 3 row x 3 col search (find) dialog.
+
+ Other dialogs override subsidiary create_x methods as needed.
+ Replace and Find-in-Files add another entry row.
+ '''
+ top = Toplevel(self.root)
+ top.bind("<Return>", self.default_command)
+ top.bind("<Escape>", self.close)
+ top.protocol("WM_DELETE_WINDOW", self.close)
+ top.wm_title(self.title)
+ top.wm_iconname(self.icon)
+ self.top = top
+
+ self.row = 0
+ self.top.grid_columnconfigure(0, pad=2, weight=0)
+ self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100)
+
+ self.create_entries() # row 0 (and maybe 1), cols 0, 1
+ self.create_option_buttons() # next row, cols 0, 1
+ self.create_other_buttons() # next row, cols 0, 1
+ self.create_command_buttons() # col 2, all rows
+
+ def make_entry(self, label_text, var):
+ '''Return (entry, label), .
+
+ entry - gridded labeled Entry for text entry.
+ label - Label widget, returned for testing.
+ '''
+ label = Label(self.top, text=label_text)
+ label.grid(row=self.row, column=0, sticky="nw")
+ entry = Entry(self.top, textvariable=var, exportselection=0)
+ entry.grid(row=self.row, column=1, sticky="nwe")
+ self.row = self.row + 1
+ return entry, label
+
+ def create_entries(self):
+ "Create one or more entry lines with make_entry."
+ self.ent = self.make_entry("Find:", self.engine.patvar)[0]
+
+ def make_frame(self,labeltext=None):
+ '''Return (frame, label).
+
+ frame - gridded labeled Frame for option or other buttons.
+ label - Label widget, returned for testing.
+ '''
+ if labeltext:
+ label = Label(self.top, text=labeltext)
+ label.grid(row=self.row, column=0, sticky="nw")
+ else:
+ label = ''
+ frame = Frame(self.top)
+ frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe")
+ self.row = self.row + 1
+ return frame, label
+
+ def create_option_buttons(self):
+ '''Return (filled frame, options) for testing.
+
+ Options is a list of SearchEngine booleanvar, label pairs.
+ A gridded frame from make_frame is filled with a Checkbutton
+ for each pair, bound to the var, with the corresponding label.
+ '''
+ frame = self.make_frame("Options")[0]
+ engine = self.engine
+ options = [(engine.revar, "Regular expression"),
+ (engine.casevar, "Match case"),
+ (engine.wordvar, "Whole word")]
+ if self.needwrapbutton:
+ options.append((engine.wrapvar, "Wrap around"))
+ for var, label in options:
+ btn = Checkbutton(frame, anchor="w", variable=var, text=label)
+ btn.pack(side="left", fill="both")
+ if var.get():
+ btn.select()
+ return frame, options
+
+ def create_other_buttons(self):
+ '''Return (frame, others) for testing.
+
+ Others is a list of value, label pairs.
+ A gridded frame from make_frame is filled with radio buttons.
+ '''
+ frame = self.make_frame("Direction")[0]
+ var = self.engine.backvar
+ others = [(1, 'Up'), (0, 'Down')]
+ for val, label in others:
+ btn = Radiobutton(frame, anchor="w",
+ variable=var, value=val, text=label)
+ btn.pack(side="left", fill="both")
+ if var.get() == val:
+ btn.select()
+ return frame, others
+
+ def make_button(self, label, command, isdef=0):
+ "Return command button gridded in command frame."
+ b = Button(self.buttonframe,
+ text=label, command=command,
+ default=isdef and "active" or "normal")
+ cols,rows=self.buttonframe.grid_size()
+ b.grid(pady=1,row=rows,column=0,sticky="ew")
+ self.buttonframe.grid(rowspan=rows+1)
+ return b
+
+ def create_command_buttons(self):
+ "Place buttons in vertical command frame gridded on right."
+ f = self.buttonframe = Frame(self.top)
+ f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2)
+
+ b = self.make_button("close", self.close)
+ b.lower()
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main(
+ 'idlelib.idle_test.test_searchdialogbase', verbosity=2)
diff --git a/lib/python2.7/idlelib/SearchEngine.py b/lib/python2.7/idlelib/SearchEngine.py
new file mode 100644
index 0000000..ad43130
--- /dev/null
+++ b/lib/python2.7/idlelib/SearchEngine.py
@@ -0,0 +1,233 @@
+'''Define SearchEngine for search dialogs.'''
+import re
+from Tkinter import StringVar, BooleanVar, TclError
+import tkMessageBox
+
+def get(root):
+ '''Return the singleton SearchEngine instance for the process.
+
+ The single SearchEngine saves settings between dialog instances.
+ If there is not a SearchEngine already, make one.
+ '''
+ if not hasattr(root, "_searchengine"):
+ root._searchengine = SearchEngine(root)
+ # This creates a cycle that persists until root is deleted.
+ return root._searchengine
+
+class SearchEngine:
+ """Handles searching a text widget for Find, Replace, and Grep."""
+
+ def __init__(self, root):
+ '''Initialize Variables that save search state.
+
+ The dialogs bind these to the UI elements present in the dialogs.
+ '''
+ self.root = root # need for report_error()
+ self.patvar = StringVar(root, '') # search pattern
+ self.revar = BooleanVar(root, False) # regular expression?
+ self.casevar = BooleanVar(root, False) # match case?
+ self.wordvar = BooleanVar(root, False) # match whole word?
+ self.wrapvar = BooleanVar(root, True) # wrap around buffer?
+ self.backvar = BooleanVar(root, False) # search backwards?
+
+ # Access methods
+
+ def getpat(self):
+ return self.patvar.get()
+
+ def setpat(self, pat):
+ self.patvar.set(pat)
+
+ def isre(self):
+ return self.revar.get()
+
+ def iscase(self):
+ return self.casevar.get()
+
+ def isword(self):
+ return self.wordvar.get()
+
+ def iswrap(self):
+ return self.wrapvar.get()
+
+ def isback(self):
+ return self.backvar.get()
+
+ # Higher level access methods
+
+ def setcookedpat(self, pat):
+ "Set pattern after escaping if re."
+ # called only in SearchDialog.py: 66
+ if self.isre():
+ pat = re.escape(pat)
+ self.setpat(pat)
+
+ def getcookedpat(self):
+ pat = self.getpat()
+ if not self.isre(): # if True, see setcookedpat
+ pat = re.escape(pat)
+ if self.isword():
+ pat = r"\b%s\b" % pat
+ return pat
+
+ def getprog(self):
+ "Return compiled cooked search pattern."
+ pat = self.getpat()
+ if not pat:
+ self.report_error(pat, "Empty regular expression")
+ return None
+ pat = self.getcookedpat()
+ flags = 0
+ if not self.iscase():
+ flags = flags | re.IGNORECASE
+ try:
+ prog = re.compile(pat, flags)
+ except re.error as what:
+ args = what.args
+ msg = args[0]
+ col = args[1] if len(args) >= 2 else -1
+ self.report_error(pat, msg, col)
+ return None
+ return prog
+
+ def report_error(self, pat, msg, col=-1):
+ # Derived class could override this with something fancier
+ msg = "Error: " + str(msg)
+ if pat:
+ msg = msg + "\nPattern: " + str(pat)
+ if col >= 0:
+ msg = msg + "\nOffset: " + str(col)
+ tkMessageBox.showerror("Regular expression error",
+ msg, master=self.root)
+
+ def search_text(self, text, prog=None, ok=0):
+ '''Return (lineno, matchobj) or None for forward/backward search.
+
+ This function calls the right function with the right arguments.
+ It directly return the result of that call.
+
+ Text is a text widget. Prog is a precompiled pattern.
+ The ok parameter is a bit complicated as it has two effects.
+
+ If there is a selection, the search begin at either end,
+ depending on the direction setting and ok, with ok meaning that
+ the search starts with the selection. Otherwise, search begins
+ at the insert mark.
+
+ To aid progress, the search functions do not return an empty
+ match at the starting position unless ok is True.
+ '''
+
+ if not prog:
+ prog = self.getprog()
+ if not prog:
+ return None # Compilation failed -- stop
+ wrap = self.wrapvar.get()
+ first, last = get_selection(text)
+ if self.isback():
+ if ok:
+ start = last
+ else:
+ start = first
+ line, col = get_line_col(start)
+ res = self.search_backward(text, prog, line, col, wrap, ok)
+ else:
+ if ok:
+ start = first
+ else:
+ start = last
+ line, col = get_line_col(start)
+ res = self.search_forward(text, prog, line, col, wrap, ok)
+ return res
+
+ def search_forward(self, text, prog, line, col, wrap, ok=0):
+ wrapped = 0
+ startline = line
+ chars = text.get("%d.0" % line, "%d.0" % (line+1))
+ while chars:
+ m = prog.search(chars[:-1], col)
+ if m:
+ if ok or m.end() > col:
+ return line, m
+ line = line + 1
+ if wrapped and line > startline:
+ break
+ col = 0
+ ok = 1
+ chars = text.get("%d.0" % line, "%d.0" % (line+1))
+ if not chars and wrap:
+ wrapped = 1
+ wrap = 0
+ line = 1
+ chars = text.get("1.0", "2.0")
+ return None
+
+ def search_backward(self, text, prog, line, col, wrap, ok=0):
+ wrapped = 0
+ startline = line
+ chars = text.get("%d.0" % line, "%d.0" % (line+1))
+ while 1:
+ m = search_reverse(prog, chars[:-1], col)
+ if m:
+ if ok or m.start() < col:
+ return line, m
+ line = line - 1
+ if wrapped and line < startline:
+ break
+ ok = 1
+ if line <= 0:
+ if not wrap:
+ break
+ wrapped = 1
+ wrap = 0
+ pos = text.index("end-1c")
+ line, col = map(int, pos.split("."))
+ chars = text.get("%d.0" % line, "%d.0" % (line+1))
+ col = len(chars) - 1
+ return None
+
+def search_reverse(prog, chars, col):
+ '''Search backwards and return an re match object or None.
+
+ This is done by searching forwards until there is no match.
+ Prog: compiled re object with a search method returning a match.
+ Chars: line of text, without \\n.
+ Col: stop index for the search; the limit for match.end().
+ '''
+ m = prog.search(chars)
+ if not m:
+ return None
+ found = None
+ i, j = m.span() # m.start(), m.end() == match slice indexes
+ while i < col and j <= col:
+ found = m
+ if i == j:
+ j = j+1
+ m = prog.search(chars, j)
+ if not m:
+ break
+ i, j = m.span()
+ return found
+
+def get_selection(text):
+ '''Return tuple of 'line.col' indexes from selection or insert mark.
+ '''
+ try:
+ first = text.index("sel.first")
+ last = text.index("sel.last")
+ except TclError:
+ first = last = None
+ if not first:
+ first = text.index("insert")
+ if not last:
+ last = first
+ return first, last
+
+def get_line_col(index):
+ '''Return (line, col) tuple of ints from 'line.col' string.'''
+ line, col = map(int, index.split(".")) # Fails on invalid index
+ return line, col
+
+if __name__ == "__main__":
+ import unittest
+ unittest.main('idlelib.idle_test.test_searchengine', verbosity=2, exit=False)
diff --git a/lib/python2.7/idlelib/StackViewer.py b/lib/python2.7/idlelib/StackViewer.py
new file mode 100644
index 0000000..555a08c
--- /dev/null
+++ b/lib/python2.7/idlelib/StackViewer.py
@@ -0,0 +1,151 @@
+import os
+import sys
+import linecache
+import re
+import Tkinter as tk
+
+from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas
+from idlelib.ObjectBrowser import ObjectTreeItem, make_objecttreeitem
+from idlelib.PyShell import PyShellFileList
+
+def StackBrowser(root, flist=None, tb=None, top=None):
+ if top is None:
+ top = tk.Toplevel(root)
+ sc = ScrolledCanvas(top, bg="white", highlightthickness=0)
+ sc.frame.pack(expand=1, fill="both")
+ item = StackTreeItem(flist, tb)
+ node = TreeNode(sc.canvas, None, item)
+ node.expand()
+
+class StackTreeItem(TreeItem):
+
+ def __init__(self, flist=None, tb=None):
+ self.flist = flist
+ self.stack = self.get_stack(tb)
+ self.text = self.get_exception()
+
+ def get_stack(self, tb):
+ if tb is None:
+ tb = sys.last_traceback
+ stack = []
+ if tb and tb.tb_frame is None:
+ tb = tb.tb_next
+ while tb is not None:
+ stack.append((tb.tb_frame, tb.tb_lineno))
+ tb = tb.tb_next
+ return stack
+
+ def get_exception(self):
+ type = sys.last_type
+ value = sys.last_value
+ if hasattr(type, "__name__"):
+ type = type.__name__
+ s = str(type)
+ if value is not None:
+ s = s + ": " + str(value)
+ return s
+
+ def GetText(self):
+ return self.text
+
+ def GetSubList(self):
+ sublist = []
+ for info in self.stack:
+ item = FrameTreeItem(info, self.flist)
+ sublist.append(item)
+ return sublist
+
+class FrameTreeItem(TreeItem):
+
+ def __init__(self, info, flist):
+ self.info = info
+ self.flist = flist
+
+ def GetText(self):
+ frame, lineno = self.info
+ try:
+ modname = frame.f_globals["__name__"]
+ except:
+ modname = "?"
+ code = frame.f_code
+ filename = code.co_filename
+ funcname = code.co_name
+ sourceline = linecache.getline(filename, lineno)
+ sourceline = sourceline.strip()
+ if funcname in ("?", "", None):
+ item = "%s, line %d: %s" % (modname, lineno, sourceline)
+ else:
+ item = "%s.%s(...), line %d: %s" % (modname, funcname,
+ lineno, sourceline)
+ return item
+
+ def GetSubList(self):
+ frame, lineno = self.info
+ sublist = []
+ if frame.f_globals is not frame.f_locals:
+ item = VariablesTreeItem("<locals>", frame.f_locals, self.flist)
+ sublist.append(item)
+ item = VariablesTreeItem("<globals>", frame.f_globals, self.flist)
+ sublist.append(item)
+ return sublist
+
+ def OnDoubleClick(self):
+ if self.flist:
+ frame, lineno = self.info
+ filename = frame.f_code.co_filename
+ if os.path.isfile(filename):
+ self.flist.gotofileline(filename, lineno)
+
+class VariablesTreeItem(ObjectTreeItem):
+
+ def GetText(self):
+ return self.labeltext
+
+ def GetLabelText(self):
+ return None
+
+ def IsExpandable(self):
+ return len(self.object) > 0
+
+ def GetSubList(self):
+ sublist = []
+ for key in self.object.keys():
+ try:
+ value = self.object[key]
+ except KeyError:
+ continue
+ def setfunction(value, key=key, object=self.object):
+ object[key] = value
+ item = make_objecttreeitem(key + " =", value, setfunction)
+ sublist.append(item)
+ return sublist
+
+ def keys(self): # unused, left for possible 3rd party use
+ return self.object.keys()
+
+def _stack_viewer(parent): # htest #
+ root = tk.Tk()
+ root.title("Test StackViewer")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ flist = PyShellFileList(root)
+ try: # to obtain a traceback object
+ intentional_name_error
+ except NameError:
+ exc_type, exc_value, exc_tb = sys.exc_info()
+
+ # inject stack trace to sys
+ sys.last_type = exc_type
+ sys.last_value = exc_value
+ sys.last_traceback = exc_tb
+
+ StackBrowser(root, flist=flist, top=root, tb=exc_tb)
+
+ # restore sys to original state
+ del sys.last_type
+ del sys.last_value
+ del sys.last_traceback
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_stack_viewer)
diff --git a/lib/python2.7/idlelib/TODO.txt b/lib/python2.7/idlelib/TODO.txt
new file mode 100644
index 0000000..e2f1ac0
--- /dev/null
+++ b/lib/python2.7/idlelib/TODO.txt
@@ -0,0 +1,210 @@
+Original IDLE todo, much of it now outdated:
+============================================
+TO DO:
+
+- improve debugger:
+ - manage breakpoints globally, allow bp deletion, tbreak, cbreak etc.
+ - real object browser
+ - help on how to use it (a simple help button will do wonders)
+ - performance? (updates of large sets of locals are slow)
+ - better integration of "debug module"
+ - debugger should be global resource (attached to flist, not to shell)
+ - fix the stupid bug where you need to step twice
+ - display class name in stack viewer entries for methods
+ - suppress tracing through IDLE internals (e.g. print) DONE
+ - add a button to suppress through a specific module or class or method
+ - more object inspection to stack viewer, e.g. to view all array items
+- insert the initial current directory into sys.path DONE
+- default directory attribute for each window instead of only for windows
+ that have an associated filename
+- command expansion from keywords, module contents, other buffers, etc.
+- "Recent documents" menu item DONE
+- Filter region command
+- Optional horizontal scroll bar
+- more Emacsisms:
+ - ^K should cut to buffer
+ - M-[, M-] to move by paragraphs
+ - incremental search?
+- search should indicate wrap-around in some way
+- restructure state sensitive code to avoid testing flags all the time
+- persistent user state (e.g. window and cursor positions, bindings)
+- make backups when saving
+- check file mtimes at various points
+- Pluggable interface with RCS/CVS/Perforce/Clearcase
+- better help?
+- don't open second class browser on same module (nor second path browser)
+- unify class and path browsers
+- Need to define a standard way whereby one can determine one is running
+ inside IDLE (needed for Tk mainloop, also handy for $PYTHONSTARTUP)
+- Add more utility methods for use by extensions (a la get_selection)
+- Way to run command in totally separate interpreter (fork+os.system?) DONE
+- Way to find definition of fully-qualified name:
+ In other words, select "UserDict.UserDict", hit some magic key and
+ it loads up UserDict.py and finds the first def or class for UserDict.
+- need a way to force colorization on/off
+- need a way to force auto-indent on/off
+
+Details:
+
+- ^O (on Unix -- open-line) should honor autoindent
+- after paste, show end of pasted text
+- on Windows, should turn short filename to long filename (not only in argv!)
+ (shouldn't this be done -- or undone -- by ntpath.normpath?)
+- new autoindent after colon even indents when the colon is in a comment!
+- sometimes forward slashes in pathname remain
+- sometimes star in window name remains in Windows menu
+- With unix bindings, ESC by itself is ignored
+- Sometimes for no apparent reason a selection from the cursor to the
+ end of the command buffer appears, which is hard to get rid of
+ because it stays when you are typing!
+- The Line/Col in the status bar can be wrong initially in PyShell DONE
+
+Structural problems:
+
+- too much knowledge in FileList about EditorWindow (for example)
+- should add some primitives for accessing the selection etc.
+ to repeat cumbersome code over and over
+
+======================================================================
+
+Jeff Bauer suggests:
+
+- Open Module doesn't appear to handle hierarchical packages.
+- Class browser should also allow hierarchical packages.
+- Open and Open Module could benefit from a history, DONE
+ either command line style, or Microsoft recent-file
+ style.
+- Add a Smalltalk-style inspector (i.e. Tkinspect)
+
+The last suggestion is already a reality, but not yet
+integrated into IDLE. I use a module called inspector.py,
+that used to be available from python.org(?) It no longer
+appears to be in the contributed section, and the source
+has no author attribution.
+
+In any case, the code is useful for visually navigating
+an object's attributes, including its container hierarchy.
+
+ >>> from inspector import Tkinspect
+ >>> Tkinspect(None, myObject)
+
+Tkinspect could probably be extended and refined to
+integrate better into IDLE.
+
+======================================================================
+
+Comparison to PTUI
+------------------
+
++ PTUI's help is better (HTML!)
+
++ PTUI can attach a shell to any module
+
++ PTUI has some more I/O commands:
+ open multiple
+ append
+ examine (what's that?)
+
+======================================================================
+
+Notes after trying to run Grail
+-------------------------------
+
+- Grail does stuff to sys.path based on sys.argv[0]; you must set
+sys.argv[0] to something decent first (it is normally set to the path of
+the idle script).
+
+- Grail must be exec'ed in __main__ because that's imported by some
+other parts of Grail.
+
+- Grail uses a module called History and so does idle :-(
+
+======================================================================
+
+Robin Friedrich's items:
+
+Things I'd like to see:
+ - I'd like support for shift-click extending the selection. There's a
+ bug now that it doesn't work the first time you try it.
+ - Printing is needed. How hard can that be on Windows? FIRST CUT DONE
+ - The python-mode trick of autoindenting a line with <tab> is neat and
+ very handy.
+ - (someday) a spellchecker for docstrings and comments.
+ - a pagedown/up command key which moves to next class/def statement (top
+ level)
+ - split window capability
+ - DnD text relocation/copying
+
+Things I don't want to see.
+ - line numbers... will probably slow things down way too much.
+ - Please use another icon for the tree browser leaf. The small snake
+ isn't cutting it.
+
+----------------------------------------------------------------------
+
+- Customizable views (multi-window or multi-pane). (Markus Gritsch)
+
+- Being able to double click (maybe double right click) on a callable
+object in the editor which shows the source of the object, if
+possible. (Gerrit Holl)
+
+- Hooks into the guts, like in Emacs. (Mike Romberg)
+
+- Sharing the editor with a remote tutor. (Martijn Faassen)
+
+- Multiple views on the same file. (Tony J Ibbs)
+
+- Store breakpoints in a global (per-project) database (GvR); Dirk
+Heise adds: save some space-trimmed context and search around when
+reopening a file that might have been edited by someone else.
+
+- Capture menu events in extensions without changing the IDLE source.
+(Matthias Barmeier)
+
+- Use overlapping panels (a "notebook" in MFC terms I think) for info
+that doesn't need to be accessible simultaneously (e.g. HTML source
+and output). Use multi-pane windows for info that does need to be
+shown together (e.g. class browser and source). (Albert Brandl)
+
+- A project should invisibly track all symbols, for instant search,
+replace and cross-ref. Projects should be allowed to span multiple
+directories, hosts, etc. Project management files are placed in a
+directory you specify. A global mapping between project names and
+project directories should exist [not so sure --GvR]. (Tim Peters)
+
+- Merge attr-tips and auto-expand. (Mark Hammond, Tim Peters)
+
+- Python Shell should behave more like a "shell window" as users know
+it -- i.e. you can only edit the current command, and the cursor can't
+escape from the command area. (Albert Brandl)
+
+- Set X11 class to "idle/Idle", set icon and title to something
+beginning with "idle" -- for window manangers. (Randall Hopper)
+
+- Config files editable through a preferences dialog. (me) DONE
+
+- Config files still editable outside the preferences dialog.
+(Randall Hopper) DONE
+
+- When you're editing a command in PyShell, and there are only blank
+lines below the cursor, hitting Return should ignore or delete those
+blank lines rather than deciding you're not on the last line. (me)
+
+- Run command (F5 c.s.) should be more like Pythonwin's Run -- a
+dialog with options to give command line arguments, run the debugger,
+etc. (me)
+
+- Shouldn't be able to delete part of the prompt (or any text before
+it) in the PyShell. (Martijn Faassen) DONE
+
+- Emacs style auto-fill (also smart about comments and strings).
+(Jeremy Hylton)
+
+- Output of Run Script should go to a separate output window, not to
+the shell window. Output of separate runs should all go to the same
+window but clearly delimited. (David Scherer) REJECT FIRST, LATTER DONE
+
+- GUI form designer to kick VB's butt. (Robert Geiger) THAT'S NOT IDLE
+
+- Printing! Possibly via generation of PDF files which the user must
+then send to the printer separately. (Dinu Gherman) FIRST CUT
diff --git a/lib/python2.7/idlelib/ToolTip.py b/lib/python2.7/idlelib/ToolTip.py
new file mode 100644
index 0000000..11136c4
--- /dev/null
+++ b/lib/python2.7/idlelib/ToolTip.py
@@ -0,0 +1,97 @@
+# general purpose 'tooltip' routines - currently unused in idlefork
+# (although the 'calltips' extension is partly based on this code)
+# may be useful for some purposes in (or almost in ;) the current project scope
+# Ideas gleaned from PySol
+
+from Tkinter import *
+
+class ToolTipBase:
+
+ def __init__(self, button):
+ self.button = button
+ self.tipwindow = None
+ self.id = None
+ self.x = self.y = 0
+ self._id1 = self.button.bind("<Enter>", self.enter)
+ self._id2 = self.button.bind("<Leave>", self.leave)
+ self._id3 = self.button.bind("<ButtonPress>", self.leave)
+
+ def enter(self, event=None):
+ self.schedule()
+
+ def leave(self, event=None):
+ self.unschedule()
+ self.hidetip()
+
+ def schedule(self):
+ self.unschedule()
+ self.id = self.button.after(1500, self.showtip)
+
+ def unschedule(self):
+ id = self.id
+ self.id = None
+ if id:
+ self.button.after_cancel(id)
+
+ def showtip(self):
+ if self.tipwindow:
+ return
+ # The tip window must be completely outside the button;
+ # otherwise when the mouse enters the tip window we get
+ # a leave event and it disappears, and then we get an enter
+ # event and it reappears, and so on forever :-(
+ x = self.button.winfo_rootx() + 20
+ y = self.button.winfo_rooty() + self.button.winfo_height() + 1
+ self.tipwindow = tw = Toplevel(self.button)
+ tw.wm_overrideredirect(1)
+ tw.wm_geometry("+%d+%d" % (x, y))
+ self.showcontents()
+
+ def showcontents(self, text="Your text here"):
+ # Override this in derived class
+ label = Label(self.tipwindow, text=text, justify=LEFT,
+ background="#ffffe0", relief=SOLID, borderwidth=1)
+ label.pack()
+
+ def hidetip(self):
+ tw = self.tipwindow
+ self.tipwindow = None
+ if tw:
+ tw.destroy()
+
+class ToolTip(ToolTipBase):
+ def __init__(self, button, text):
+ ToolTipBase.__init__(self, button)
+ self.text = text
+ def showcontents(self):
+ ToolTipBase.showcontents(self, self.text)
+
+class ListboxToolTip(ToolTipBase):
+ def __init__(self, button, items):
+ ToolTipBase.__init__(self, button)
+ self.items = items
+ def showcontents(self):
+ listbox = Listbox(self.tipwindow, background="#ffffe0")
+ listbox.pack()
+ for item in self.items:
+ listbox.insert(END, item)
+
+def _tooltip(parent):
+ root = Tk()
+ root.title("Test tooltip")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ label = Label(root, text="Place your mouse over buttons")
+ label.pack()
+ button1 = Button(root, text="Button 1")
+ button2 = Button(root, text="Button 2")
+ button1.pack()
+ button2.pack()
+ ToolTip(button1, "This is tooltip text for button1.")
+ ListboxToolTip(button2, ["This is","multiple line",
+ "tooltip text","for button2"])
+ root.mainloop()
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_tooltip)
diff --git a/lib/python2.7/idlelib/TreeWidget.py b/lib/python2.7/idlelib/TreeWidget.py
new file mode 100644
index 0000000..9d9d4d9
--- /dev/null
+++ b/lib/python2.7/idlelib/TreeWidget.py
@@ -0,0 +1,467 @@
+# XXX TO DO:
+# - popup menu
+# - support partial or total redisplay
+# - key bindings (instead of quick-n-dirty bindings on Canvas):
+# - up/down arrow keys to move focus around
+# - ditto for page up/down, home/end
+# - left/right arrows to expand/collapse & move out/in
+# - more doc strings
+# - add icons for "file", "module", "class", "method"; better "python" icon
+# - callback for selection???
+# - multiple-item selection
+# - tooltips
+# - redo geometry without magic numbers
+# - keep track of object ids to allow more careful cleaning
+# - optimize tree redraw after expand of subnode
+
+import os
+from Tkinter import *
+import imp
+
+from idlelib import ZoomHeight
+from idlelib.configHandler import idleConf
+
+ICONDIR = "Icons"
+
+# Look for Icons subdirectory in the same directory as this module
+try:
+ _icondir = os.path.join(os.path.dirname(__file__), ICONDIR)
+except NameError:
+ _icondir = ICONDIR
+if os.path.isdir(_icondir):
+ ICONDIR = _icondir
+elif not os.path.isdir(ICONDIR):
+ raise RuntimeError, "can't find icon directory (%r)" % (ICONDIR,)
+
+def listicons(icondir=ICONDIR):
+ """Utility to display the available icons."""
+ root = Tk()
+ import glob
+ list = glob.glob(os.path.join(icondir, "*.gif"))
+ list.sort()
+ images = []
+ row = column = 0
+ for file in list:
+ name = os.path.splitext(os.path.basename(file))[0]
+ image = PhotoImage(file=file, master=root)
+ images.append(image)
+ label = Label(root, image=image, bd=1, relief="raised")
+ label.grid(row=row, column=column)
+ label = Label(root, text=name)
+ label.grid(row=row+1, column=column)
+ column = column + 1
+ if column >= 10:
+ row = row+2
+ column = 0
+ root.images = images
+
+
+class TreeNode:
+
+ def __init__(self, canvas, parent, item):
+ self.canvas = canvas
+ self.parent = parent
+ self.item = item
+ self.state = 'collapsed'
+ self.selected = False
+ self.children = []
+ self.x = self.y = None
+ self.iconimages = {} # cache of PhotoImage instances for icons
+
+ def destroy(self):
+ for c in self.children[:]:
+ self.children.remove(c)
+ c.destroy()
+ self.parent = None
+
+ def geticonimage(self, name):
+ try:
+ return self.iconimages[name]
+ except KeyError:
+ pass
+ file, ext = os.path.splitext(name)
+ ext = ext or ".gif"
+ fullname = os.path.join(ICONDIR, file + ext)
+ image = PhotoImage(master=self.canvas, file=fullname)
+ self.iconimages[name] = image
+ return image
+
+ def select(self, event=None):
+ if self.selected:
+ return
+ self.deselectall()
+ self.selected = True
+ self.canvas.delete(self.image_id)
+ self.drawicon()
+ self.drawtext()
+
+ def deselect(self, event=None):
+ if not self.selected:
+ return
+ self.selected = False
+ self.canvas.delete(self.image_id)
+ self.drawicon()
+ self.drawtext()
+
+ def deselectall(self):
+ if self.parent:
+ self.parent.deselectall()
+ else:
+ self.deselecttree()
+
+ def deselecttree(self):
+ if self.selected:
+ self.deselect()
+ for child in self.children:
+ child.deselecttree()
+
+ def flip(self, event=None):
+ if self.state == 'expanded':
+ self.collapse()
+ else:
+ self.expand()
+ self.item.OnDoubleClick()
+ return "break"
+
+ def expand(self, event=None):
+ if not self.item._IsExpandable():
+ return
+ if self.state != 'expanded':
+ self.state = 'expanded'
+ self.update()
+ self.view()
+
+ def collapse(self, event=None):
+ if self.state != 'collapsed':
+ self.state = 'collapsed'
+ self.update()
+
+ def view(self):
+ top = self.y - 2
+ bottom = self.lastvisiblechild().y + 17
+ height = bottom - top
+ visible_top = self.canvas.canvasy(0)
+ visible_height = self.canvas.winfo_height()
+ visible_bottom = self.canvas.canvasy(visible_height)
+ if visible_top <= top and bottom <= visible_bottom:
+ return
+ x0, y0, x1, y1 = self.canvas._getints(self.canvas['scrollregion'])
+ if top >= visible_top and height <= visible_height:
+ fraction = top + height - visible_height
+ else:
+ fraction = top
+ fraction = float(fraction) / y1
+ self.canvas.yview_moveto(fraction)
+
+ def lastvisiblechild(self):
+ if self.children and self.state == 'expanded':
+ return self.children[-1].lastvisiblechild()
+ else:
+ return self
+
+ def update(self):
+ if self.parent:
+ self.parent.update()
+ else:
+ oldcursor = self.canvas['cursor']
+ self.canvas['cursor'] = "watch"
+ self.canvas.update()
+ self.canvas.delete(ALL) # XXX could be more subtle
+ self.draw(7, 2)
+ x0, y0, x1, y1 = self.canvas.bbox(ALL)
+ self.canvas.configure(scrollregion=(0, 0, x1, y1))
+ self.canvas['cursor'] = oldcursor
+
+ def draw(self, x, y):
+ # XXX This hard-codes too many geometry constants!
+ dy = 20
+ self.x, self.y = x, y
+ self.drawicon()
+ self.drawtext()
+ if self.state != 'expanded':
+ return y + dy
+ # draw children
+ if not self.children:
+ sublist = self.item._GetSubList()
+ if not sublist:
+ # _IsExpandable() was mistaken; that's allowed
+ return y+17
+ for item in sublist:
+ child = self.__class__(self.canvas, self, item)
+ self.children.append(child)
+ cx = x+20
+ cy = y + dy
+ cylast = 0
+ for child in self.children:
+ cylast = cy
+ self.canvas.create_line(x+9, cy+7, cx, cy+7, fill="gray50")
+ cy = child.draw(cx, cy)
+ if child.item._IsExpandable():
+ if child.state == 'expanded':
+ iconname = "minusnode"
+ callback = child.collapse
+ else:
+ iconname = "plusnode"
+ callback = child.expand
+ image = self.geticonimage(iconname)
+ id = self.canvas.create_image(x+9, cylast+7, image=image)
+ # XXX This leaks bindings until canvas is deleted:
+ self.canvas.tag_bind(id, "<1>", callback)
+ self.canvas.tag_bind(id, "<Double-1>", lambda x: None)
+ id = self.canvas.create_line(x+9, y+10, x+9, cylast+7,
+ ##stipple="gray50", # XXX Seems broken in Tk 8.0.x
+ fill="gray50")
+ self.canvas.tag_lower(id) # XXX .lower(id) before Python 1.5.2
+ return cy
+
+ def drawicon(self):
+ if self.selected:
+ imagename = (self.item.GetSelectedIconName() or
+ self.item.GetIconName() or
+ "openfolder")
+ else:
+ imagename = self.item.GetIconName() or "folder"
+ image = self.geticonimage(imagename)
+ id = self.canvas.create_image(self.x, self.y, anchor="nw", image=image)
+ self.image_id = id
+ self.canvas.tag_bind(id, "<1>", self.select)
+ self.canvas.tag_bind(id, "<Double-1>", self.flip)
+
+ def drawtext(self):
+ textx = self.x+20-1
+ texty = self.y-4
+ labeltext = self.item.GetLabelText()
+ if labeltext:
+ id = self.canvas.create_text(textx, texty, anchor="nw",
+ text=labeltext)
+ self.canvas.tag_bind(id, "<1>", self.select)
+ self.canvas.tag_bind(id, "<Double-1>", self.flip)
+ x0, y0, x1, y1 = self.canvas.bbox(id)
+ textx = max(x1, 200) + 10
+ text = self.item.GetText() or "<no text>"
+ try:
+ self.entry
+ except AttributeError:
+ pass
+ else:
+ self.edit_finish()
+ try:
+ self.label
+ except AttributeError:
+ # padding carefully selected (on Windows) to match Entry widget:
+ self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2)
+ theme = idleConf.CurrentTheme()
+ if self.selected:
+ self.label.configure(idleConf.GetHighlight(theme, 'hilite'))
+ else:
+ self.label.configure(idleConf.GetHighlight(theme, 'normal'))
+ id = self.canvas.create_window(textx, texty,
+ anchor="nw", window=self.label)
+ self.label.bind("<1>", self.select_or_edit)
+ self.label.bind("<Double-1>", self.flip)
+ self.text_id = id
+
+ def select_or_edit(self, event=None):
+ if self.selected and self.item.IsEditable():
+ self.edit(event)
+ else:
+ self.select(event)
+
+ def edit(self, event=None):
+ self.entry = Entry(self.label, bd=0, highlightthickness=1, width=0)
+ self.entry.insert(0, self.label['text'])
+ self.entry.selection_range(0, END)
+ self.entry.pack(ipadx=5)
+ self.entry.focus_set()
+ self.entry.bind("<Return>", self.edit_finish)
+ self.entry.bind("<Escape>", self.edit_cancel)
+
+ def edit_finish(self, event=None):
+ try:
+ entry = self.entry
+ del self.entry
+ except AttributeError:
+ return
+ text = entry.get()
+ entry.destroy()
+ if text and text != self.item.GetText():
+ self.item.SetText(text)
+ text = self.item.GetText()
+ self.label['text'] = text
+ self.drawtext()
+ self.canvas.focus_set()
+
+ def edit_cancel(self, event=None):
+ try:
+ entry = self.entry
+ del self.entry
+ except AttributeError:
+ return
+ entry.destroy()
+ self.drawtext()
+ self.canvas.focus_set()
+
+
+class TreeItem:
+
+ """Abstract class representing tree items.
+
+ Methods should typically be overridden, otherwise a default action
+ is used.
+
+ """
+
+ def __init__(self):
+ """Constructor. Do whatever you need to do."""
+
+ def GetText(self):
+ """Return text string to display."""
+
+ def GetLabelText(self):
+ """Return label text string to display in front of text (if any)."""
+
+ expandable = None
+
+ def _IsExpandable(self):
+ """Do not override! Called by TreeNode."""
+ if self.expandable is None:
+ self.expandable = self.IsExpandable()
+ return self.expandable
+
+ def IsExpandable(self):
+ """Return whether there are subitems."""
+ return 1
+
+ def _GetSubList(self):
+ """Do not override! Called by TreeNode."""
+ if not self.IsExpandable():
+ return []
+ sublist = self.GetSubList()
+ if not sublist:
+ self.expandable = 0
+ return sublist
+
+ def IsEditable(self):
+ """Return whether the item's text may be edited."""
+
+ def SetText(self, text):
+ """Change the item's text (if it is editable)."""
+
+ def GetIconName(self):
+ """Return name of icon to be displayed normally."""
+
+ def GetSelectedIconName(self):
+ """Return name of icon to be displayed when selected."""
+
+ def GetSubList(self):
+ """Return list of items forming sublist."""
+
+ def OnDoubleClick(self):
+ """Called on a double-click on the item."""
+
+
+# Example application
+
+class FileTreeItem(TreeItem):
+
+ """Example TreeItem subclass -- browse the file system."""
+
+ def __init__(self, path):
+ self.path = path
+
+ def GetText(self):
+ return os.path.basename(self.path) or self.path
+
+ def IsEditable(self):
+ return os.path.basename(self.path) != ""
+
+ def SetText(self, text):
+ newpath = os.path.dirname(self.path)
+ newpath = os.path.join(newpath, text)
+ if os.path.dirname(newpath) != os.path.dirname(self.path):
+ return
+ try:
+ os.rename(self.path, newpath)
+ self.path = newpath
+ except os.error:
+ pass
+
+ def GetIconName(self):
+ if not self.IsExpandable():
+ return "python" # XXX wish there was a "file" icon
+
+ def IsExpandable(self):
+ return os.path.isdir(self.path)
+
+ def GetSubList(self):
+ try:
+ names = os.listdir(self.path)
+ except os.error:
+ return []
+ names.sort(key = os.path.normcase)
+ sublist = []
+ for name in names:
+ item = FileTreeItem(os.path.join(self.path, name))
+ sublist.append(item)
+ return sublist
+
+
+# A canvas widget with scroll bars and some useful bindings
+
+class ScrolledCanvas:
+ def __init__(self, master, **opts):
+ if 'yscrollincrement' not in opts:
+ opts['yscrollincrement'] = 17
+ self.master = master
+ self.frame = Frame(master)
+ self.frame.rowconfigure(0, weight=1)
+ self.frame.columnconfigure(0, weight=1)
+ self.canvas = Canvas(self.frame, **opts)
+ self.canvas.grid(row=0, column=0, sticky="nsew")
+ self.vbar = Scrollbar(self.frame, name="vbar")
+ self.vbar.grid(row=0, column=1, sticky="nse")
+ self.hbar = Scrollbar(self.frame, name="hbar", orient="horizontal")
+ self.hbar.grid(row=1, column=0, sticky="ews")
+ self.canvas['yscrollcommand'] = self.vbar.set
+ self.vbar['command'] = self.canvas.yview
+ self.canvas['xscrollcommand'] = self.hbar.set
+ self.hbar['command'] = self.canvas.xview
+ self.canvas.bind("<Key-Prior>", self.page_up)
+ self.canvas.bind("<Key-Next>", self.page_down)
+ self.canvas.bind("<Key-Up>", self.unit_up)
+ self.canvas.bind("<Key-Down>", self.unit_down)
+ #if isinstance(master, Toplevel) or isinstance(master, Tk):
+ self.canvas.bind("<Alt-Key-2>", self.zoom_height)
+ self.canvas.focus_set()
+ def page_up(self, event):
+ self.canvas.yview_scroll(-1, "page")
+ return "break"
+ def page_down(self, event):
+ self.canvas.yview_scroll(1, "page")
+ return "break"
+ def unit_up(self, event):
+ self.canvas.yview_scroll(-1, "unit")
+ return "break"
+ def unit_down(self, event):
+ self.canvas.yview_scroll(1, "unit")
+ return "break"
+ def zoom_height(self, event):
+ ZoomHeight.zoom_height(self.master)
+ return "break"
+
+
+def _tree_widget(parent):
+ root = Tk()
+ root.title("Test TreeWidget")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1)
+ sc.frame.pack(expand=1, fill="both", side=LEFT)
+ item = FileTreeItem(os.getcwd())
+ node = TreeNode(sc.canvas, None, item)
+ node.expand()
+ root.mainloop()
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_tree_widget)
diff --git a/lib/python2.7/idlelib/UndoDelegator.py b/lib/python2.7/idlelib/UndoDelegator.py
new file mode 100644
index 0000000..cdeacea
--- /dev/null
+++ b/lib/python2.7/idlelib/UndoDelegator.py
@@ -0,0 +1,365 @@
+import string
+from Tkinter import *
+
+from idlelib.Delegator import Delegator
+
+#$ event <<redo>>
+#$ win <Control-y>
+#$ unix <Alt-z>
+
+#$ event <<undo>>
+#$ win <Control-z>
+#$ unix <Control-z>
+
+#$ event <<dump-undo-state>>
+#$ win <Control-backslash>
+#$ unix <Control-backslash>
+
+
+class UndoDelegator(Delegator):
+
+ max_undo = 1000
+
+ def __init__(self):
+ Delegator.__init__(self)
+ self.reset_undo()
+
+ def setdelegate(self, delegate):
+ if self.delegate is not None:
+ self.unbind("<<undo>>")
+ self.unbind("<<redo>>")
+ self.unbind("<<dump-undo-state>>")
+ Delegator.setdelegate(self, delegate)
+ if delegate is not None:
+ self.bind("<<undo>>", self.undo_event)
+ self.bind("<<redo>>", self.redo_event)
+ self.bind("<<dump-undo-state>>", self.dump_event)
+
+ 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 "get_saved():", self.get_saved()
+ pprint(self.undolist[self.pointer:])
+ return "break"
+
+ def reset_undo(self):
+ self.was_saved = -1
+ self.pointer = 0
+ self.undolist = []
+ self.undoblock = 0 # or a CommandSequence instance
+ self.set_saved(1)
+
+ def set_saved(self, flag):
+ if flag:
+ self.saved = self.pointer
+ else:
+ self.saved = -1
+ self.can_merge = False
+ self.check_saved()
+
+ def get_saved(self):
+ return self.saved == self.pointer
+
+ saved_change_hook = None
+
+ def set_saved_change_hook(self, hook):
+ self.saved_change_hook = hook
+
+ was_saved = -1
+
+ def check_saved(self):
+ is_saved = self.get_saved()
+ if is_saved != self.was_saved:
+ self.was_saved = is_saved
+ if self.saved_change_hook:
+ self.saved_change_hook()
+
+ def insert(self, index, chars, tags=None):
+ self.addcmd(InsertCommand(index, chars, tags))
+
+ def delete(self, index1, index2=None):
+ self.addcmd(DeleteCommand(index1, index2))
+
+ # Clients should call undo_block_start() and undo_block_stop()
+ # around a sequence of editing cmds to be treated as a unit by
+ # undo & redo. Nested matching calls are OK, and the inner calls
+ # then act like nops. OK too if no editing cmds, or only one
+ # editing cmd, is issued in between: if no cmds, the whole
+ # sequence has no effect; and if only one cmd, that cmd is entered
+ # directly into the undo list, as if undo_block_xxx hadn't been
+ # called. The intent of all that is to make this scheme easy
+ # to use: all the client has to worry about is making sure each
+ # _start() call is matched by a _stop() call.
+
+ def undo_block_start(self):
+ if self.undoblock == 0:
+ self.undoblock = CommandSequence()
+ self.undoblock.bump_depth()
+
+ def undo_block_stop(self):
+ if self.undoblock.bump_depth(-1) == 0:
+ cmd = self.undoblock
+ self.undoblock = 0
+ if len(cmd) > 0:
+ if len(cmd) == 1:
+ # no need to wrap a single cmd
+ cmd = cmd.getcmd(0)
+ # this blk of cmds, or single cmd, has already
+ # been done, so don't execute it again
+ self.addcmd(cmd, 0)
+
+ def addcmd(self, cmd, execute=True):
+ if execute:
+ cmd.do(self.delegate)
+ if self.undoblock != 0:
+ self.undoblock.append(cmd)
+ return
+ if self.can_merge and self.pointer > 0:
+ lastcmd = self.undolist[self.pointer-1]
+ if lastcmd.merge(cmd):
+ return
+ self.undolist[self.pointer:] = [cmd]
+ if self.saved > self.pointer:
+ self.saved = -1
+ self.pointer = self.pointer + 1
+ if len(self.undolist) > self.max_undo:
+ ##print "truncating undo list"
+ del self.undolist[0]
+ self.pointer = self.pointer - 1
+ if self.saved >= 0:
+ self.saved = self.saved - 1
+ self.can_merge = True
+ self.check_saved()
+
+ def undo_event(self, event):
+ if self.pointer == 0:
+ self.bell()
+ return "break"
+ cmd = self.undolist[self.pointer - 1]
+ cmd.undo(self.delegate)
+ self.pointer = self.pointer - 1
+ self.can_merge = False
+ self.check_saved()
+ return "break"
+
+ def redo_event(self, event):
+ if self.pointer >= len(self.undolist):
+ self.bell()
+ return "break"
+ cmd = self.undolist[self.pointer]
+ cmd.redo(self.delegate)
+ self.pointer = self.pointer + 1
+ self.can_merge = False
+ self.check_saved()
+ return "break"
+
+
+class Command:
+
+ # Base class for Undoable commands
+
+ tags = None
+
+ def __init__(self, index1, index2, chars, tags=None):
+ self.marks_before = {}
+ self.marks_after = {}
+ self.index1 = index1
+ self.index2 = index2
+ self.chars = chars
+ if tags:
+ self.tags = tags
+
+ def __repr__(self):
+ s = self.__class__.__name__
+ t = (self.index1, self.index2, self.chars, self.tags)
+ if self.tags is None:
+ t = t[:-1]
+ return s + repr(t)
+
+ def do(self, text):
+ pass
+
+ def redo(self, text):
+ pass
+
+ def undo(self, text):
+ pass
+
+ def merge(self, cmd):
+ return 0
+
+ def save_marks(self, text):
+ marks = {}
+ for name in text.mark_names():
+ if name != "insert" and name != "current":
+ marks[name] = text.index(name)
+ return marks
+
+ def set_marks(self, text, marks):
+ for name, index in marks.items():
+ text.mark_set(name, index)
+
+
+class InsertCommand(Command):
+
+ # Undoable insert command
+
+ def __init__(self, index1, chars, tags=None):
+ Command.__init__(self, index1, None, chars, tags)
+
+ def do(self, text):
+ self.marks_before = self.save_marks(text)
+ self.index1 = text.index(self.index1)
+ if text.compare(self.index1, ">", "end-1c"):
+ # Insert before the final newline
+ self.index1 = text.index("end-1c")
+ text.insert(self.index1, self.chars, self.tags)
+ self.index2 = text.index("%s+%dc" % (self.index1, len(self.chars)))
+ self.marks_after = self.save_marks(text)
+ ##sys.__stderr__.write("do: %s\n" % self)
+
+ def redo(self, text):
+ text.mark_set('insert', self.index1)
+ text.insert(self.index1, self.chars, self.tags)
+ self.set_marks(text, self.marks_after)
+ text.see('insert')
+ ##sys.__stderr__.write("redo: %s\n" % self)
+
+ def undo(self, text):
+ text.mark_set('insert', self.index1)
+ text.delete(self.index1, self.index2)
+ self.set_marks(text, self.marks_before)
+ text.see('insert')
+ ##sys.__stderr__.write("undo: %s\n" % self)
+
+ def merge(self, cmd):
+ if self.__class__ is not cmd.__class__:
+ return False
+ if self.index2 != cmd.index1:
+ return False
+ if self.tags != cmd.tags:
+ return False
+ if len(cmd.chars) != 1:
+ return False
+ if self.chars and \
+ self.classify(self.chars[-1]) != self.classify(cmd.chars):
+ return False
+ self.index2 = cmd.index2
+ self.chars = self.chars + cmd.chars
+ return True
+
+ alphanumeric = string.ascii_letters + string.digits + "_"
+
+ def classify(self, c):
+ if c in self.alphanumeric:
+ return "alphanumeric"
+ if c == "\n":
+ return "newline"
+ return "punctuation"
+
+
+class DeleteCommand(Command):
+
+ # Undoable delete command
+
+ def __init__(self, index1, index2=None):
+ Command.__init__(self, index1, index2, None, None)
+
+ def do(self, text):
+ self.marks_before = self.save_marks(text)
+ self.index1 = text.index(self.index1)
+ if self.index2:
+ self.index2 = text.index(self.index2)
+ else:
+ self.index2 = text.index(self.index1 + " +1c")
+ if text.compare(self.index2, ">", "end-1c"):
+ # Don't delete the final newline
+ self.index2 = text.index("end-1c")
+ self.chars = text.get(self.index1, self.index2)
+ text.delete(self.index1, self.index2)
+ self.marks_after = self.save_marks(text)
+ ##sys.__stderr__.write("do: %s\n" % self)
+
+ def redo(self, text):
+ text.mark_set('insert', self.index1)
+ text.delete(self.index1, self.index2)
+ self.set_marks(text, self.marks_after)
+ text.see('insert')
+ ##sys.__stderr__.write("redo: %s\n" % self)
+
+ def undo(self, text):
+ text.mark_set('insert', self.index1)
+ text.insert(self.index1, self.chars)
+ self.set_marks(text, self.marks_before)
+ text.see('insert')
+ ##sys.__stderr__.write("undo: %s\n" % self)
+
+class CommandSequence(Command):
+
+ # Wrapper for a sequence of undoable cmds to be undone/redone
+ # as a unit
+
+ def __init__(self):
+ self.cmds = []
+ self.depth = 0
+
+ def __repr__(self):
+ s = self.__class__.__name__
+ strs = []
+ for cmd in self.cmds:
+ strs.append(" %r" % (cmd,))
+ return s + "(\n" + ",\n".join(strs) + "\n)"
+
+ def __len__(self):
+ return len(self.cmds)
+
+ def append(self, cmd):
+ self.cmds.append(cmd)
+
+ def getcmd(self, i):
+ return self.cmds[i]
+
+ def redo(self, text):
+ for cmd in self.cmds:
+ cmd.redo(text)
+
+ def undo(self, text):
+ cmds = self.cmds[:]
+ cmds.reverse()
+ for cmd in cmds:
+ cmd.undo(text)
+
+ def bump_depth(self, incr=1):
+ self.depth = self.depth + incr
+ return self.depth
+
+def _undo_delegator(parent):
+ from idlelib.Percolator import Percolator
+ root = Tk()
+ root.title("Test UndoDelegator")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+
+ text = Text(root)
+ text.config(height=10)
+ text.pack()
+ text.focus_set()
+ p = Percolator(text)
+ d = UndoDelegator()
+ p.insertfilter(d)
+
+ undo = Button(root, text="Undo", command=lambda:d.undo_event(None))
+ undo.pack(side='left')
+ redo = Button(root, text="Redo", command=lambda:d.redo_event(None))
+ redo.pack(side='left')
+ dump = Button(root, text="Dump", command=lambda:d.dump_event(None))
+ dump.pack(side='left')
+
+ root.mainloop()
+
+if __name__ == "__main__":
+ from idlelib.idle_test.htest import run
+ run(_undo_delegator)
diff --git a/lib/python2.7/idlelib/WidgetRedirector.py b/lib/python2.7/idlelib/WidgetRedirector.py
new file mode 100644
index 0000000..54431f7
--- /dev/null
+++ b/lib/python2.7/idlelib/WidgetRedirector.py
@@ -0,0 +1,175 @@
+from __future__ import print_function
+from Tkinter import TclError
+
+class WidgetRedirector:
+ """Support for redirecting arbitrary widget subcommands.
+
+ Some Tk operations don't normally pass through tkinter. For example, if a
+ character is inserted into a Text widget by pressing a key, a default Tk
+ binding to the widget's 'insert' operation is activated, and the Tk library
+ processes the insert without calling back into tkinter.
+
+ Although a binding to <Key> could be made via tkinter, what we really want
+ to do is to hook the Tk 'insert' operation itself. For one thing, we want
+ a text.insert call in idle code to have the same effect as a key press.
+
+ When a widget is instantiated, a Tcl command is created whose name is the
+ same as the pathname widget._w. This command is used to invoke the various
+ widget operations, e.g. insert (for a Text widget). We are going to hook
+ this command and provide a facility ('register') to intercept the widget
+ operation. We will also intercept method calls on the Tkinter class
+ instance that represents the tk widget.
+
+ In IDLE, WidgetRedirector is used in Percolator to intercept Text
+ commands. The function being registered provides access to the top
+ of a Percolator chain. At the bottom of the chain is a call to the
+ original Tk widget operation.
+ """
+ def __init__(self, widget):
+ '''Initialize attributes and setup redirection.
+
+ _operations: dict mapping operation name to new function.
+ widget: the widget whose tcl command is to be intercepted.
+ tk: widget.tk, a convenience attribute, probably not needed.
+ orig: new name of the original tcl command.
+
+ Since renaming to orig fails with TclError when orig already
+ exists, only one WidgetDirector can exist for a given widget.
+ '''
+ self._operations = {}
+ self.widget = widget # widget instance
+ self.tk = tk = widget.tk # widget's root
+ w = widget._w # widget's (full) Tk pathname
+ self.orig = w + "_orig"
+ # Rename the Tcl command within Tcl:
+ tk.call("rename", w, self.orig)
+ # Create a new Tcl command whose name is the widget's pathname, and
+ # whose action is to dispatch on the operation passed to the widget:
+ tk.createcommand(w, self.dispatch)
+
+ def __repr__(self):
+ return "WidgetRedirector(%s<%s>)" % (self.widget.__class__.__name__,
+ self.widget._w)
+
+ def close(self):
+ "Unregister operations and revert redirection created by .__init__."
+ for operation in list(self._operations):
+ self.unregister(operation)
+ widget = self.widget
+ tk = widget.tk
+ w = widget._w
+ # Restore the original widget Tcl command.
+ tk.deletecommand(w)
+ tk.call("rename", self.orig, w)
+ del self.widget, self.tk # Should not be needed
+ # if instance is deleted after close, as in Percolator.
+
+ def register(self, operation, function):
+ '''Return OriginalCommand(operation) after registering function.
+
+ Registration adds an operation: function pair to ._operations.
+ It also adds a widget function attribute that masks the Tkinter
+ class instance method. Method masking operates independently
+ from command dispatch.
+
+ If a second function is registered for the same operation, the
+ first function is replaced in both places.
+ '''
+ self._operations[operation] = function
+ setattr(self.widget, operation, function)
+ return OriginalCommand(self, operation)
+
+ def unregister(self, operation):
+ '''Return the function for the operation, or None.
+
+ Deleting the instance attribute unmasks the class attribute.
+ '''
+ if operation in self._operations:
+ function = self._operations[operation]
+ del self._operations[operation]
+ try:
+ delattr(self.widget, operation)
+ except AttributeError:
+ pass
+ return function
+ else:
+ return None
+
+ def dispatch(self, operation, *args):
+ '''Callback from Tcl which runs when the widget is referenced.
+
+ If an operation has been registered in self._operations, apply the
+ associated function to the args passed into Tcl. Otherwise, pass the
+ operation through to Tk via the original Tcl function.
+
+ Note that if a registered function is called, the operation is not
+ passed through to Tk. Apply the function returned by self.register()
+ to *args to accomplish that. For an example, see ColorDelegator.py.
+
+ '''
+ m = self._operations.get(operation)
+ try:
+ if m:
+ return m(*args)
+ else:
+ return self.tk.call((self.orig, operation) + args)
+ except TclError:
+ return ""
+
+
+class OriginalCommand:
+ '''Callable for original tk command that has been redirected.
+
+ Returned by .register; can be used in the function registered.
+ redir = WidgetRedirector(text)
+ def my_insert(*args):
+ print("insert", args)
+ original_insert(*args)
+ original_insert = redir.register("insert", my_insert)
+ '''
+
+ def __init__(self, redir, operation):
+ '''Create .tk_call and .orig_and_operation for .__call__ method.
+
+ .redir and .operation store the input args for __repr__.
+ .tk and .orig copy attributes of .redir (probably not needed).
+ '''
+ self.redir = redir
+ self.operation = operation
+ self.tk = redir.tk # redundant with self.redir
+ self.orig = redir.orig # redundant with self.redir
+ # These two could be deleted after checking recipient code.
+ self.tk_call = redir.tk.call
+ self.orig_and_operation = (redir.orig, operation)
+
+ def __repr__(self):
+ return "OriginalCommand(%r, %r)" % (self.redir, self.operation)
+
+ def __call__(self, *args):
+ return self.tk_call(self.orig_and_operation + args)
+
+
+def _widget_redirector(parent): # htest #
+ from Tkinter import Tk, Text
+ import re
+
+ root = Tk()
+ root.title("Test WidgetRedirector")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ text = Text(root)
+ text.pack()
+ text.focus_set()
+ redir = WidgetRedirector(text)
+ def my_insert(*args):
+ print("insert", args)
+ original_insert(*args)
+ original_insert = redir.register("insert", my_insert)
+ root.mainloop()
+
+if __name__ == "__main__":
+ import unittest
+ unittest.main('idlelib.idle_test.test_widgetredir',
+ verbosity=2, exit=False)
+ from idlelib.idle_test.htest import run
+ run(_widget_redirector)
diff --git a/lib/python2.7/idlelib/WindowList.py b/lib/python2.7/idlelib/WindowList.py
new file mode 100644
index 0000000..658502b
--- /dev/null
+++ b/lib/python2.7/idlelib/WindowList.py
@@ -0,0 +1,90 @@
+from Tkinter import *
+
+class WindowList:
+
+ def __init__(self):
+ self.dict = {}
+ self.callbacks = []
+
+ def add(self, window):
+ window.after_idle(self.call_callbacks)
+ self.dict[str(window)] = window
+
+ def delete(self, window):
+ try:
+ del self.dict[str(window)]
+ except KeyError:
+ # Sometimes, destroy() is called twice
+ pass
+ self.call_callbacks()
+
+ def add_windows_to_menu(self, menu):
+ list = []
+ for key in self.dict.keys():
+ window = self.dict[key]
+ try:
+ title = window.get_title()
+ except TclError:
+ continue
+ list.append((title, window))
+ list.sort()
+ for title, window in list:
+ menu.add_command(label=title, command=window.wakeup)
+
+ def register_callback(self, callback):
+ self.callbacks.append(callback)
+
+ def unregister_callback(self, callback):
+ try:
+ self.callbacks.remove(callback)
+ except ValueError:
+ pass
+
+ def call_callbacks(self):
+ for callback in self.callbacks:
+ try:
+ callback()
+ except:
+ print "warning: callback failed in WindowList", \
+ sys.exc_type, ":", sys.exc_value
+
+registry = WindowList()
+
+add_windows_to_menu = registry.add_windows_to_menu
+register_callback = registry.register_callback
+unregister_callback = registry.unregister_callback
+
+
+class ListedToplevel(Toplevel):
+
+ def __init__(self, master, **kw):
+ Toplevel.__init__(self, master, kw)
+ registry.add(self)
+ self.focused_widget = self
+
+ def destroy(self):
+ registry.delete(self)
+ Toplevel.destroy(self)
+ # If this is Idle's last window then quit the mainloop
+ # (Needed for clean exit on Windows 98)
+ if not registry.dict:
+ self.quit()
+
+ def update_windowlist_registry(self, window):
+ registry.call_callbacks()
+
+ def get_title(self):
+ # Subclass can override
+ return self.wm_title()
+
+ def wakeup(self):
+ try:
+ if self.wm_state() == "iconic":
+ self.wm_withdraw()
+ self.wm_deiconify()
+ self.tkraise()
+ self.focused_widget.focus_set()
+ except TclError:
+ # This can happen when the window menu was torn off.
+ # Simply ignore it.
+ pass
diff --git a/lib/python2.7/idlelib/ZoomHeight.py b/lib/python2.7/idlelib/ZoomHeight.py
new file mode 100644
index 0000000..a5d679e
--- /dev/null
+++ b/lib/python2.7/idlelib/ZoomHeight.py
@@ -0,0 +1,51 @@
+# Sample extension: zoom a window to maximum height
+
+import re
+import sys
+
+from idlelib import macosxSupport
+
+class ZoomHeight:
+
+ menudefs = [
+ ('windows', [
+ ('_Zoom Height', '<<zoom-height>>'),
+ ])
+ ]
+
+ def __init__(self, editwin):
+ self.editwin = editwin
+
+ def zoom_height_event(self, event):
+ top = self.editwin.top
+ zoom_height(top)
+
+def zoom_height(top):
+ geom = top.wm_geometry()
+ m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
+ if not m:
+ top.bell()
+ return
+ width, height, x, y = map(int, m.groups())
+ newheight = top.winfo_screenheight()
+ if sys.platform == 'win32':
+ newy = 0
+ newheight = newheight - 72
+
+ elif macosxSupport.isAquaTk():
+ # 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.
+ newy = 22
+ newheight = newheight - newy - 88
+
+ else:
+ #newy = 24
+ newy = 0
+ #newheight = newheight - 96
+ newheight = newheight - 88
+ if height >= newheight:
+ newgeom = ""
+ else:
+ newgeom = "%dx%d+%d+%d" % (width, newheight, x, newy)
+ top.wm_geometry(newgeom)
diff --git a/lib/python2.7/idlelib/__init__.py b/lib/python2.7/idlelib/__init__.py
new file mode 100644
index 0000000..32b7eac
--- /dev/null
+++ b/lib/python2.7/idlelib/__init__.py
@@ -0,0 +1,8 @@
+"""The idlelib package implements the Idle application.
+
+Idle includes an interactive shell and editor.
+Use the files named idle.* to start Idle.
+
+The other files are private implementations. Their details are subject
+to change. See PEP 434 for more. Import them at your own risk.
+"""
diff --git a/lib/python2.7/idlelib/aboutDialog.py b/lib/python2.7/idlelib/aboutDialog.py
new file mode 100644
index 0000000..c9adc08
--- /dev/null
+++ b/lib/python2.7/idlelib/aboutDialog.py
@@ -0,0 +1,150 @@
+"""About Dialog for IDLE
+
+"""
+import os
+from sys import version
+from Tkinter import *
+from idlelib import textView
+
+class AboutDialog(Toplevel):
+ """Modal about dialog for idle
+
+ """
+ def __init__(self, parent, title, _htest=False):
+ """
+ _htest - bool, change box location when running htest
+ """
+ Toplevel.__init__(self, parent)
+ self.configure(borderwidth=5)
+ # place dialog below parent if running htest
+ self.geometry("+%d+%d" % (
+ parent.winfo_rootx()+30,
+ parent.winfo_rooty()+(30 if not _htest else 100)))
+ self.bg = "#707070"
+ self.fg = "#ffffff"
+ self.CreateWidgets()
+ self.resizable(height=FALSE, width=FALSE)
+ self.title(title)
+ self.transient(parent)
+ self.grab_set()
+ self.protocol("WM_DELETE_WINDOW", self.Ok)
+ self.parent = parent
+ self.buttonOk.focus_set()
+ self.bind('<Return>',self.Ok) #dismiss dialog
+ self.bind('<Escape>',self.Ok) #dismiss dialog
+ self.wait_window()
+
+ def CreateWidgets(self):
+ release = version[:version.index(' ')]
+ frameMain = Frame(self, borderwidth=2, relief=SUNKEN)
+ frameButtons = Frame(self)
+ frameButtons.pack(side=BOTTOM, fill=X)
+ frameMain.pack(side=TOP, expand=TRUE, fill=BOTH)
+ self.buttonOk = Button(frameButtons, text='Close',
+ command=self.Ok)
+ self.buttonOk.pack(padx=5, pady=5)
+ #self.picture = Image('photo', data=self.pictureData)
+ frameBg = Frame(frameMain, bg=self.bg)
+ frameBg.pack(expand=TRUE, fill=BOTH)
+ labelTitle = Label(frameBg, text='IDLE', fg=self.fg, bg=self.bg,
+ font=('courier', 24, 'bold'))
+ labelTitle.grid(row=0, column=0, sticky=W, padx=10, pady=10)
+ #labelPicture = Label(frameBg, text='[picture]')
+ #image=self.picture, bg=self.bg)
+ #labelPicture.grid(row=1, column=1, sticky=W, rowspan=2,
+ # padx=0, pady=3)
+ byline = "Python's Integrated DeveLopment Environment" + 5*'\n'
+ labelDesc = Label(frameBg, text=byline, justify=LEFT,
+ fg=self.fg, bg=self.bg)
+ labelDesc.grid(row=2, column=0, sticky=W, columnspan=3, padx=10, pady=5)
+ labelEmail = Label(frameBg, text='email: idle-dev@python.org',
+ justify=LEFT, fg=self.fg, bg=self.bg)
+ labelEmail.grid(row=6, column=0, columnspan=2,
+ sticky=W, padx=10, pady=0)
+ labelWWW = Label(frameBg, text='https://docs.python.org/' +
+ version[:3] + '/library/idle.html',
+ justify=LEFT, fg=self.fg, bg=self.bg)
+ labelWWW.grid(row=7, column=0, columnspan=2, sticky=W, padx=10, pady=0)
+ Frame(frameBg, borderwidth=1, relief=SUNKEN,
+ height=2, bg=self.bg).grid(row=8, column=0, sticky=EW,
+ columnspan=3, padx=5, pady=5)
+ labelPythonVer = Label(frameBg, text='Python version: ' +
+ release, fg=self.fg, bg=self.bg)
+ labelPythonVer.grid(row=9, column=0, sticky=W, padx=10, pady=0)
+ tkVer = self.tk.call('info', 'patchlevel')
+ labelTkVer = Label(frameBg, text='Tk version: '+
+ tkVer, fg=self.fg, bg=self.bg)
+ labelTkVer.grid(row=9, column=1, sticky=W, padx=2, pady=0)
+ py_button_f = Frame(frameBg, bg=self.bg)
+ py_button_f.grid(row=10, column=0, columnspan=2, sticky=NSEW)
+ buttonLicense = Button(py_button_f, text='License', width=8,
+ highlightbackground=self.bg,
+ command=self.ShowLicense)
+ buttonLicense.pack(side=LEFT, padx=10, pady=10)
+ buttonCopyright = Button(py_button_f, text='Copyright', width=8,
+ highlightbackground=self.bg,
+ command=self.ShowCopyright)
+ buttonCopyright.pack(side=LEFT, padx=10, pady=10)
+ buttonCredits = Button(py_button_f, text='Credits', width=8,
+ highlightbackground=self.bg,
+ command=self.ShowPythonCredits)
+ buttonCredits.pack(side=LEFT, padx=10, pady=10)
+ Frame(frameBg, borderwidth=1, relief=SUNKEN,
+ height=2, bg=self.bg).grid(row=11, column=0, sticky=EW,
+ columnspan=3, padx=5, pady=5)
+ idle_v = Label(frameBg, text='IDLE version: ' + release,
+ fg=self.fg, bg=self.bg)
+ idle_v.grid(row=12, column=0, sticky=W, padx=10, pady=0)
+ idle_button_f = Frame(frameBg, bg=self.bg)
+ idle_button_f.grid(row=13, column=0, columnspan=3, sticky=NSEW)
+ idle_about_b = Button(idle_button_f, text='README', width=8,
+ highlightbackground=self.bg,
+ command=self.ShowIDLEAbout)
+ idle_about_b.pack(side=LEFT, padx=10, pady=10)
+ idle_news_b = Button(idle_button_f, text='NEWS', width=8,
+ highlightbackground=self.bg,
+ command=self.ShowIDLENEWS)
+ idle_news_b.pack(side=LEFT, padx=10, pady=10)
+ idle_credits_b = Button(idle_button_f, text='Credits', width=8,
+ highlightbackground=self.bg,
+ command=self.ShowIDLECredits)
+ idle_credits_b.pack(side=LEFT, padx=10, pady=10)
+
+ # License, et all, are of type _sitebuiltins._Printer
+ def ShowLicense(self):
+ self.display_printer_text('About - License', license)
+
+ def ShowCopyright(self):
+ self.display_printer_text('About - Copyright', copyright)
+
+ def ShowPythonCredits(self):
+ self.display_printer_text('About - Python Credits', credits)
+
+ # Encode CREDITS.txt to utf-8 for proper version of Loewis.
+ # Specify others as ascii until need utf-8, so catch errors.
+ def ShowIDLECredits(self):
+ self.display_file_text('About - Credits', 'CREDITS.txt', 'utf-8')
+
+ def ShowIDLEAbout(self):
+ self.display_file_text('About - Readme', 'README.txt', 'ascii')
+
+ def ShowIDLENEWS(self):
+ self.display_file_text('About - NEWS', 'NEWS.txt', 'utf-8')
+
+ def display_printer_text(self, title, printer):
+ printer._Printer__setup()
+ text = '\n'.join(printer._Printer__lines)
+ textView.view_text(self, title, text)
+
+ def display_file_text(self, title, filename, encoding=None):
+ fn = os.path.join(os.path.abspath(os.path.dirname(__file__)), filename)
+ textView.view_file(self, title, fn, encoding)
+
+ def Ok(self, event=None):
+ self.destroy()
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main('idlelib.idle_test.test_helpabout', verbosity=2, exit=False)
+ from idlelib.idle_test.htest import run
+ run(AboutDialog)
diff --git a/lib/python2.7/idlelib/config-extensions.def b/lib/python2.7/idlelib/config-extensions.def
new file mode 100644
index 0000000..a24b8c9
--- /dev/null
+++ b/lib/python2.7/idlelib/config-extensions.def
@@ -0,0 +1,99 @@
+# config-extensions.def
+#
+# IDLE reads several config files to determine user preferences. This
+# file is the default configuration file for IDLE extensions settings.
+#
+# Each extension must have at least one section, named after the
+# extension module. This section must contain an 'enable' item (=True to
+# enable the extension, =False to disable it), it may contain
+# 'enable_editor' or 'enable_shell' items, to apply it only to editor ir
+# shell windows, and may also contain any other general configuration
+# items for the extension. Other True/False values will also be
+# recognized as boolean by the Extension Configuration dialog.
+#
+# Each extension must define at least one section named
+# ExtensionName_bindings or ExtensionName_cfgBindings. If present,
+# ExtensionName_bindings defines virtual event bindings for the
+# extension that are not user re-configurable. If present,
+# ExtensionName_cfgBindings defines virtual event bindings for the
+# extension that may be sensibly re-configured.
+#
+# If there are no keybindings for a menus' virtual events, include lines
+# like <<toggle-code-context>>= (See [CodeContext], below.)
+#
+# Currently it is necessary to manually modify this file to change
+# extension key bindings and default values. To customize, create
+# ~/.idlerc/config-extensions.cfg and append the appropriate customized
+# section(s). Those sections will override the defaults in this file.
+#
+# Note: If a keybinding is already in use when the extension is loaded,
+# the extension's virtual event's keybinding will be set to ''.
+#
+# See config-keys.def for notes on specifying keys and extend.txt for
+# information on creating IDLE extensions.
+
+[AutoComplete]
+enable=True
+popupwait=2000
+[AutoComplete_cfgBindings]
+force-open-completions=<Control-Key-space>
+[AutoComplete_bindings]
+autocomplete=<Key-Tab>
+try-open-completions=<KeyRelease-period> <KeyRelease-slash> <KeyRelease-backslash>
+
+[AutoExpand]
+enable=True
+[AutoExpand_cfgBindings]
+expand-word=<Alt-Key-slash>
+
+[CallTips]
+enable=True
+[CallTips_cfgBindings]
+force-open-calltip=<Control-Key-backslash>
+[CallTips_bindings]
+try-open-calltip=<KeyRelease-parenleft>
+refresh-calltip=<KeyRelease-parenright> <KeyRelease-0>
+
+[CodeContext]
+enable=True
+enable_shell=False
+numlines=3
+visible=False
+bgcolor=LightGray
+fgcolor=Black
+[CodeContext_bindings]
+toggle-code-context=
+
+[FormatParagraph]
+enable=True
+max-width=72
+[FormatParagraph_cfgBindings]
+format-paragraph=<Alt-Key-q>
+
+[ParenMatch]
+enable=True
+style= expression
+flash-delay= 500
+bell=True
+[ParenMatch_cfgBindings]
+flash-paren=<Control-Key-0>
+[ParenMatch_bindings]
+paren-closed=<KeyRelease-parenright> <KeyRelease-bracketright> <KeyRelease-braceright>
+
+[RstripExtension]
+enable=True
+enable_shell=False
+enable_editor=True
+
+[ScriptBinding]
+enable=True
+enable_shell=False
+enable_editor=True
+[ScriptBinding_cfgBindings]
+run-module=<Key-F5>
+check-module=<Alt-Key-x>
+
+[ZoomHeight]
+enable=True
+[ZoomHeight_cfgBindings]
+zoom-height=<Alt-Key-2>
diff --git a/lib/python2.7/idlelib/config-highlight.def b/lib/python2.7/idlelib/config-highlight.def
new file mode 100644
index 0000000..4146e28
--- /dev/null
+++ b/lib/python2.7/idlelib/config-highlight.def
@@ -0,0 +1,93 @@
+# IDLE reads several config files to determine user preferences. This
+# file is the default config file for idle highlight theme settings.
+
+[IDLE Classic]
+normal-foreground= #000000
+normal-background= #ffffff
+keyword-foreground= #ff7700
+keyword-background= #ffffff
+builtin-foreground= #900090
+builtin-background= #ffffff
+comment-foreground= #dd0000
+comment-background= #ffffff
+string-foreground= #00aa00
+string-background= #ffffff
+definition-foreground= #0000ff
+definition-background= #ffffff
+hilite-foreground= #000000
+hilite-background= gray
+break-foreground= black
+break-background= #ffff55
+hit-foreground= #ffffff
+hit-background= #000000
+error-foreground= #000000
+error-background= #ff7777
+#cursor (only foreground can be set, restart IDLE)
+cursor-foreground= black
+#shell window
+stdout-foreground= blue
+stdout-background= #ffffff
+stderr-foreground= red
+stderr-background= #ffffff
+console-foreground= #770000
+console-background= #ffffff
+
+[IDLE New]
+normal-foreground= #000000
+normal-background= #ffffff
+keyword-foreground= #ff7700
+keyword-background= #ffffff
+builtin-foreground= #900090
+builtin-background= #ffffff
+comment-foreground= #dd0000
+comment-background= #ffffff
+string-foreground= #00aa00
+string-background= #ffffff
+definition-foreground= #0000ff
+definition-background= #ffffff
+hilite-foreground= #000000
+hilite-background= gray
+break-foreground= black
+break-background= #ffff55
+hit-foreground= #ffffff
+hit-background= #000000
+error-foreground= #000000
+error-background= #ff7777
+#cursor (only foreground can be set, restart IDLE)
+cursor-foreground= black
+#shell window
+stdout-foreground= blue
+stdout-background= #ffffff
+stderr-foreground= red
+stderr-background= #ffffff
+console-foreground= #770000
+console-background= #ffffff
+
+[IDLE Dark]
+comment-foreground = #dd0000
+console-foreground = #ff4d4d
+error-foreground = #FFFFFF
+hilite-background = #7e7e7e
+string-foreground = #02ff02
+stderr-background = #002240
+stderr-foreground = #ffb3b3
+console-background = #002240
+hit-background = #fbfbfb
+string-background = #002240
+normal-background = #002240
+hilite-foreground = #FFFFFF
+keyword-foreground = #ff8000
+error-background = #c86464
+keyword-background = #002240
+builtin-background = #002240
+break-background = #808000
+builtin-foreground = #ff00ff
+definition-foreground = #5e5eff
+stdout-foreground = #c2d1fa
+definition-background = #002240
+normal-foreground = #FFFFFF
+cursor-foreground = #ffffff
+stdout-background = #002240
+hit-foreground = #002240
+comment-background = #002240
+break-foreground = #FFFFFF
diff --git a/lib/python2.7/idlelib/config-keys.def b/lib/python2.7/idlelib/config-keys.def
new file mode 100644
index 0000000..3bfcb69
--- /dev/null
+++ b/lib/python2.7/idlelib/config-keys.def
@@ -0,0 +1,214 @@
+# IDLE reads several config files to determine user preferences. This
+# file is the default config file for idle key binding settings.
+# Where multiple keys are specified for an action: if they are separated
+# by a space (eg. action=<key1> <key2>) then the keys are alternatives, if
+# there is no space (eg. action=<key1><key2>) then the keys comprise a
+# single 'emacs style' multi-keystoke binding. The tk event specifier 'Key'
+# is used in all cases, for consistency in auto key conflict checking in the
+# configuration gui.
+
+[IDLE Classic Windows]
+copy=<Control-Key-c> <Control-Key-C>
+cut=<Control-Key-x> <Control-Key-X>
+paste=<Control-Key-v> <Control-Key-V>
+beginning-of-line= <Key-Home>
+center-insert=<Control-Key-l> <Control-Key-L>
+close-all-windows=<Control-Key-q> <Control-Key-Q>
+close-window=<Alt-Key-F4> <Meta-Key-F4>
+do-nothing=<Control-Key-F12>
+end-of-file=<Control-Key-d> <Control-Key-D>
+python-docs=<Key-F1>
+python-context-help=<Shift-Key-F1>
+history-next=<Alt-Key-n> <Meta-Key-n> <Alt-Key-N> <Meta-Key-N>
+history-previous=<Alt-Key-p> <Meta-Key-p> <Alt-Key-P> <Meta-Key-P>
+interrupt-execution=<Control-Key-c> <Control-Key-C>
+view-restart=<Key-F6>
+restart-shell=<Control-Key-F6>
+open-class-browser=<Alt-Key-c> <Meta-Key-c> <Alt-Key-C> <Meta-Key-C>
+open-module=<Alt-Key-m> <Meta-Key-m> <Alt-Key-M> <Meta-Key-M>
+open-new-window=<Control-Key-n> <Control-Key-N>
+open-window-from-file=<Control-Key-o> <Control-Key-O>
+plain-newline-and-indent=<Control-Key-j> <Control-Key-J>
+print-window=<Control-Key-p> <Control-Key-P>
+redo=<Control-Shift-Key-Z> <Control-Shift-Key-z>
+remove-selection=<Key-Escape>
+save-copy-of-window-as-file=<Alt-Shift-Key-S> <Alt-Shift-Key-s>
+save-window-as-file=<Control-Shift-Key-S> <Control-Shift-Key-s>
+save-window=<Control-Key-s> <Control-Key-S>
+select-all=<Control-Key-a> <Control-Key-A>
+toggle-auto-coloring=<Control-Key-slash>
+undo=<Control-Key-z> <Control-Key-Z>
+find=<Control-Key-f> <Control-Key-F>
+find-again=<Control-Key-g> <Key-F3> <Control-Key-G>
+find-in-files=<Alt-Key-F3> <Meta-Key-F3>
+find-selection=<Control-Key-F3>
+replace=<Control-Key-h> <Control-Key-H>
+goto-line=<Alt-Key-g> <Meta-Key-g> <Alt-Key-G> <Meta-Key-G>
+smart-backspace=<Key-BackSpace>
+newline-and-indent=<Key-Return> <Key-KP_Enter>
+smart-indent=<Key-Tab>
+indent-region=<Control-Key-bracketright>
+dedent-region=<Control-Key-bracketleft>
+comment-region=<Alt-Key-3> <Meta-Key-3>
+uncomment-region=<Alt-Key-4> <Meta-Key-4>
+tabify-region=<Alt-Key-5> <Meta-Key-5>
+untabify-region=<Alt-Key-6> <Meta-Key-6>
+toggle-tabs=<Alt-Key-t> <Meta-Key-t> <Alt-Key-T> <Meta-Key-T>
+change-indentwidth=<Alt-Key-u> <Meta-Key-u> <Alt-Key-U> <Meta-Key-U>
+del-word-left=<Control-Key-BackSpace>
+del-word-right=<Control-Key-Delete>
+
+[IDLE Classic Unix]
+copy=<Alt-Key-w> <Meta-Key-w>
+cut=<Control-Key-w>
+paste=<Control-Key-y>
+beginning-of-line=<Control-Key-a> <Key-Home>
+center-insert=<Control-Key-l>
+close-all-windows=<Control-Key-x><Control-Key-c>
+close-window=<Control-Key-x><Control-Key-0>
+do-nothing=<Control-Key-x>
+end-of-file=<Control-Key-d>
+history-next=<Alt-Key-n> <Meta-Key-n>
+history-previous=<Alt-Key-p> <Meta-Key-p>
+interrupt-execution=<Control-Key-c>
+view-restart=<Key-F6>
+restart-shell=<Control-Key-F6>
+open-class-browser=<Control-Key-x><Control-Key-b>
+open-module=<Control-Key-x><Control-Key-m>
+open-new-window=<Control-Key-x><Control-Key-n>
+open-window-from-file=<Control-Key-x><Control-Key-f>
+plain-newline-and-indent=<Control-Key-j>
+print-window=<Control-x><Control-Key-p>
+python-docs=<Control-Key-h>
+python-context-help=<Control-Shift-Key-H>
+redo=<Alt-Key-z> <Meta-Key-z>
+remove-selection=<Key-Escape>
+save-copy-of-window-as-file=<Control-Key-x><Control-Key-y>
+save-window-as-file=<Control-Key-x><Control-Key-w>
+save-window=<Control-Key-x><Control-Key-s>
+select-all=<Alt-Key-a> <Meta-Key-a>
+toggle-auto-coloring=<Control-Key-slash>
+undo=<Control-Key-z>
+find=<Control-Key-u><Control-Key-u><Control-Key-s>
+find-again=<Control-Key-u><Control-Key-s>
+find-in-files=<Alt-Key-s> <Meta-Key-s>
+find-selection=<Control-Key-s>
+replace=<Control-Key-r>
+goto-line=<Alt-Key-g> <Meta-Key-g>
+smart-backspace=<Key-BackSpace>
+newline-and-indent=<Key-Return> <Key-KP_Enter>
+smart-indent=<Key-Tab>
+indent-region=<Control-Key-bracketright>
+dedent-region=<Control-Key-bracketleft>
+comment-region=<Alt-Key-3>
+uncomment-region=<Alt-Key-4>
+tabify-region=<Alt-Key-5>
+untabify-region=<Alt-Key-6>
+toggle-tabs=<Alt-Key-t>
+change-indentwidth=<Alt-Key-u>
+del-word-left=<Alt-Key-BackSpace>
+del-word-right=<Alt-Key-d>
+
+[IDLE Classic Mac]
+copy=<Command-Key-c>
+cut=<Command-Key-x>
+paste=<Command-Key-v>
+beginning-of-line= <Key-Home>
+center-insert=<Control-Key-l>
+close-all-windows=<Command-Key-q>
+close-window=<Command-Key-w>
+do-nothing=<Control-Key-F12>
+end-of-file=<Control-Key-d>
+python-docs=<Key-F1>
+python-context-help=<Shift-Key-F1>
+history-next=<Control-Key-n>
+history-previous=<Control-Key-p>
+interrupt-execution=<Control-Key-c>
+view-restart=<Key-F6>
+restart-shell=<Control-Key-F6>
+open-class-browser=<Command-Key-b>
+open-module=<Command-Key-m>
+open-new-window=<Command-Key-n>
+open-window-from-file=<Command-Key-o>
+plain-newline-and-indent=<Control-Key-j>
+print-window=<Command-Key-p>
+redo=<Shift-Command-Key-Z>
+remove-selection=<Key-Escape>
+save-window-as-file=<Shift-Command-Key-S>
+save-window=<Command-Key-s>
+save-copy-of-window-as-file=<Option-Command-Key-s>
+select-all=<Command-Key-a>
+toggle-auto-coloring=<Control-Key-slash>
+undo=<Command-Key-z>
+find=<Command-Key-f>
+find-again=<Command-Key-g> <Key-F3>
+find-in-files=<Command-Key-F3>
+find-selection=<Shift-Command-Key-F3>
+replace=<Command-Key-r>
+goto-line=<Command-Key-j>
+smart-backspace=<Key-BackSpace>
+newline-and-indent=<Key-Return> <Key-KP_Enter>
+smart-indent=<Key-Tab>
+indent-region=<Command-Key-bracketright>
+dedent-region=<Command-Key-bracketleft>
+comment-region=<Control-Key-3>
+uncomment-region=<Control-Key-4>
+tabify-region=<Control-Key-5>
+untabify-region=<Control-Key-6>
+toggle-tabs=<Control-Key-t>
+change-indentwidth=<Control-Key-u>
+del-word-left=<Control-Key-BackSpace>
+del-word-right=<Control-Key-Delete>
+
+[IDLE Classic OSX]
+toggle-tabs = <Control-Key-t>
+interrupt-execution = <Control-Key-c>
+untabify-region = <Control-Key-6>
+remove-selection = <Key-Escape>
+print-window = <Command-Key-p>
+replace = <Command-Key-r>
+goto-line = <Command-Key-j>
+plain-newline-and-indent = <Control-Key-j>
+history-previous = <Control-Key-p>
+beginning-of-line = <Control-Key-Left>
+end-of-line = <Control-Key-Right>
+comment-region = <Control-Key-3>
+redo = <Shift-Command-Key-Z>
+close-window = <Command-Key-w>
+restart-shell = <Control-Key-F6>
+save-window-as-file = <Shift-Command-Key-S>
+close-all-windows = <Command-Key-q>
+view-restart = <Key-F6>
+tabify-region = <Control-Key-5>
+find-again = <Command-Key-g> <Key-F3>
+find = <Command-Key-f>
+toggle-auto-coloring = <Control-Key-slash>
+select-all = <Command-Key-a>
+smart-backspace = <Key-BackSpace>
+change-indentwidth = <Control-Key-u>
+do-nothing = <Control-Key-F12>
+smart-indent = <Key-Tab>
+center-insert = <Control-Key-l>
+history-next = <Control-Key-n>
+del-word-right = <Option-Key-Delete>
+undo = <Command-Key-z>
+save-window = <Command-Key-s>
+uncomment-region = <Control-Key-4>
+cut = <Command-Key-x>
+find-in-files = <Command-Key-F3>
+dedent-region = <Command-Key-bracketleft>
+copy = <Command-Key-c>
+paste = <Command-Key-v>
+indent-region = <Command-Key-bracketright>
+del-word-left = <Option-Key-BackSpace> <Option-Command-Key-BackSpace>
+newline-and-indent = <Key-Return> <Key-KP_Enter>
+end-of-file = <Control-Key-d>
+open-class-browser = <Command-Key-b>
+open-new-window = <Command-Key-n>
+open-module = <Command-Key-m>
+find-selection = <Shift-Command-Key-F3>
+python-context-help = <Shift-Key-F1>
+save-copy-of-window-as-file = <Option-Command-Key-s>
+open-window-from-file = <Command-Key-o>
+python-docs = <Key-F1>
+
diff --git a/lib/python2.7/idlelib/config-main.def b/lib/python2.7/idlelib/config-main.def
new file mode 100644
index 0000000..f241199
--- /dev/null
+++ b/lib/python2.7/idlelib/config-main.def
@@ -0,0 +1,78 @@
+# IDLE reads several config files to determine user preferences. This
+# file is the default config file for general idle settings.
+#
+# When IDLE starts, it will look in
+# the following two sets of files, in order:
+#
+# default configuration
+# ---------------------
+# config-main.def the default general config file
+# config-extensions.def the default extension config file
+# config-highlight.def the default highlighting config file
+# config-keys.def the default keybinding config file
+#
+# user configuration
+# -------------------
+# ~/.idlerc/config-main.cfg the user general config file
+# ~/.idlerc/config-extensions.cfg the user extension config file
+# ~/.idlerc/config-highlight.cfg the user highlighting config file
+# ~/.idlerc/config-keys.cfg the user keybinding config file
+#
+# On Windows2000 and Windows XP the .idlerc directory is at
+# Documents and Settings\<username>\.idlerc
+#
+# On Windows98 it is at c:\.idlerc
+#
+# Any options the user saves through the config dialog will be saved to
+# the relevant user config file. Reverting any general setting to the
+# default causes that entry to be wiped from the user file and re-read
+# from the default file. User highlighting themes or keybinding sets are
+# retained unless specifically deleted within the config dialog. Choosing
+# one of the default themes or keysets just applies the relevant settings
+# from the default file.
+#
+# Additional help sources are listed in the [HelpFiles] section and must be
+# viewable by a web browser (or the Windows Help viewer in the case of .chm
+# files). These sources will be listed on the Help menu. The pattern is
+# <sequence_number = menu item;/path/to/help/source>
+# You can't use a semi-colon in a menu item or path. The path will be platform
+# specific because of path separators, drive specs etc.
+#
+# It is best to use the Configuration GUI to set up additional help sources!
+# Example:
+#1 = My Extra Help Source;/usr/share/doc/foo/index.html
+#2 = Another Help Source;/path/to/another.pdf
+
+[General]
+editor-on-startup= 0
+autosave= 0
+print-command-posix=lpr %s
+print-command-win=start /min notepad /p %s
+delete-exitfunc= 1
+
+[EditorWindow]
+width= 80
+height= 40
+font= TkFixedFont
+font-size= 10
+font-bold= 0
+encoding= none
+
+[Indent]
+use-spaces= 1
+num-spaces= 4
+
+[Theme]
+default= 1
+name= IDLE Classic
+name2=
+# name2 set in user config-main.cfg for themes added after 2015 Oct 1
+
+[Keys]
+default= 1
+name= IDLE Classic Windows
+
+[History]
+cyclic=1
+
+[HelpFiles]
diff --git a/lib/python2.7/idlelib/configDialog.py b/lib/python2.7/idlelib/configDialog.py
new file mode 100644
index 0000000..d53f5ff
--- /dev/null
+++ b/lib/python2.7/idlelib/configDialog.py
@@ -0,0 +1,1455 @@
+"""IDLE Configuration Dialog: support user customization of IDLE by GUI
+
+Customize font faces, sizes, and colorization attributes. Set indentation
+defaults. Customize keybindings. Colorization and keybindings can be
+saved as user defined sets. Select startup options including shell/editor
+and default window size. Define additional help sources.
+
+Note that tab width in IDLE is currently fixed at eight due to Tk issues.
+Refer to comments in EditorWindow autoindent code for details.
+
+"""
+from Tkinter import *
+import tkMessageBox, tkColorChooser, tkFont
+
+from idlelib.configHandler import idleConf
+from idlelib.dynOptionMenuWidget import DynOptionMenu
+from idlelib.keybindingDialog import GetKeysDialog
+from idlelib.configSectionNameDialog import GetCfgSectionNameDialog
+from idlelib.configHelpSourceEdit import GetHelpSourceDialog
+from idlelib.tabbedpages import TabbedPageSet
+from idlelib.textView import view_text
+from idlelib import macosxSupport
+
+class ConfigDialog(Toplevel):
+
+ def __init__(self, parent, title='', _htest=False, _utest=False):
+ """
+ _htest - bool, change box location when running htest
+ _utest - bool, don't wait_window when running unittest
+ """
+ Toplevel.__init__(self, parent)
+ self.parent = parent
+ if _htest:
+ parent.instance_dict = {}
+ self.wm_withdraw()
+
+ self.configure(borderwidth=5)
+ self.title(title or 'IDLE Preferences')
+ self.geometry(
+ "+%d+%d" % (parent.winfo_rootx() + 20,
+ parent.winfo_rooty() + (30 if not _htest else 150)))
+ #Theme Elements. Each theme element key is its display name.
+ #The first value of the tuple is the sample area tag name.
+ #The second value is the display name list sort index.
+ self.themeElements={
+ 'Normal Text': ('normal', '00'),
+ 'Python Keywords': ('keyword', '01'),
+ 'Python Definitions': ('definition', '02'),
+ 'Python Builtins': ('builtin', '03'),
+ 'Python Comments': ('comment', '04'),
+ 'Python Strings': ('string', '05'),
+ 'Selected Text': ('hilite', '06'),
+ 'Found Text': ('hit', '07'),
+ 'Cursor': ('cursor', '08'),
+ 'Editor Breakpoint': ('break', '09'),
+ 'Shell Normal Text': ('console', '10'),
+ 'Shell Error Text': ('error', '11'),
+ 'Shell Stdout Text': ('stdout', '12'),
+ 'Shell Stderr Text': ('stderr', '13'),
+ }
+ self.ResetChangedItems() #load initial values in changed items dict
+ self.CreateWidgets()
+ self.resizable(height=FALSE, width=FALSE)
+ self.transient(parent)
+ self.grab_set()
+ self.protocol("WM_DELETE_WINDOW", self.Cancel)
+ self.tabPages.focus_set()
+ #key bindings for this dialog
+ #self.bind('<Escape>', self.Cancel) #dismiss dialog, no save
+ #self.bind('<Alt-a>', self.Apply) #apply changes, save
+ #self.bind('<F1>', self.Help) #context help
+ self.LoadConfigs()
+ self.AttachVarCallbacks() #avoid callbacks during LoadConfigs
+
+ if not _utest:
+ self.wm_deiconify()
+ self.wait_window()
+
+ def CreateWidgets(self):
+ self.tabPages = TabbedPageSet(self,
+ page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General',
+ 'Extensions'])
+ self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH)
+ self.CreatePageFontTab()
+ self.CreatePageHighlight()
+ self.CreatePageKeys()
+ self.CreatePageGeneral()
+ self.CreatePageExtensions()
+ self.create_action_buttons().pack(side=BOTTOM)
+
+ def create_action_buttons(self):
+ if macosxSupport.isAquaTk():
+ # Changing the default padding on OSX results in unreadable
+ # text in the buttons
+ paddingArgs = {}
+ else:
+ paddingArgs = {'padx':6, 'pady':3}
+ outer = Frame(self, pady=2)
+ buttons = Frame(outer, pady=2)
+ for txt, cmd in (
+ ('Ok', self.Ok),
+ ('Apply', self.Apply),
+ ('Cancel', self.Cancel),
+ ('Help', self.Help)):
+ Button(buttons, text=txt, command=cmd, takefocus=FALSE,
+ **paddingArgs).pack(side=LEFT, padx=5)
+ # add space above buttons
+ Frame(outer, height=2, borderwidth=0).pack(side=TOP)
+ buttons.pack(side=BOTTOM)
+ return outer
+
+ def CreatePageFontTab(self):
+ parent = self.parent
+ self.fontSize = StringVar(parent)
+ self.fontBold = BooleanVar(parent)
+ self.fontName = StringVar(parent)
+ self.spaceNum = IntVar(parent)
+ self.editFont = tkFont.Font(parent, ('courier', 10, 'normal'))
+
+ ##widget creation
+ #body frame
+ frame = self.tabPages.pages['Fonts/Tabs'].frame
+ #body section frames
+ frameFont = LabelFrame(
+ frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
+ frameIndent = LabelFrame(
+ frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
+ #frameFont
+ frameFontName = Frame(frameFont)
+ frameFontParam = Frame(frameFont)
+ labelFontNameTitle = Label(
+ frameFontName, justify=LEFT, text='Font Face :')
+ self.listFontName = Listbox(
+ frameFontName, height=5, takefocus=FALSE, exportselection=FALSE)
+ self.listFontName.bind(
+ '<ButtonRelease-1>', self.OnListFontButtonRelease)
+ scrollFont = Scrollbar(frameFontName)
+ scrollFont.config(command=self.listFontName.yview)
+ self.listFontName.config(yscrollcommand=scrollFont.set)
+ labelFontSizeTitle = Label(frameFontParam, text='Size :')
+ self.optMenuFontSize = DynOptionMenu(
+ frameFontParam, self.fontSize, None, command=self.SetFontSample)
+ checkFontBold = Checkbutton(
+ frameFontParam, variable=self.fontBold, onvalue=1,
+ offvalue=0, text='Bold', command=self.SetFontSample)
+ frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1)
+ self.labelFontSample = Label(
+ frameFontSample, justify=LEFT, font=self.editFont,
+ text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]')
+ #frameIndent
+ frameIndentSize = Frame(frameIndent)
+ labelSpaceNumTitle = Label(
+ frameIndentSize, justify=LEFT,
+ text='Python Standard: 4 Spaces!')
+ self.scaleSpaceNum = Scale(
+ frameIndentSize, variable=self.spaceNum,
+ orient='horizontal', tickinterval=2, from_=2, to=16)
+
+ #widget packing
+ #body
+ frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y)
+ #frameFont
+ frameFontName.pack(side=TOP, padx=5, pady=5, fill=X)
+ frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X)
+ labelFontNameTitle.pack(side=TOP, anchor=W)
+ self.listFontName.pack(side=LEFT, expand=TRUE, fill=X)
+ scrollFont.pack(side=LEFT, fill=Y)
+ labelFontSizeTitle.pack(side=LEFT, anchor=W)
+ self.optMenuFontSize.pack(side=LEFT, anchor=W)
+ checkFontBold.pack(side=LEFT, anchor=W, padx=20)
+ frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ self.labelFontSample.pack(expand=TRUE, fill=BOTH)
+ #frameIndent
+ frameIndentSize.pack(side=TOP, fill=X)
+ labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5)
+ self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X)
+ return frame
+
+ def CreatePageHighlight(self):
+ parent = self.parent
+ self.builtinTheme = StringVar(parent)
+ self.customTheme = StringVar(parent)
+ self.fgHilite = BooleanVar(parent)
+ self.colour = StringVar(parent)
+ self.fontName = StringVar(parent)
+ self.themeIsBuiltin = BooleanVar(parent)
+ self.highlightTarget = StringVar(parent)
+
+ ##widget creation
+ #body frame
+ frame = self.tabPages.pages['Highlighting'].frame
+ #body section frames
+ frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE,
+ text=' Custom Highlighting ')
+ frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE,
+ text=' Highlighting Theme ')
+ #frameCustom
+ self.textHighlightSample=Text(
+ frameCustom, relief=SOLID, borderwidth=1,
+ font=('courier', 12, ''), cursor='hand2', width=21, height=11,
+ takefocus=FALSE, highlightthickness=0, wrap=NONE)
+ text=self.textHighlightSample
+ text.bind('<Double-Button-1>', lambda e: 'break')
+ text.bind('<B1-Motion>', lambda e: 'break')
+ textAndTags=(
+ ('#you can click here', 'comment'), ('\n', 'normal'),
+ ('#to choose items', 'comment'), ('\n', 'normal'),
+ ('def', 'keyword'), (' ', 'normal'),
+ ('func', 'definition'), ('(param):\n ', 'normal'),
+ ('"""string"""', 'string'), ('\n var0 = ', 'normal'),
+ ("'string'", 'string'), ('\n var1 = ', 'normal'),
+ ("'selected'", 'hilite'), ('\n var2 = ', 'normal'),
+ ("'found'", 'hit'), ('\n var3 = ', 'normal'),
+ ('list', 'builtin'), ('(', 'normal'),
+ ('None', 'builtin'), (')\n', 'normal'),
+ (' breakpoint("line")', 'break'), ('\n\n', 'normal'),
+ (' error ', 'error'), (' ', 'normal'),
+ ('cursor |', 'cursor'), ('\n ', 'normal'),
+ ('shell', 'console'), (' ', 'normal'),
+ ('stdout', 'stdout'), (' ', 'normal'),
+ ('stderr', 'stderr'), ('\n', 'normal'))
+ for txTa in textAndTags:
+ text.insert(END, txTa[0], txTa[1])
+ for element in self.themeElements:
+ def tem(event, elem=element):
+ event.widget.winfo_toplevel().highlightTarget.set(elem)
+ text.tag_bind(
+ self.themeElements[element][0], '<ButtonPress-1>', tem)
+ text.config(state=DISABLED)
+ self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1)
+ frameFgBg = Frame(frameCustom)
+ buttonSetColour = Button(
+ self.frameColourSet, text='Choose Colour for :',
+ command=self.GetColour, highlightthickness=0)
+ self.optMenuHighlightTarget = DynOptionMenu(
+ self.frameColourSet, self.highlightTarget, None,
+ highlightthickness=0) #, command=self.SetHighlightTargetBinding
+ self.radioFg = Radiobutton(
+ frameFgBg, variable=self.fgHilite, value=1,
+ text='Foreground', command=self.SetColourSampleBinding)
+ self.radioBg=Radiobutton(
+ frameFgBg, variable=self.fgHilite, value=0,
+ text='Background', command=self.SetColourSampleBinding)
+ self.fgHilite.set(1)
+ buttonSaveCustomTheme = Button(
+ frameCustom, text='Save as New Custom Theme',
+ command=self.SaveAsNewTheme)
+ #frameTheme
+ labelTypeTitle = Label(frameTheme, text='Select : ')
+ self.radioThemeBuiltin = Radiobutton(
+ frameTheme, variable=self.themeIsBuiltin, value=1,
+ command=self.SetThemeType, text='a Built-in Theme')
+ self.radioThemeCustom = Radiobutton(
+ frameTheme, variable=self.themeIsBuiltin, value=0,
+ command=self.SetThemeType, text='a Custom Theme')
+ self.optMenuThemeBuiltin = DynOptionMenu(
+ frameTheme, self.builtinTheme, None, command=None)
+ self.optMenuThemeCustom=DynOptionMenu(
+ frameTheme, self.customTheme, None, command=None)
+ self.buttonDeleteCustomTheme=Button(
+ frameTheme, text='Delete Custom Theme',
+ command=self.DeleteCustomTheme)
+ self.new_custom_theme = Label(frameTheme, bd=2)
+
+ ##widget packing
+ #body
+ frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y)
+ #frameCustom
+ self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
+ frameFgBg.pack(side=TOP, padx=5, pady=0)
+ self.textHighlightSample.pack(
+ side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ buttonSetColour.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
+ self.optMenuHighlightTarget.pack(
+ side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
+ self.radioFg.pack(side=LEFT, anchor=E)
+ self.radioBg.pack(side=RIGHT, anchor=W)
+ buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
+ #frameTheme
+ labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5)
+ self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5)
+ self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2)
+ self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5)
+ self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
+ self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5)
+ self.new_custom_theme.pack(side=TOP, fill=X, pady=5)
+ return frame
+
+ def CreatePageKeys(self):
+ parent = self.parent
+ self.bindingTarget = StringVar(parent)
+ self.builtinKeys = StringVar(parent)
+ self.customKeys = StringVar(parent)
+ self.keysAreBuiltin = BooleanVar(parent)
+ self.keyBinding = StringVar(parent)
+
+ ##widget creation
+ #body frame
+ frame = self.tabPages.pages['Keys'].frame
+ #body section frames
+ frameCustom = LabelFrame(
+ frame, borderwidth=2, relief=GROOVE,
+ text=' Custom Key Bindings ')
+ frameKeySets = LabelFrame(
+ frame, borderwidth=2, relief=GROOVE, text=' Key Set ')
+ #frameCustom
+ frameTarget = Frame(frameCustom)
+ labelTargetTitle = Label(frameTarget, text='Action - Key(s)')
+ scrollTargetY = Scrollbar(frameTarget)
+ scrollTargetX = Scrollbar(frameTarget, orient=HORIZONTAL)
+ self.listBindings = Listbox(
+ frameTarget, takefocus=FALSE, exportselection=FALSE)
+ self.listBindings.bind('<ButtonRelease-1>', self.KeyBindingSelected)
+ scrollTargetY.config(command=self.listBindings.yview)
+ scrollTargetX.config(command=self.listBindings.xview)
+ self.listBindings.config(yscrollcommand=scrollTargetY.set)
+ self.listBindings.config(xscrollcommand=scrollTargetX.set)
+ self.buttonNewKeys = Button(
+ frameCustom, text='Get New Keys for Selection',
+ command=self.GetNewKeys, state=DISABLED)
+ #frameKeySets
+ frames = [Frame(frameKeySets, padx=2, pady=2, borderwidth=0)
+ for i in range(2)]
+ self.radioKeysBuiltin = Radiobutton(
+ frames[0], variable=self.keysAreBuiltin, value=1,
+ command=self.SetKeysType, text='Use a Built-in Key Set')
+ self.radioKeysCustom = Radiobutton(
+ frames[0], variable=self.keysAreBuiltin, value=0,
+ command=self.SetKeysType, text='Use a Custom Key Set')
+ self.optMenuKeysBuiltin = DynOptionMenu(
+ frames[0], self.builtinKeys, None, command=None)
+ self.optMenuKeysCustom = DynOptionMenu(
+ frames[0], self.customKeys, None, command=None)
+ self.buttonDeleteCustomKeys = Button(
+ frames[1], text='Delete Custom Key Set',
+ command=self.DeleteCustomKeys)
+ buttonSaveCustomKeys = Button(
+ frames[1], text='Save as New Custom Key Set',
+ command=self.SaveAsNewKeySet)
+
+ ##widget packing
+ #body
+ frameCustom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ frameKeySets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
+ #frameCustom
+ self.buttonNewKeys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
+ frameTarget.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ #frame target
+ frameTarget.columnconfigure(0, weight=1)
+ frameTarget.rowconfigure(1, weight=1)
+ labelTargetTitle.grid(row=0, column=0, columnspan=2, sticky=W)
+ self.listBindings.grid(row=1, column=0, sticky=NSEW)
+ scrollTargetY.grid(row=1, column=1, sticky=NS)
+ scrollTargetX.grid(row=2, column=0, sticky=EW)
+ #frameKeySets
+ self.radioKeysBuiltin.grid(row=0, column=0, sticky=W+NS)
+ self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS)
+ self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW)
+ self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW)
+ self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
+ buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
+ frames[0].pack(side=TOP, fill=BOTH, expand=True)
+ frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
+ return frame
+
+ def CreatePageGeneral(self):
+ parent = self.parent
+ self.winWidth = StringVar(parent)
+ self.winHeight = StringVar(parent)
+ self.startupEdit = IntVar(parent)
+ self.autoSave = IntVar(parent)
+ self.encoding = StringVar(parent)
+ self.userHelpBrowser = BooleanVar(parent)
+ self.helpBrowser = StringVar(parent)
+
+ #widget creation
+ #body
+ frame = self.tabPages.pages['General'].frame
+ #body section frames
+ frameRun = LabelFrame(frame, borderwidth=2, relief=GROOVE,
+ text=' Startup Preferences ')
+ frameSave = LabelFrame(frame, borderwidth=2, relief=GROOVE,
+ text=' Autosave Preferences ')
+ frameWinSize = 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, value=1,
+ command=self.SetKeysType, text="Open Edit Window")
+ radioStartupShell = Radiobutton(
+ frameRun, 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')
+ #frameWinSize
+ labelWinSizeTitle = Label(
+ frameWinSize, text='Initial Window Size (in characters)')
+ labelWinWidthTitle = Label(frameWinSize, text='Width')
+ entryWinWidth = Entry(
+ frameWinSize, textvariable=self.winWidth, width=3)
+ labelWinHeightTitle = Label(frameWinSize, text='Height')
+ entryWinHeight = Entry(
+ frameWinSize, textvariable=self.winHeight, 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)
+ scrollHelpList = Scrollbar(frameHelpList)
+ self.listHelp = Listbox(
+ frameHelpList, height=5, takefocus=FALSE,
+ exportselection=FALSE)
+ scrollHelpList.config(command=self.listHelp.yview)
+ self.listHelp.config(yscrollcommand=scrollHelpList.set)
+ self.listHelp.bind('<ButtonRelease-1>', self.HelpSourceSelected)
+ self.buttonHelpListEdit = Button(
+ frameHelpListButtons, text='Edit', state=DISABLED,
+ width=8, command=self.HelpListItemEdit)
+ self.buttonHelpListAdd = Button(
+ frameHelpListButtons, text='Add',
+ width=8, command=self.HelpListItemAdd)
+ self.buttonHelpListRemove = Button(
+ frameHelpListButtons, text='Remove', state=DISABLED,
+ width=8, command=self.HelpListItemRemove)
+
+ #widget packing
+ #body
+ 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)
+ frameEncoding.pack(side=TOP, padx=5, pady=5, fill=X)
+ frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ #frameRun
+ 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)
+ #frameWinSize
+ labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
+ entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5)
+ labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5)
+ entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5)
+ labelWinWidthTitle.pack(side=RIGHT, anchor=E, 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)
+ scrollHelpList.pack(side=RIGHT, anchor=W, fill=Y)
+ self.listHelp.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH)
+ self.buttonHelpListEdit.pack(side=TOP, anchor=W, pady=5)
+ self.buttonHelpListAdd.pack(side=TOP, anchor=W)
+ self.buttonHelpListRemove.pack(side=TOP, anchor=W, pady=5)
+ return frame
+
+ def AttachVarCallbacks(self):
+ self.fontSize.trace_variable('w', self.VarChanged_font)
+ self.fontName.trace_variable('w', self.VarChanged_font)
+ self.fontBold.trace_variable('w', self.VarChanged_font)
+ self.spaceNum.trace_variable('w', self.VarChanged_spaceNum)
+ self.colour.trace_variable('w', self.VarChanged_colour)
+ self.builtinTheme.trace_variable('w', self.VarChanged_builtinTheme)
+ self.customTheme.trace_variable('w', self.VarChanged_customTheme)
+ self.themeIsBuiltin.trace_variable('w', self.VarChanged_themeIsBuiltin)
+ self.highlightTarget.trace_variable('w', self.VarChanged_highlightTarget)
+ self.keyBinding.trace_variable('w', self.VarChanged_keyBinding)
+ self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys)
+ self.customKeys.trace_variable('w', self.VarChanged_customKeys)
+ self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin)
+ self.winWidth.trace_variable('w', self.VarChanged_winWidth)
+ self.winHeight.trace_variable('w', self.VarChanged_winHeight)
+ self.startupEdit.trace_variable('w', self.VarChanged_startupEdit)
+ self.autoSave.trace_variable('w', self.VarChanged_autoSave)
+ self.encoding.trace_variable('w', self.VarChanged_encoding)
+
+ def remove_var_callbacks(self):
+ for var in (
+ self.fontSize, self.fontName, self.fontBold,
+ self.spaceNum, self.colour, self.builtinTheme,
+ self.customTheme, self.themeIsBuiltin, self.highlightTarget,
+ self.keyBinding, self.builtinKeys, self.customKeys,
+ self.keysAreBuiltin, self.winWidth, self.winHeight,
+ self.startupEdit, self.autoSave, self.encoding,):
+ var.trace_vdelete('w', var.trace_vinfo()[0][1])
+
+ def VarChanged_font(self, *params):
+ '''When one font attribute changes, save them all, as they are
+ not independent from each other. In particular, when we are
+ overriding the default font, we need to write out everything.
+ '''
+ value = self.fontName.get()
+ self.AddChangedItem('main', 'EditorWindow', 'font', value)
+ value = self.fontSize.get()
+ self.AddChangedItem('main', 'EditorWindow', 'font-size', value)
+ value = self.fontBold.get()
+ self.AddChangedItem('main', 'EditorWindow', 'font-bold', value)
+
+ def VarChanged_spaceNum(self, *params):
+ value = self.spaceNum.get()
+ self.AddChangedItem('main', 'Indent', 'num-spaces', value)
+
+ def VarChanged_colour(self, *params):
+ self.OnNewColourSet()
+
+ def VarChanged_builtinTheme(self, *params):
+ value = self.builtinTheme.get()
+ if value == 'IDLE Dark':
+ if idleConf.GetOption('main', 'Theme', 'name') != 'IDLE New':
+ self.AddChangedItem('main', 'Theme', 'name', 'IDLE Classic')
+ self.AddChangedItem('main', 'Theme', 'name2', value)
+ self.new_custom_theme.config(text='New theme, see Help',
+ fg='#500000')
+ else:
+ self.AddChangedItem('main', 'Theme', 'name', value)
+ self.AddChangedItem('main', 'Theme', 'name2', '')
+ self.new_custom_theme.config(text='', fg='black')
+ self.PaintThemeSample()
+
+ def VarChanged_customTheme(self, *params):
+ value = self.customTheme.get()
+ if value != '- no custom themes -':
+ self.AddChangedItem('main', 'Theme', 'name', value)
+ self.PaintThemeSample()
+
+ def VarChanged_themeIsBuiltin(self, *params):
+ value = self.themeIsBuiltin.get()
+ self.AddChangedItem('main', 'Theme', 'default', value)
+ if value:
+ self.VarChanged_builtinTheme()
+ else:
+ self.VarChanged_customTheme()
+
+ def VarChanged_highlightTarget(self, *params):
+ self.SetHighlightTarget()
+
+ def VarChanged_keyBinding(self, *params):
+ value = self.keyBinding.get()
+ keySet = self.customKeys.get()
+ event = self.listBindings.get(ANCHOR).split()[0]
+ if idleConf.IsCoreBinding(event):
+ #this is a core keybinding
+ self.AddChangedItem('keys', keySet, event, value)
+ else: #this is an extension key binding
+ extName = idleConf.GetExtnNameForEvent(event)
+ extKeybindSection = extName + '_cfgBindings'
+ self.AddChangedItem('extensions', extKeybindSection, event, value)
+
+ def VarChanged_builtinKeys(self, *params):
+ value = self.builtinKeys.get()
+ self.AddChangedItem('main', 'Keys', 'name', value)
+ self.LoadKeysList(value)
+
+ def VarChanged_customKeys(self, *params):
+ value = self.customKeys.get()
+ if value != '- no custom keys -':
+ self.AddChangedItem('main', 'Keys', 'name', value)
+ self.LoadKeysList(value)
+
+ def VarChanged_keysAreBuiltin(self, *params):
+ value = self.keysAreBuiltin.get()
+ self.AddChangedItem('main', 'Keys', 'default', value)
+ if value:
+ self.VarChanged_builtinKeys()
+ else:
+ self.VarChanged_customKeys()
+
+ def VarChanged_winWidth(self, *params):
+ value = self.winWidth.get()
+ self.AddChangedItem('main', 'EditorWindow', 'width', value)
+
+ def VarChanged_winHeight(self, *params):
+ value = self.winHeight.get()
+ self.AddChangedItem('main', 'EditorWindow', 'height', value)
+
+ def VarChanged_startupEdit(self, *params):
+ value = self.startupEdit.get()
+ self.AddChangedItem('main', 'General', 'editor-on-startup', value)
+
+ def VarChanged_autoSave(self, *params):
+ value = self.autoSave.get()
+ self.AddChangedItem('main', 'General', 'autosave', value)
+
+ def VarChanged_encoding(self, *params):
+ value = self.encoding.get()
+ self.AddChangedItem('main', 'EditorWindow', 'encoding', 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
+ #dictionary. The key should be the config file section name and the
+ #value a dictionary, whose key:value pairs are item=value pairs for
+ #that config file section.
+ self.changedItems = {'main':{}, 'highlight':{}, 'keys':{},
+ 'extensions':{}}
+
+ def AddChangedItem(self, typ, section, item, value):
+ value = str(value) #make sure we use a string
+ if section not in self.changedItems[typ]:
+ self.changedItems[typ][section] = {}
+ self.changedItems[typ][section][item] = value
+
+ def GetDefaultItems(self):
+ dItems={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}}
+ for configType in dItems:
+ sections = idleConf.GetSectionList('default', configType)
+ for section in sections:
+ dItems[configType][section] = {}
+ options = idleConf.defaultCfg[configType].GetOptionList(section)
+ for option in options:
+ dItems[configType][section][option] = (
+ idleConf.defaultCfg[configType].Get(section, option))
+ return dItems
+
+ def SetThemeType(self):
+ if self.themeIsBuiltin.get():
+ self.optMenuThemeBuiltin.config(state=NORMAL)
+ self.optMenuThemeCustom.config(state=DISABLED)
+ self.buttonDeleteCustomTheme.config(state=DISABLED)
+ else:
+ self.optMenuThemeBuiltin.config(state=DISABLED)
+ self.radioThemeCustom.config(state=NORMAL)
+ self.optMenuThemeCustom.config(state=NORMAL)
+ self.buttonDeleteCustomTheme.config(state=NORMAL)
+
+ def SetKeysType(self):
+ if self.keysAreBuiltin.get():
+ self.optMenuKeysBuiltin.config(state=NORMAL)
+ self.optMenuKeysCustom.config(state=DISABLED)
+ self.buttonDeleteCustomKeys.config(state=DISABLED)
+ else:
+ self.optMenuKeysBuiltin.config(state=DISABLED)
+ self.radioKeysCustom.config(state=NORMAL)
+ self.optMenuKeysCustom.config(state=NORMAL)
+ self.buttonDeleteCustomKeys.config(state=NORMAL)
+
+ def GetNewKeys(self):
+ listIndex = self.listBindings.index(ANCHOR)
+ binding = self.listBindings.get(listIndex)
+ bindName = binding.split()[0] #first part, up to first space
+ if self.keysAreBuiltin.get():
+ currentKeySetName = self.builtinKeys.get()
+ else:
+ currentKeySetName = self.customKeys.get()
+ currentBindings = idleConf.GetCurrentKeySet()
+ if currentKeySetName in self.changedItems['keys']: #unsaved changes
+ keySetChanges = self.changedItems['keys'][currentKeySetName]
+ for event in keySetChanges:
+ currentBindings[event] = keySetChanges[event].split()
+ currentKeySequences = currentBindings.values()
+ newKeys = GetKeysDialog(self, 'Get New Keys', bindName,
+ currentKeySequences).result
+ if newKeys: #new keys were specified
+ if self.keysAreBuiltin.get(): #current key set is a built-in
+ message = ('Your changes will be saved as a new Custom Key Set.'
+ ' Enter a name for your new Custom Key Set below.')
+ newKeySet = self.GetNewKeysName(message)
+ if not newKeySet: #user cancelled custom key set creation
+ self.listBindings.select_set(listIndex)
+ self.listBindings.select_anchor(listIndex)
+ return
+ else: #create new custom key set based on previously active key set
+ self.CreateNewKeySet(newKeySet)
+ self.listBindings.delete(listIndex)
+ self.listBindings.insert(listIndex, bindName+' - '+newKeys)
+ self.listBindings.select_set(listIndex)
+ self.listBindings.select_anchor(listIndex)
+ self.keyBinding.set(newKeys)
+ else:
+ self.listBindings.select_set(listIndex)
+ self.listBindings.select_anchor(listIndex)
+
+ def GetNewKeysName(self, message):
+ usedNames = (idleConf.GetSectionList('user', 'keys') +
+ idleConf.GetSectionList('default', 'keys'))
+ newKeySet = GetCfgSectionNameDialog(
+ self, 'New Custom Key Set', message, usedNames).result
+ return newKeySet
+
+ def SaveAsNewKeySet(self):
+ newKeysName = self.GetNewKeysName('New Key Set Name:')
+ if newKeysName:
+ self.CreateNewKeySet(newKeysName)
+
+ def KeyBindingSelected(self, event):
+ self.buttonNewKeys.config(state=NORMAL)
+
+ def CreateNewKeySet(self, newKeySetName):
+ #creates new custom key set based on the previously active key set,
+ #and makes the new key set active
+ if self.keysAreBuiltin.get():
+ prevKeySetName = self.builtinKeys.get()
+ else:
+ prevKeySetName = self.customKeys.get()
+ prevKeys = idleConf.GetCoreKeys(prevKeySetName)
+ newKeys = {}
+ for event in prevKeys: #add key set to changed items
+ eventName = event[2:-2] #trim off the angle brackets
+ binding = ' '.join(prevKeys[event])
+ newKeys[eventName] = binding
+ #handle any unsaved changes to prev key set
+ if prevKeySetName in self.changedItems['keys']:
+ keySetChanges = self.changedItems['keys'][prevKeySetName]
+ for event in keySetChanges:
+ newKeys[event] = keySetChanges[event]
+ #save the new theme
+ self.SaveNewKeySet(newKeySetName, newKeys)
+ #change gui over to the new key set
+ customKeyList = idleConf.GetSectionList('user', 'keys')
+ customKeyList.sort()
+ self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName)
+ self.keysAreBuiltin.set(0)
+ self.SetKeysType()
+
+ def LoadKeysList(self, keySetName):
+ reselect = 0
+ newKeySet = 0
+ if self.listBindings.curselection():
+ reselect = 1
+ listIndex = self.listBindings.index(ANCHOR)
+ keySet = idleConf.GetKeySet(keySetName)
+ bindNames = keySet.keys()
+ bindNames.sort()
+ self.listBindings.delete(0, END)
+ for bindName in bindNames:
+ 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']:
+ #handle any unsaved changes to this key set
+ if bindName in self.changedItems['keys'][keySetName]:
+ key = self.changedItems['keys'][keySetName][bindName]
+ self.listBindings.insert(END, bindName+' - '+key)
+ if reselect:
+ self.listBindings.see(listIndex)
+ self.listBindings.select_set(listIndex)
+ self.listBindings.select_anchor(listIndex)
+
+ def DeleteCustomKeys(self):
+ keySetName=self.customKeys.get()
+ delmsg = 'Are you sure you wish to delete the key set %r ?'
+ if not tkMessageBox.askyesno(
+ 'Delete Key Set', delmsg % keySetName, parent=self):
+ return
+ self.DeactivateCurrentConfig()
+ #remove key set from config
+ idleConf.userCfg['keys'].remove_section(keySetName)
+ if keySetName in self.changedItems['keys']:
+ del(self.changedItems['keys'][keySetName])
+ #write changes
+ idleConf.userCfg['keys'].Save()
+ #reload user key set list
+ itemList = idleConf.GetSectionList('user', 'keys')
+ itemList.sort()
+ if not itemList:
+ self.radioKeysCustom.config(state=DISABLED)
+ 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.SaveAllChangedConfigs()
+ self.ActivateConfigChanges()
+ self.SetKeysType()
+
+ def DeleteCustomTheme(self):
+ themeName = self.customTheme.get()
+ delmsg = 'Are you sure you wish to delete the theme %r ?'
+ if not tkMessageBox.askyesno(
+ 'Delete Theme', delmsg % themeName, parent=self):
+ return
+ self.DeactivateCurrentConfig()
+ #remove theme from config
+ idleConf.userCfg['highlight'].remove_section(themeName)
+ if themeName in self.changedItems['highlight']:
+ del(self.changedItems['highlight'][themeName])
+ #write changes
+ idleConf.userCfg['highlight'].Save()
+ #reload user theme list
+ itemList = idleConf.GetSectionList('user', 'highlight')
+ itemList.sort()
+ if not itemList:
+ self.radioThemeCustom.config(state=DISABLED)
+ self.optMenuThemeCustom.SetMenu(itemList, '- no custom themes -')
+ else:
+ self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
+ #revert to default theme
+ self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
+ self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
+ #user can't back out of these changes, they must be applied now
+ self.SaveAllChangedConfigs()
+ self.ActivateConfigChanges()
+ self.SetThemeType()
+
+ def GetColour(self):
+ target = self.highlightTarget.get()
+ prevColour = self.frameColourSet.cget('bg')
+ rgbTuplet, colourString = tkColorChooser.askcolor(
+ parent=self, title='Pick new colour for : '+target,
+ initialcolor=prevColour)
+ if colourString and (colourString != prevColour):
+ #user didn't cancel, and they chose a new colour
+ if self.themeIsBuiltin.get(): #current theme is a built-in
+ message = ('Your changes will be saved as a new Custom Theme. '
+ 'Enter a name for your new Custom Theme below.')
+ newTheme = self.GetNewThemeName(message)
+ if not newTheme: #user cancelled custom theme creation
+ return
+ else: #create new custom theme based on previously active theme
+ self.CreateNewTheme(newTheme)
+ self.colour.set(colourString)
+ else: #current theme is user defined
+ self.colour.set(colourString)
+
+ def OnNewColourSet(self):
+ newColour=self.colour.get()
+ self.frameColourSet.config(bg=newColour) #set sample
+ plane ='foreground' if self.fgHilite.get() else 'background'
+ sampleElement = self.themeElements[self.highlightTarget.get()][0]
+ self.textHighlightSample.tag_config(sampleElement, **{plane:newColour})
+ theme = self.customTheme.get()
+ themeElement = sampleElement + '-' + plane
+ self.AddChangedItem('highlight', theme, themeElement, newColour)
+
+ def GetNewThemeName(self, message):
+ usedNames = (idleConf.GetSectionList('user', 'highlight') +
+ idleConf.GetSectionList('default', 'highlight'))
+ newTheme = GetCfgSectionNameDialog(
+ self, 'New Custom Theme', message, usedNames).result
+ return newTheme
+
+ def SaveAsNewTheme(self):
+ newThemeName = self.GetNewThemeName('New Theme Name:')
+ if newThemeName:
+ self.CreateNewTheme(newThemeName)
+
+ def CreateNewTheme(self, newThemeName):
+ #creates new custom theme based on the previously active theme,
+ #and makes the new theme active
+ if self.themeIsBuiltin.get():
+ themeType = 'default'
+ themeName = self.builtinTheme.get()
+ else:
+ themeType = 'user'
+ 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']:
+ themeChanges = self.changedItems['highlight'][themeName]
+ for element in themeChanges:
+ newTheme[element] = themeChanges[element]
+ #save the new theme
+ self.SaveNewTheme(newThemeName, newTheme)
+ #change gui over to the new theme
+ customThemeList = idleConf.GetSectionList('user', 'highlight')
+ customThemeList.sort()
+ self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName)
+ self.themeIsBuiltin.set(0)
+ self.SetThemeType()
+
+ def OnListFontButtonRelease(self, event):
+ font = self.listFontName.get(ANCHOR)
+ self.fontName.set(font.lower())
+ self.SetFontSample()
+
+ def SetFontSample(self, event=None):
+ fontName = self.fontName.get()
+ fontWeight = tkFont.BOLD if self.fontBold.get() else tkFont.NORMAL
+ newFont = (fontName, self.fontSize.get(), fontWeight)
+ self.labelFontSample.config(font=newFont)
+ self.textHighlightSample.configure(font=newFont)
+
+ def SetHighlightTarget(self):
+ if self.highlightTarget.get() == 'Cursor': #bg not possible
+ self.radioFg.config(state=DISABLED)
+ self.radioBg.config(state=DISABLED)
+ self.fgHilite.set(1)
+ else: #both fg and bg can be set
+ self.radioFg.config(state=NORMAL)
+ self.radioBg.config(state=NORMAL)
+ self.fgHilite.set(1)
+ self.SetColourSample()
+
+ def SetColourSampleBinding(self, *args):
+ self.SetColourSample()
+
+ def SetColourSample(self):
+ #set the colour smaple area
+ tag = self.themeElements[self.highlightTarget.get()][0]
+ plane = 'foreground' if self.fgHilite.get() else 'background'
+ colour = self.textHighlightSample.tag_cget(tag, plane)
+ self.frameColourSet.config(bg=colour)
+
+ def PaintThemeSample(self):
+ if self.themeIsBuiltin.get(): #a default theme
+ theme = self.builtinTheme.get()
+ else: #a user theme
+ theme = self.customTheme.get()
+ 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']:
+ themeDict = self.changedItems['highlight'][theme]
+ if element + '-foreground' in themeDict:
+ colours['foreground'] = themeDict[element + '-foreground']
+ if element + '-background' in themeDict:
+ colours['background'] = themeDict[element + '-background']
+ self.textHighlightSample.tag_config(element, **colours)
+ self.SetColourSample()
+
+ def HelpSourceSelected(self, event):
+ self.SetHelpListButtonStates()
+
+ def SetHelpListButtonStates(self):
+ if self.listHelp.size() < 1: #no entries in list
+ self.buttonHelpListEdit.config(state=DISABLED)
+ self.buttonHelpListRemove.config(state=DISABLED)
+ else: #there are some entries
+ if self.listHelp.curselection(): #there currently is a selection
+ self.buttonHelpListEdit.config(state=NORMAL)
+ self.buttonHelpListRemove.config(state=NORMAL)
+ else: #there currently is not a selection
+ self.buttonHelpListEdit.config(state=DISABLED)
+ self.buttonHelpListRemove.config(state=DISABLED)
+
+ def HelpListItemAdd(self):
+ helpSource = GetHelpSourceDialog(self, 'New Help Source').result
+ if helpSource:
+ self.userHelpList.append((helpSource[0], helpSource[1]))
+ self.listHelp.insert(END, helpSource[0])
+ self.UpdateUserHelpChangedItems()
+ self.SetHelpListButtonStates()
+
+ def HelpListItemEdit(self):
+ itemIndex = self.listHelp.index(ANCHOR)
+ helpSource = self.userHelpList[itemIndex]
+ newHelpSource = GetHelpSourceDialog(
+ self, 'Edit Help Source', menuItem=helpSource[0],
+ filePath=helpSource[1]).result
+ if (not newHelpSource) or (newHelpSource == helpSource):
+ return #no changes
+ self.userHelpList[itemIndex] = newHelpSource
+ self.listHelp.delete(itemIndex)
+ self.listHelp.insert(itemIndex, newHelpSource[0])
+ self.UpdateUserHelpChangedItems()
+ self.SetHelpListButtonStates()
+
+ def HelpListItemRemove(self):
+ itemIndex = self.listHelp.index(ANCHOR)
+ del(self.userHelpList[itemIndex])
+ self.listHelp.delete(itemIndex)
+ self.UpdateUserHelpChangedItems()
+ self.SetHelpListButtonStates()
+
+ def UpdateUserHelpChangedItems(self):
+ "Clear and rebuild the HelpFiles section in self.changedItems"
+ self.changedItems['main']['HelpFiles'] = {}
+ for num in range(1, len(self.userHelpList) + 1):
+ self.AddChangedItem(
+ 'main', 'HelpFiles', str(num),
+ ';'.join(self.userHelpList[num-1][:2]))
+
+ def LoadFontCfg(self):
+ ##base editor font selection list
+ fonts = list(tkFont.families(self))
+ fonts.sort()
+ for font in fonts:
+ self.listFontName.insert(END, font)
+ configuredFont = idleConf.GetFont(self, 'main', 'EditorWindow')
+ fontName = configuredFont[0].lower()
+ fontSize = configuredFont[1]
+ fontBold = configuredFont[2]=='bold'
+ self.fontName.set(fontName)
+ lc_fonts = [s.lower() for s in fonts]
+ try:
+ currentFontIndex = lc_fonts.index(fontName)
+ self.listFontName.see(currentFontIndex)
+ self.listFontName.select_set(currentFontIndex)
+ self.listFontName.select_anchor(currentFontIndex)
+ except ValueError:
+ pass
+ ##font size dropdown
+ self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13',
+ '14', '16', '18', '20', '22',
+ '25', '29', '34', '40'), fontSize )
+ ##fontWeight
+ self.fontBold.set(fontBold)
+ ##font sample
+ self.SetFontSample()
+
+ def LoadTabCfg(self):
+ ##indent sizes
+ spaceNum = idleConf.GetOption(
+ 'main', 'Indent', 'num-spaces', default=4, type='int')
+ self.spaceNum.set(spaceNum)
+
+ def LoadThemeCfg(self):
+ ##current theme type radiobutton
+ self.themeIsBuiltin.set(idleConf.GetOption(
+ 'main', 'Theme', 'default', type='bool', default=1))
+ ##currently set theme
+ currentOption = idleConf.CurrentTheme()
+ ##load available theme option menus
+ if self.themeIsBuiltin.get(): #default theme selected
+ itemList = idleConf.GetSectionList('default', 'highlight')
+ itemList.sort()
+ self.optMenuThemeBuiltin.SetMenu(itemList, currentOption)
+ itemList = idleConf.GetSectionList('user', 'highlight')
+ itemList.sort()
+ if not itemList:
+ self.radioThemeCustom.config(state=DISABLED)
+ self.customTheme.set('- no custom themes -')
+ else:
+ self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
+ else: #user theme selected
+ itemList = idleConf.GetSectionList('user', 'highlight')
+ itemList.sort()
+ self.optMenuThemeCustom.SetMenu(itemList, currentOption)
+ itemList = idleConf.GetSectionList('default', 'highlight')
+ itemList.sort()
+ self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0])
+ self.SetThemeType()
+ ##load theme element option menu
+ themeNames = self.themeElements.keys()
+ themeNames.sort(key=lambda x: self.themeElements[x][1])
+ self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0])
+ self.PaintThemeSample()
+ self.SetHighlightTarget()
+
+ def LoadKeyCfg(self):
+ ##current keys type radiobutton
+ self.keysAreBuiltin.set(idleConf.GetOption(
+ 'main', 'Keys', 'default', type='bool', default=1))
+ ##currently set keys
+ currentOption = idleConf.CurrentKeys()
+ ##load available keyset option menus
+ if self.keysAreBuiltin.get(): #default theme selected
+ itemList = idleConf.GetSectionList('default', 'keys')
+ itemList.sort()
+ self.optMenuKeysBuiltin.SetMenu(itemList, currentOption)
+ itemList = idleConf.GetSectionList('user', 'keys')
+ itemList.sort()
+ if not itemList:
+ self.radioKeysCustom.config(state=DISABLED)
+ self.customKeys.set('- no custom keys -')
+ else:
+ self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
+ else: #user key set selected
+ itemList = idleConf.GetSectionList('user', 'keys')
+ itemList.sort()
+ self.optMenuKeysCustom.SetMenu(itemList, currentOption)
+ itemList = idleConf.GetSectionList('default', 'keys')
+ itemList.sort()
+ self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0])
+ self.SetKeysType()
+ ##load keyset element list
+ keySetName = idleConf.CurrentKeys()
+ self.LoadKeysList(keySetName)
+
+ def LoadGeneralCfg(self):
+ #startup state
+ self.startupEdit.set(idleConf.GetOption(
+ 'main', 'General', 'editor-on-startup', default=1, type='bool'))
+ #autosave state
+ self.autoSave.set(idleConf.GetOption(
+ 'main', 'General', 'autosave', default=0, type='bool'))
+ #initial window size
+ self.winWidth.set(idleConf.GetOption(
+ 'main', 'EditorWindow', 'width', type='int'))
+ self.winHeight.set(idleConf.GetOption(
+ 'main', 'EditorWindow', 'height', type='int'))
+ # default source encoding
+ self.encoding.set(idleConf.GetOption(
+ 'main', 'EditorWindow', 'encoding', default='none'))
+ # additional help sources
+ self.userHelpList = idleConf.GetAllExtraHelpSourcesList()
+ for helpItem in self.userHelpList:
+ self.listHelp.insert(END, helpItem[0])
+ self.SetHelpListButtonStates()
+
+ def LoadConfigs(self):
+ """
+ load configuration from default and user config files and populate
+ the widgets on the config dialog pages.
+ """
+ ### fonts / tabs page
+ self.LoadFontCfg()
+ self.LoadTabCfg()
+ ### highlighting page
+ self.LoadThemeCfg()
+ ### keys page
+ self.LoadKeyCfg()
+ ### general page
+ self.LoadGeneralCfg()
+ # note: extension page handled separately
+
+ def SaveNewKeySet(self, keySetName, keySet):
+ """
+ save a newly created core key set.
+ keySetName - string, the name of the new key set
+ keySet - dictionary containing the new key set
+ """
+ if not idleConf.userCfg['keys'].has_section(keySetName):
+ idleConf.userCfg['keys'].add_section(keySetName)
+ for event in keySet:
+ value = keySet[event]
+ idleConf.userCfg['keys'].SetOption(keySetName, event, value)
+
+ def SaveNewTheme(self, themeName, theme):
+ """
+ save a newly created theme.
+ themeName - string, the name of the new theme
+ theme - dictionary containing the new theme
+ """
+ if not idleConf.userCfg['highlight'].has_section(themeName):
+ idleConf.userCfg['highlight'].add_section(themeName)
+ for element in theme:
+ value = theme[element]
+ idleConf.userCfg['highlight'].SetOption(themeName, element, value)
+
+ def SetUserValue(self, configType, section, item, value):
+ if idleConf.defaultCfg[configType].has_option(section, item):
+ if idleConf.defaultCfg[configType].Get(section, item) == value:
+ #the setting equals a default setting, remove it from user cfg
+ return idleConf.userCfg[configType].RemoveOption(section, item)
+ #if we got here set the option
+ return idleConf.userCfg[configType].SetOption(section, item, value)
+
+ def SaveAllChangedConfigs(self):
+ "Save configuration changes to the user config file."
+ idleConf.userCfg['main'].Save()
+ for configType in self.changedItems:
+ cfgTypeHasChanges = False
+ 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]:
+ value = self.changedItems[configType][section][item]
+ if self.SetUserValue(configType, section, item, value):
+ cfgTypeHasChanges = True
+ if cfgTypeHasChanges:
+ idleConf.userCfg[configType].Save()
+ for configType in ['keys', 'highlight']:
+ # save these even if unchanged!
+ idleConf.userCfg[configType].Save()
+ self.ResetChangedItems() #clear the changed items dict
+ self.save_all_changed_extensions() # uses a different mechanism
+
+ 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
+ for instance in winInstances:
+ instance.RemoveKeybindings()
+
+ def ActivateConfigChanges(self):
+ "Dynamically apply configuration changes"
+ winInstances = self.parent.instance_dict.keys()
+ for instance in winInstances:
+ instance.ResetColorizer()
+ instance.ResetFont()
+ instance.set_notabs_indentwidth()
+ instance.ApplyKeybindings()
+ instance.reset_help_menu_entries()
+
+ def Cancel(self):
+ self.destroy()
+
+ def Ok(self):
+ self.Apply()
+ self.destroy()
+
+ def Apply(self):
+ self.DeactivateCurrentConfig()
+ self.SaveAllChangedConfigs()
+ self.ActivateConfigChanges()
+
+ def Help(self):
+ page = self.tabPages._current_page
+ view_text(self, title='Help for IDLE preferences',
+ text=help_common+help_pages.get(page, ''))
+
+ def CreatePageExtensions(self):
+ """Part of the config dialog used for configuring IDLE extensions.
+
+ This code is generic - it works for any and all IDLE extensions.
+
+ IDLE extensions save their configuration options using idleConf.
+ This code reads the current configuration using idleConf, supplies a
+ GUI interface to change the configuration values, and saves the
+ changes using idleConf.
+
+ Not all changes take effect immediately - some may require restarting IDLE.
+ This depends on each extension's implementation.
+
+ All values are treated as text, and it is up to the user to supply
+ reasonable values. The only exception to this are the 'enable*' options,
+ which are boolean, and can be toggled with a True/False button.
+ """
+ parent = self.parent
+ frame = self.tabPages.pages['Extensions'].frame
+ self.ext_defaultCfg = idleConf.defaultCfg['extensions']
+ self.ext_userCfg = idleConf.userCfg['extensions']
+ self.is_int = self.register(is_int)
+ self.load_extensions()
+ # create widgets - a listbox shows all available extensions, with the
+ # controls for the extension selected in the listbox to the right
+ self.extension_names = StringVar(self)
+ frame.rowconfigure(0, weight=1)
+ frame.columnconfigure(2, weight=1)
+ self.extension_list = Listbox(frame, listvariable=self.extension_names,
+ selectmode='browse')
+ self.extension_list.bind('<<ListboxSelect>>', self.extension_selected)
+ scroll = Scrollbar(frame, command=self.extension_list.yview)
+ self.extension_list.yscrollcommand=scroll.set
+ self.details_frame = LabelFrame(frame, width=250, height=250)
+ self.extension_list.grid(column=0, row=0, sticky='nws')
+ scroll.grid(column=1, row=0, sticky='ns')
+ self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
+ frame.configure(padx=10, pady=10)
+ self.config_frame = {}
+ self.current_extension = None
+
+ self.outerframe = self # TEMPORARY
+ self.tabbed_page_set = self.extension_list # TEMPORARY
+
+ # create the frame holding controls for each extension
+ ext_names = ''
+ for ext_name in sorted(self.extensions):
+ self.create_extension_frame(ext_name)
+ ext_names = ext_names + '{' + ext_name + '} '
+ self.extension_names.set(ext_names)
+ self.extension_list.selection_set(0)
+ self.extension_selected(None)
+
+ def load_extensions(self):
+ "Fill self.extensions with data from the default and user configs."
+ self.extensions = {}
+ for ext_name in idleConf.GetExtensions(active_only=False):
+ self.extensions[ext_name] = []
+
+ for ext_name in self.extensions:
+ opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name))
+
+ # bring 'enable' options to the beginning of the list
+ enables = [opt_name for opt_name in opt_list
+ if opt_name.startswith('enable')]
+ for opt_name in enables:
+ opt_list.remove(opt_name)
+ opt_list = enables + opt_list
+
+ for opt_name in opt_list:
+ def_str = self.ext_defaultCfg.Get(
+ ext_name, opt_name, raw=True)
+ try:
+ def_obj = {'True':True, 'False':False}[def_str]
+ opt_type = 'bool'
+ except KeyError:
+ try:
+ def_obj = int(def_str)
+ opt_type = 'int'
+ except ValueError:
+ def_obj = def_str
+ opt_type = None
+ try:
+ value = self.ext_userCfg.Get(
+ ext_name, opt_name, type=opt_type, raw=True,
+ default=def_obj)
+ except ValueError: # Need this until .Get fixed
+ value = def_obj # bad values overwritten by entry
+ var = StringVar(self)
+ var.set(str(value))
+
+ self.extensions[ext_name].append({'name': opt_name,
+ 'type': opt_type,
+ 'default': def_str,
+ 'value': value,
+ 'var': var,
+ })
+
+ def extension_selected(self, event):
+ newsel = self.extension_list.curselection()
+ if newsel:
+ newsel = self.extension_list.get(newsel)
+ if newsel is None or newsel != self.current_extension:
+ if self.current_extension:
+ self.details_frame.config(text='')
+ self.config_frame[self.current_extension].grid_forget()
+ self.current_extension = None
+ if newsel:
+ self.details_frame.config(text=newsel)
+ self.config_frame[newsel].grid(column=0, row=0, sticky='nsew')
+ self.current_extension = newsel
+
+ def create_extension_frame(self, ext_name):
+ """Create a frame holding the widgets to configure one extension"""
+ f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
+ self.config_frame[ext_name] = f
+ entry_area = f.interior
+ # create an entry for each configuration option
+ for row, opt in enumerate(self.extensions[ext_name]):
+ # create a row with a label and entry/checkbutton
+ label = Label(entry_area, text=opt['name'])
+ label.grid(row=row, column=0, sticky=NW)
+ var = opt['var']
+ if opt['type'] == 'bool':
+ Checkbutton(entry_area, textvariable=var, variable=var,
+ onvalue='True', offvalue='False',
+ indicatoron=FALSE, selectcolor='', width=8
+ ).grid(row=row, column=1, sticky=W, padx=7)
+ elif opt['type'] == 'int':
+ Entry(entry_area, textvariable=var, validate='key',
+ validatecommand=(self.is_int, '%P')
+ ).grid(row=row, column=1, sticky=NSEW, padx=7)
+
+ else:
+ Entry(entry_area, textvariable=var
+ ).grid(row=row, column=1, sticky=NSEW, padx=7)
+ return
+
+ def set_extension_value(self, section, opt):
+ name = opt['name']
+ default = opt['default']
+ value = opt['var'].get().strip() or default
+ opt['var'].set(value)
+ # if self.defaultCfg.has_section(section):
+ # Currently, always true; if not, indent to return
+ if (value == default):
+ return self.ext_userCfg.RemoveOption(section, name)
+ # set the option
+ return self.ext_userCfg.SetOption(section, name, value)
+
+ def save_all_changed_extensions(self):
+ """Save configuration changes to the user config file."""
+ has_changes = False
+ for ext_name in self.extensions:
+ options = self.extensions[ext_name]
+ for opt in options:
+ if self.set_extension_value(ext_name, opt):
+ has_changes = True
+ if has_changes:
+ self.ext_userCfg.Save()
+
+
+help_common = '''\
+When you click either the Apply or Ok buttons, settings in this
+dialog that are different from IDLE's default are saved in
+a .idlerc directory in your home directory. Except as noted,
+these changes apply to all versions of IDLE installed on this
+machine. Some do not take affect until IDLE is restarted.
+[Cancel] only cancels changes made since the last save.
+'''
+help_pages = {
+ 'Highlighting':'''
+Highlighting:
+The IDLE Dark color theme is new in October 2015. It can only
+be used with older IDLE releases if it is saved as a custom
+theme, with a different name.
+'''
+}
+
+
+def is_int(s):
+ "Return 's is blank or represents an int'"
+ if not s:
+ return True
+ try:
+ int(s)
+ return True
+ except ValueError:
+ return False
+
+
+class VerticalScrolledFrame(Frame):
+ """A pure Tkinter vertically scrollable frame.
+
+ * Use the 'interior' attribute to place widgets inside the scrollable frame
+ * Construct and pack/place/grid normally
+ * This frame only allows vertical scrolling
+ """
+ def __init__(self, parent, *args, **kw):
+ Frame.__init__(self, parent, *args, **kw)
+
+ # create a canvas object and a vertical scrollbar for scrolling it
+ vscrollbar = Scrollbar(self, orient=VERTICAL)
+ vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
+ canvas = Canvas(self, bd=0, highlightthickness=0,
+ yscrollcommand=vscrollbar.set, width=240)
+ canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
+ vscrollbar.config(command=canvas.yview)
+
+ # reset the view
+ canvas.xview_moveto(0)
+ canvas.yview_moveto(0)
+
+ # create a frame inside the canvas which will be scrolled with it
+ self.interior = interior = Frame(canvas)
+ interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)
+
+ # track changes to the canvas and frame width and sync them,
+ # also updating the scrollbar
+ def _configure_interior(event):
+ # update the scrollbars to match the size of the inner frame
+ size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
+ canvas.config(scrollregion="0 0 %s %s" % size)
+ interior.bind('<Configure>', _configure_interior)
+
+ def _configure_canvas(event):
+ if interior.winfo_reqwidth() != canvas.winfo_width():
+ # update the inner frame's width to fill the canvas
+ canvas.itemconfigure(interior_id, width=canvas.winfo_width())
+ canvas.bind('<Configure>', _configure_canvas)
+
+ return
+
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main('idlelib.idle_test.test_configdialog',
+ verbosity=2, exit=False)
+ from idlelib.idle_test.htest import run
+ run(ConfigDialog)
diff --git a/lib/python2.7/idlelib/configHandler.py b/lib/python2.7/idlelib/configHandler.py
new file mode 100644
index 0000000..ca885ed
--- /dev/null
+++ b/lib/python2.7/idlelib/configHandler.py
@@ -0,0 +1,772 @@
+"""Provides access to stored IDLE configuration information.
+
+Refer to the comments at the beginning of config-main.def for a description of
+the available configuration files and the design implemented to update user
+configuration information. In particular, user configuration choices which
+duplicate the defaults will be removed from the user's configuration files,
+and if a file becomes empty, it will be deleted.
+
+The contents of the user files may be altered using the Options/Configure IDLE
+menu to access the configuration GUI (configDialog.py), or manually.
+
+Throughout this module there is an emphasis on returning useable defaults
+when a problem occurs in returning a requested configuration value back to
+idle. This is to allow IDLE to continue to function in spite of errors in
+the retrieval of config information. When a default is returned instead of
+a requested config value, a message is printed to stderr to aid in
+configuration problem notification and resolution.
+"""
+# TODOs added Oct 2014, tjr
+
+from __future__ import print_function
+import os
+import sys
+
+from ConfigParser import ConfigParser
+from Tkinter import TkVersion
+from tkFont import Font, nametofont
+
+class InvalidConfigType(Exception): pass
+class InvalidConfigSet(Exception): pass
+class InvalidFgBg(Exception): pass
+class InvalidTheme(Exception): pass
+
+class IdleConfParser(ConfigParser):
+ """
+ A ConfigParser specialised for idle configuration file handling
+ """
+ def __init__(self, cfgFile, cfgDefaults=None):
+ """
+ cfgFile - string, fully specified configuration file name
+ """
+ self.file = cfgFile
+ ConfigParser.__init__(self, defaults=cfgDefaults)
+
+ def Get(self, section, option, type=None, default=None, raw=False):
+ """
+ Get an option value for given section/option or return default.
+ If type is specified, return as type.
+ """
+ # TODO Use default as fallback, at least if not None
+ # Should also print Warning(file, section, option).
+ # Currently may raise ValueError
+ if not self.has_option(section, option):
+ return default
+ if type == 'bool':
+ return self.getboolean(section, option)
+ elif type == 'int':
+ return self.getint(section, option)
+ else:
+ return self.get(section, option, raw=raw)
+
+ def GetOptionList(self, section):
+ "Return a list of options for given section, else []."
+ if self.has_section(section):
+ return self.options(section)
+ else: #return a default value
+ return []
+
+ def Load(self):
+ "Load the configuration file from disk."
+ self.read(self.file)
+
+class IdleUserConfParser(IdleConfParser):
+ """
+ IdleConfigParser specialised for user configuration handling.
+ """
+
+ def AddSection(self, section):
+ "If section doesn't exist, add it."
+ if not self.has_section(section):
+ self.add_section(section)
+
+ def RemoveEmptySections(self):
+ "Remove any sections that have no options."
+ for section in self.sections():
+ if not self.GetOptionList(section):
+ self.remove_section(section)
+
+ def IsEmpty(self):
+ "Return True if no sections after removing empty sections."
+ self.RemoveEmptySections()
+ return not self.sections()
+
+ def RemoveOption(self, section, option):
+ """Return True if option is removed from section, else False.
+
+ False if either section does not exist or did not have option.
+ """
+ if self.has_section(section):
+ return self.remove_option(section, option)
+ return False
+
+ def SetOption(self, section, option, value):
+ """Return True if option is added or changed to value, else False.
+
+ Add section if required. False means option already had value.
+ """
+ if self.has_option(section, option):
+ if self.get(section, option) == value:
+ return False
+ else:
+ self.set(section, option, value)
+ return True
+ else:
+ if not self.has_section(section):
+ self.add_section(section)
+ self.set(section, option, value)
+ return True
+
+ def RemoveFile(self):
+ "Remove user config file self.file from disk if it exists."
+ if os.path.exists(self.file):
+ os.remove(self.file)
+
+ def Save(self):
+ """Update user configuration file.
+
+ Remove empty sections. If resulting config isn't empty, write the file
+ to disk. If config is empty, remove the file from disk if it exists.
+
+ """
+ if not self.IsEmpty():
+ fname = self.file
+ try:
+ cfgFile = open(fname, 'w')
+ except IOError:
+ os.unlink(fname)
+ cfgFile = open(fname, 'w')
+ with cfgFile:
+ self.write(cfgFile)
+ else:
+ self.RemoveFile()
+
+class IdleConf:
+ """Hold config parsers for all idle config files in singleton instance.
+
+ Default config files, self.defaultCfg --
+ for config_type in self.config_types:
+ (idle install dir)/config-{config-type}.def
+
+ User config files, self.userCfg --
+ for config_type in self.config_types:
+ (user home dir)/.idlerc/config-{config-type}.cfg
+ """
+ def __init__(self):
+ self.config_types = ('main', 'extensions', 'highlight', 'keys')
+ self.defaultCfg = {}
+ self.userCfg = {}
+ self.cfg = {} # TODO use to select userCfg vs defaultCfg
+ self.CreateConfigHandlers()
+ self.LoadCfgFiles()
+
+
+ def CreateConfigHandlers(self):
+ "Populate default and user config parser dictionaries."
+ #build idle install path
+ if __name__ != '__main__': # we were imported
+ idleDir=os.path.dirname(__file__)
+ else: # we were exec'ed (for testing only)
+ idleDir=os.path.abspath(sys.path[0])
+ userDir=self.GetUserCfgDir()
+
+ defCfgFiles = {}
+ usrCfgFiles = {}
+ # TODO eliminate these temporaries by combining loops
+ for cfgType in self.config_types: #build config file names
+ defCfgFiles[cfgType] = os.path.join(
+ idleDir, 'config-' + cfgType + '.def')
+ usrCfgFiles[cfgType] = os.path.join(
+ userDir, 'config-' + cfgType + '.cfg')
+ for cfgType in self.config_types: #create config parsers
+ self.defaultCfg[cfgType] = IdleConfParser(defCfgFiles[cfgType])
+ self.userCfg[cfgType] = IdleUserConfParser(usrCfgFiles[cfgType])
+
+ def GetUserCfgDir(self):
+ """Return a filesystem directory for storing user config files.
+
+ Creates it if required.
+ """
+ cfgDir = '.idlerc'
+ userDir = os.path.expanduser('~')
+ if userDir != '~': # expanduser() found user home dir
+ if not os.path.exists(userDir):
+ warn = ('\n Warning: os.path.expanduser("~") points to\n ' +
+ userDir + ',\n but the path does not exist.')
+ try:
+ print(warn, file=sys.stderr)
+ except IOError:
+ pass
+ userDir = '~'
+ if userDir == "~": # still no path to home!
+ # traditionally IDLE has defaulted to os.getcwd(), is this adequate?
+ userDir = os.getcwd()
+ userDir = os.path.join(userDir, cfgDir)
+ if not os.path.exists(userDir):
+ try:
+ os.mkdir(userDir)
+ except (OSError, IOError):
+ warn = ('\n Warning: unable to create user config directory\n' +
+ userDir + '\n Check path and permissions.\n Exiting!\n')
+ print(warn, file=sys.stderr)
+ raise SystemExit
+ # TODO continue without userDIr instead of exit
+ return userDir
+
+ def GetOption(self, configType, section, option, default=None, type=None,
+ warn_on_default=True, raw=False):
+ """Return a value for configType section option, or default.
+
+ If type is not None, return a value of that type. Also pass raw
+ to the config parser. First try to return a valid value
+ (including type) from a user configuration. If that fails, try
+ the default configuration. If that fails, return default, with a
+ default of None.
+
+ Warn if either user or default configurations have an invalid value.
+ Warn if default is returned and warn_on_default is True.
+ """
+ try:
+ if self.userCfg[configType].has_option(section, option):
+ return self.userCfg[configType].Get(section, option,
+ type=type, raw=raw)
+ except ValueError:
+ warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
+ ' invalid %r value for configuration option %r\n'
+ ' from section %r: %r' %
+ (type, option, section,
+ self.userCfg[configType].Get(section, option, raw=raw)))
+ try:
+ print(warning, file=sys.stderr)
+ except IOError:
+ pass
+ try:
+ if self.defaultCfg[configType].has_option(section,option):
+ return self.defaultCfg[configType].Get(
+ section, option, type=type, raw=raw)
+ except ValueError:
+ pass
+ #returning default, print warning
+ if warn_on_default:
+ warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
+ ' problem retrieving configuration option %r\n'
+ ' from section %r.\n'
+ ' returning default value: %r' %
+ (option, section, default))
+ try:
+ print(warning, file=sys.stderr)
+ except IOError:
+ pass
+ return default
+
+ def SetOption(self, configType, section, option, value):
+ """Set section option to value in user config file."""
+ self.userCfg[configType].SetOption(section, option, value)
+
+ def GetSectionList(self, configSet, configType):
+ """Return sections for configSet configType configuration.
+
+ configSet must be either 'user' or 'default'
+ configType must be in self.config_types.
+ """
+ if not (configType in self.config_types):
+ 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')
+ return cfgParser.sections()
+
+ def GetHighlight(self, theme, element, fgBg=None):
+ """Return individual theme element highlight color(s).
+
+ fgBg - string ('fg' or 'bg') or None.
+ If None, return a dictionary containing fg and bg colors with
+ keys 'foreground' and 'background'. Otherwise, only return
+ fg or bg color, as specified. Colors are intended to be
+ appropriate for passing to Tkinter in, e.g., a tag_config call).
+ """
+ if self.defaultCfg['highlight'].has_section(theme):
+ themeDict = self.GetThemeDict('default', theme)
+ else:
+ themeDict = self.GetThemeDict('user', theme)
+ fore = themeDict[element + '-foreground']
+ if element == 'cursor': # There is no config value for cursor bg
+ back = themeDict['normal-background']
+ else:
+ back = themeDict[element + '-background']
+ highlight = {"foreground": fore, "background": back}
+ if not fgBg: # Return dict of both colors
+ return highlight
+ else: # Return specified color only
+ if fgBg == 'fg':
+ return highlight["foreground"]
+ if fgBg == 'bg':
+ return highlight["background"]
+ else:
+ raise InvalidFgBg('Invalid fgBg specified')
+
+ def GetThemeDict(self, type, themeName):
+ """Return {option:value} dict for elements in themeName.
+
+ type - string, 'default' or 'user' theme type
+ themeName - string, theme name
+ Values are loaded over ultimate fallback defaults to guarantee
+ that all theme elements are present in a newly created theme.
+ """
+ if type == 'user':
+ cfgParser = self.userCfg['highlight']
+ elif type == 'default':
+ cfgParser = self.defaultCfg['highlight']
+ else:
+ raise InvalidTheme('Invalid theme type specified')
+ # Provide foreground and background colors for each theme
+ # element (other than cursor) even though some values are not
+ # yet used by idle, to allow for their use in the future.
+ # Default values are generally black and white.
+ # TODO copy theme from a class attribute.
+ theme ={'normal-foreground':'#000000',
+ 'normal-background':'#ffffff',
+ 'keyword-foreground':'#000000',
+ 'keyword-background':'#ffffff',
+ 'builtin-foreground':'#000000',
+ 'builtin-background':'#ffffff',
+ 'comment-foreground':'#000000',
+ 'comment-background':'#ffffff',
+ 'string-foreground':'#000000',
+ 'string-background':'#ffffff',
+ 'definition-foreground':'#000000',
+ 'definition-background':'#ffffff',
+ 'hilite-foreground':'#000000',
+ 'hilite-background':'gray',
+ 'break-foreground':'#ffffff',
+ 'break-background':'#000000',
+ 'hit-foreground':'#ffffff',
+ 'hit-background':'#000000',
+ 'error-foreground':'#ffffff',
+ 'error-background':'#000000',
+ #cursor (only foreground can be set)
+ 'cursor-foreground':'#000000',
+ #shell window
+ 'stdout-foreground':'#000000',
+ 'stdout-background':'#ffffff',
+ 'stderr-foreground':'#000000',
+ 'stderr-background':'#ffffff',
+ 'console-foreground':'#000000',
+ 'console-background':'#ffffff' }
+ for element in theme:
+ if not cfgParser.has_option(themeName, element):
+ # Print warning that will return a default color
+ warning = ('\n Warning: configHandler.IdleConf.GetThemeDict'
+ ' -\n problem retrieving theme element %r'
+ '\n from theme %r.\n'
+ ' returning default color: %r' %
+ (element, themeName, theme[element]))
+ try:
+ print(warning, file=sys.stderr)
+ except IOError:
+ pass
+ theme[element] = cfgParser.Get(
+ themeName, element, default=theme[element])
+ return theme
+
+ def CurrentTheme(self):
+ """Return the name of the currently active text color theme.
+
+ idlelib.config-main.def includes this section
+ [Theme]
+ default= 1
+ name= IDLE Classic
+ name2=
+ # name2 set in user config-main.cfg for themes added after 2015 Oct 1
+
+ Item name2 is needed because setting name to a new builtin
+ causes older IDLEs to display multiple error messages or quit.
+ See https://bugs.python.org/issue25313.
+ When default = True, name2 takes precedence over name,
+ while older IDLEs will just use name.
+ """
+ default = self.GetOption('main', 'Theme', 'default',
+ type='bool', default=True)
+ if default:
+ theme = self.GetOption('main', 'Theme', 'name2', default='')
+ if default and not theme or not default:
+ theme = self.GetOption('main', 'Theme', 'name', default='')
+ source = self.defaultCfg if default else self.userCfg
+ if source['highlight'].has_section(theme):
+ return theme
+ else:
+ return "IDLE Classic"
+
+ def CurrentKeys(self):
+ "Return the name of the currently active key set."
+ return self.GetOption('main', 'Keys', 'name', default='')
+
+ def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
+ """Return extensions in default and user config-extensions files.
+
+ If active_only True, only return active (enabled) extensions
+ and optionally only editor or shell extensions.
+ If active_only False, return all extensions.
+ """
+ extns = self.RemoveKeyBindNames(
+ self.GetSectionList('default', 'extensions'))
+ userExtns = self.RemoveKeyBindNames(
+ self.GetSectionList('user', 'extensions'))
+ for extn in userExtns:
+ if extn not in extns: #user has added own extension
+ extns.append(extn)
+ if active_only:
+ activeExtns = []
+ for extn in extns:
+ if self.GetOption('extensions', extn, 'enable', default=True,
+ type='bool'):
+ #the extension is enabled
+ if editor_only or shell_only: # TODO if both, contradictory
+ if editor_only:
+ option = "enable_editor"
+ else:
+ option = "enable_shell"
+ if self.GetOption('extensions', extn,option,
+ default=True, type='bool',
+ warn_on_default=False):
+ activeExtns.append(extn)
+ else:
+ activeExtns.append(extn)
+ return activeExtns
+ else:
+ return extns
+
+ def RemoveKeyBindNames(self, extnNameList):
+ "Return extnNameList with keybinding section names removed."
+ # TODO Easier to return filtered copy with list comp
+ names = extnNameList
+ kbNameIndicies = []
+ for name in names:
+ if name.endswith(('_bindings', '_cfgBindings')):
+ kbNameIndicies.append(names.index(name))
+ kbNameIndicies.sort(reverse=True)
+ for index in kbNameIndicies: #delete each keybinding section name
+ del(names[index])
+ return names
+
+ def GetExtnNameForEvent(self, virtualEvent):
+ """Return the name of the extension binding virtualEvent, or None.
+
+ virtualEvent - string, name of the virtual event to test for,
+ without the enclosing '<< >>'
+ """
+ extName = None
+ vEvent = '<<' + virtualEvent + '>>'
+ for extn in self.GetExtensions(active_only=0):
+ for event in self.GetExtensionKeys(extn):
+ if event == vEvent:
+ extName = extn # TODO return here?
+ return extName
+
+ def GetExtensionKeys(self, extensionName):
+ """Return dict: {configurable extensionName event : active keybinding}.
+
+ Events come from default config extension_cfgBindings section.
+ Keybindings come from GetCurrentKeySet() active key dict,
+ where previously used bindings are disabled.
+ """
+ keysName = extensionName + '_cfgBindings'
+ activeKeys = self.GetCurrentKeySet()
+ extKeys = {}
+ if self.defaultCfg['extensions'].has_section(keysName):
+ eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
+ for eventName in eventNames:
+ event = '<<' + eventName + '>>'
+ binding = activeKeys[event]
+ extKeys[event] = binding
+ return extKeys
+
+ def __GetRawExtensionKeys(self,extensionName):
+ """Return dict {configurable extensionName event : keybinding list}.
+
+ Events come from default config extension_cfgBindings section.
+ Keybindings list come from the splitting of GetOption, which
+ tries user config before default config.
+ """
+ keysName = extensionName+'_cfgBindings'
+ extKeys = {}
+ if self.defaultCfg['extensions'].has_section(keysName):
+ eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
+ for eventName in eventNames:
+ binding = self.GetOption(
+ 'extensions', keysName, eventName, default='').split()
+ event = '<<' + eventName + '>>'
+ extKeys[event] = binding
+ return extKeys
+
+ def GetExtensionBindings(self, extensionName):
+ """Return dict {extensionName event : active or defined keybinding}.
+
+ Augment self.GetExtensionKeys(extensionName) with mapping of non-
+ configurable events (from default config) to GetOption splits,
+ as in self.__GetRawExtensionKeys.
+ """
+ bindsName = extensionName + '_bindings'
+ extBinds = self.GetExtensionKeys(extensionName)
+ #add the non-configurable bindings
+ if self.defaultCfg['extensions'].has_section(bindsName):
+ eventNames = self.defaultCfg['extensions'].GetOptionList(bindsName)
+ for eventName in eventNames:
+ binding = self.GetOption(
+ 'extensions', bindsName, eventName, default='').split()
+ event = '<<' + eventName + '>>'
+ extBinds[event] = binding
+
+ return extBinds
+
+ def GetKeyBinding(self, keySetName, eventStr):
+ """Return the keybinding list for keySetName eventStr.
+
+ keySetName - name of key binding set (config-keys section).
+ eventStr - virtual event, including brackets, as in '<<event>>'.
+ """
+ eventName = eventStr[2:-2] #trim off the angle brackets
+ binding = self.GetOption('keys', keySetName, eventName, default='').split()
+ return binding
+
+ def GetCurrentKeySet(self):
+ "Return CurrentKeys with 'darwin' modifications."
+ result = self.GetKeySet(self.CurrentKeys())
+
+ if sys.platform == "darwin":
+ # OS X Tk variants do not support the "Alt" keyboard modifier.
+ # So replace all keybingings that use "Alt" with ones that
+ # use the "Option" keyboard modifier.
+ # TODO (Ned?): the "Option" modifier does not work properly for
+ # Cocoa Tk and XQuartz Tk so we should not use it
+ # in default OS X KeySets.
+ for k, v in result.items():
+ v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
+ if v != v2:
+ result[k] = v2
+
+ return result
+
+ def GetKeySet(self, keySetName):
+ """Return event-key dict for keySetName core plus active extensions.
+
+ If a binding defined in an extension is already in use, the
+ extension binding is disabled by being set to ''
+ """
+ keySet = self.GetCoreKeys(keySetName)
+ activeExtns = self.GetExtensions(active_only=1)
+ for extn in activeExtns:
+ extKeys = self.__GetRawExtensionKeys(extn)
+ if extKeys: #the extension defines keybindings
+ for event in extKeys:
+ if extKeys[event] in keySet.values():
+ #the binding is already in use
+ extKeys[event] = '' #disable this binding
+ keySet[event] = extKeys[event] #add binding
+ return keySet
+
+ def IsCoreBinding(self, virtualEvent):
+ """Return True if the virtual event is one of the core idle key events.
+
+ virtualEvent - string, name of the virtual event to test for,
+ without the enclosing '<< >>'
+ """
+ return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
+
+# TODO make keyBindins a file or class attribute used for test above
+# and copied in function below
+
+ def GetCoreKeys(self, keySetName=None):
+ """Return dict of core virtual-key keybindings for keySetName.
+
+ The default keySetName None corresponds to the keyBindings base
+ dict. If keySetName is not None, bindings from the config
+ file(s) are loaded _over_ these defaults, so if there is a
+ problem getting any core binding there will be an 'ultimate last
+ resort fallback' to the CUA-ish bindings defined here.
+ """
+ keyBindings={
+ '<<copy>>': ['<Control-c>', '<Control-C>'],
+ '<<cut>>': ['<Control-x>', '<Control-X>'],
+ '<<paste>>': ['<Control-v>', '<Control-V>'],
+ '<<beginning-of-line>>': ['<Control-a>', '<Home>'],
+ '<<center-insert>>': ['<Control-l>'],
+ '<<close-all-windows>>': ['<Control-q>'],
+ '<<close-window>>': ['<Alt-F4>'],
+ '<<do-nothing>>': ['<Control-x>'],
+ '<<end-of-file>>': ['<Control-d>'],
+ '<<python-docs>>': ['<F1>'],
+ '<<python-context-help>>': ['<Shift-F1>'],
+ '<<history-next>>': ['<Alt-n>'],
+ '<<history-previous>>': ['<Alt-p>'],
+ '<<interrupt-execution>>': ['<Control-c>'],
+ '<<view-restart>>': ['<F6>'],
+ '<<restart-shell>>': ['<Control-F6>'],
+ '<<open-class-browser>>': ['<Alt-c>'],
+ '<<open-module>>': ['<Alt-m>'],
+ '<<open-new-window>>': ['<Control-n>'],
+ '<<open-window-from-file>>': ['<Control-o>'],
+ '<<plain-newline-and-indent>>': ['<Control-j>'],
+ '<<print-window>>': ['<Control-p>'],
+ '<<redo>>': ['<Control-y>'],
+ '<<remove-selection>>': ['<Escape>'],
+ '<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
+ '<<save-window-as-file>>': ['<Alt-s>'],
+ '<<save-window>>': ['<Control-s>'],
+ '<<select-all>>': ['<Alt-a>'],
+ '<<toggle-auto-coloring>>': ['<Control-slash>'],
+ '<<undo>>': ['<Control-z>'],
+ '<<find-again>>': ['<Control-g>', '<F3>'],
+ '<<find-in-files>>': ['<Alt-F3>'],
+ '<<find-selection>>': ['<Control-F3>'],
+ '<<find>>': ['<Control-f>'],
+ '<<replace>>': ['<Control-h>'],
+ '<<goto-line>>': ['<Alt-g>'],
+ '<<smart-backspace>>': ['<Key-BackSpace>'],
+ '<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
+ '<<smart-indent>>': ['<Key-Tab>'],
+ '<<indent-region>>': ['<Control-Key-bracketright>'],
+ '<<dedent-region>>': ['<Control-Key-bracketleft>'],
+ '<<comment-region>>': ['<Alt-Key-3>'],
+ '<<uncomment-region>>': ['<Alt-Key-4>'],
+ '<<tabify-region>>': ['<Alt-Key-5>'],
+ '<<untabify-region>>': ['<Alt-Key-6>'],
+ '<<toggle-tabs>>': ['<Alt-Key-t>'],
+ '<<change-indentwidth>>': ['<Alt-Key-u>'],
+ '<<del-word-left>>': ['<Control-Key-BackSpace>'],
+ '<<del-word-right>>': ['<Control-Key-Delete>']
+ }
+ if keySetName:
+ for event in keyBindings:
+ binding = self.GetKeyBinding(keySetName, event)
+ if binding:
+ keyBindings[event] = binding
+ else: #we are going to return a default, print warning
+ warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
+ ' -\n problem retrieving key binding for event %r'
+ '\n from key set %r.\n'
+ ' returning default value: %r' %
+ (event, keySetName, keyBindings[event]))
+ try:
+ print(warning, file=sys.stderr)
+ except IOError:
+ pass
+ return keyBindings
+
+ def GetExtraHelpSourceList(self, configSet):
+ """Return list of extra help sources from a given configSet.
+
+ Valid configSets are 'user' or 'default'. Return a list of tuples of
+ the form (menu_item , path_to_help_file , option), or return the empty
+ list. 'option' is the sequence number of the help resource. 'option'
+ values determine the position of the menu items on the Help menu,
+ therefore the returned list must be sorted by 'option'.
+
+ """
+ helpSources = []
+ if configSet == 'user':
+ cfgParser = self.userCfg['main']
+ elif configSet == 'default':
+ cfgParser = self.defaultCfg['main']
+ else:
+ raise InvalidConfigSet('Invalid configSet specified')
+ options=cfgParser.GetOptionList('HelpFiles')
+ for option in options:
+ value=cfgParser.Get('HelpFiles', option, default=';')
+ if value.find(';') == -1: #malformed config entry with no ';'
+ menuItem = '' #make these empty
+ helpPath = '' #so value won't be added to list
+ else: #config entry contains ';' as expected
+ 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]))
+ return helpSources
+
+ def GetAllExtraHelpSourcesList(self):
+ """Return a list of the details of all additional help sources.
+
+ Tuples in the list are those of GetExtraHelpSourceList.
+ """
+ allHelpSources = (self.GetExtraHelpSourceList('default') +
+ self.GetExtraHelpSourceList('user') )
+ return allHelpSources
+
+ def GetFont(self, root, configType, section):
+ """Retrieve a font from configuration (font, font-size, font-bold)
+ Intercept the special value 'TkFixedFont' and substitute
+ the actual font, factoring in some tweaks if needed for
+ appearance sakes.
+
+ The 'root' parameter can normally be any valid Tkinter widget.
+
+ Return a tuple (family, size, weight) suitable for passing
+ to tkinter.Font
+ """
+ family = self.GetOption(configType, section, 'font', default='courier')
+ size = self.GetOption(configType, section, 'font-size', type='int',
+ default='10')
+ bold = self.GetOption(configType, section, 'font-bold', default=0,
+ type='bool')
+ if (family == 'TkFixedFont'):
+ if TkVersion < 8.5:
+ family = 'Courier'
+ else:
+ f = Font(name='TkFixedFont', exists=True, root=root)
+ actualFont = Font.actual(f)
+ family = actualFont['family']
+ size = actualFont['size']
+ if size <= 0:
+ size = 10 # if font in pixels, ignore actual size
+ bold = actualFont['weight']=='bold'
+ return (family, size, 'bold' if bold else 'normal')
+
+ def LoadCfgFiles(self):
+ "Load all configuration files."
+ for key in self.defaultCfg:
+ self.defaultCfg[key].Load()
+ self.userCfg[key].Load() #same keys
+
+ def SaveUserCfgFiles(self):
+ "Write all loaded user configuration files to disk."
+ for key in self.userCfg:
+ self.userCfg[key].Save()
+
+
+idleConf = IdleConf()
+
+# TODO Revise test output, write expanded unittest
+#
+if __name__ == '__main__':
+ from zlib import crc32
+ line, crc = 0, 0
+
+ def sprint(obj):
+ global line, crc
+ txt = str(obj)
+ line += 1
+ crc = crc32(txt.encode(encoding='utf-8'), crc)
+ print(txt)
+ #print('***', line, crc, '***') # uncomment for diagnosis
+
+ def dumpCfg(cfg):
+ print('\n', cfg, '\n') # has variable '0xnnnnnnnn' addresses
+ for key in sorted(cfg.keys()):
+ sections = cfg[key].sections()
+ sprint(key)
+ sprint(sections)
+ for section in sections:
+ options = cfg[key].options(section)
+ sprint(section)
+ sprint(options)
+ for option in options:
+ sprint(option + ' = ' + cfg[key].Get(section, option))
+
+ dumpCfg(idleConf.defaultCfg)
+ dumpCfg(idleConf.userCfg)
+ print('\nlines = ', line, ', crc = ', crc, sep='')
diff --git a/lib/python2.7/idlelib/configHelpSourceEdit.py b/lib/python2.7/idlelib/configHelpSourceEdit.py
new file mode 100644
index 0000000..5816449
--- /dev/null
+++ b/lib/python2.7/idlelib/configHelpSourceEdit.py
@@ -0,0 +1,166 @@
+"Dialog to specify or edit the parameters for a user configured help source."
+
+import os
+import sys
+
+from Tkinter import *
+import tkMessageBox
+import tkFileDialog
+
+class GetHelpSourceDialog(Toplevel):
+ def __init__(self, parent, title, menuItem='', filePath='', _htest=False):
+ """Get menu entry and url/ local file location for Additional Help
+
+ User selects a name for the Help resource and provides a web url
+ or a local file as its source. The user can enter a url or browse
+ for the file.
+
+ _htest - bool, change box location when running htest
+ """
+ Toplevel.__init__(self, parent)
+ self.configure(borderwidth=5)
+ self.resizable(height=FALSE, width=FALSE)
+ self.title(title)
+ self.transient(parent)
+ self.grab_set()
+ self.protocol("WM_DELETE_WINDOW", self.Cancel)
+ self.parent = parent
+ self.result = None
+ self.CreateWidgets()
+ self.menu.set(menuItem)
+ self.path.set(filePath)
+ self.withdraw() #hide while setting geometry
+ #needs to be done here so that the winfo_reqwidth is valid
+ self.update_idletasks()
+ #centre dialog over parent. below parent if running htest.
+ self.geometry(
+ "+%d+%d" % (
+ parent.winfo_rootx() +
+ (parent.winfo_width()/2 - self.winfo_reqwidth()/2),
+ parent.winfo_rooty() +
+ ((parent.winfo_height()/2 - self.winfo_reqheight()/2)
+ if not _htest else 150)))
+ self.deiconify() #geometry set, unhide
+ self.bind('<Return>', self.Ok)
+ self.wait_window()
+
+ def CreateWidgets(self):
+ self.menu = StringVar(self)
+ self.path = StringVar(self)
+ self.fontSize = StringVar(self)
+ self.frameMain = Frame(self, borderwidth=2, relief=GROOVE)
+ self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH)
+ labelMenu = Label(self.frameMain, anchor=W, justify=LEFT,
+ text='Menu Item:')
+ self.entryMenu = Entry(self.frameMain, textvariable=self.menu,
+ width=30)
+ self.entryMenu.focus_set()
+ labelPath = Label(self.frameMain, anchor=W, justify=LEFT,
+ text='Help File Path: Enter URL or browse for file')
+ self.entryPath = Entry(self.frameMain, textvariable=self.path,
+ width=40)
+ self.entryMenu.focus_set()
+ labelMenu.pack(anchor=W, padx=5, pady=3)
+ self.entryMenu.pack(anchor=W, padx=5, pady=3)
+ labelPath.pack(anchor=W, padx=5, pady=3)
+ self.entryPath.pack(anchor=W, padx=5, pady=3)
+ browseButton = Button(self.frameMain, text='Browse', width=8,
+ command=self.browseFile)
+ browseButton.pack(pady=3)
+ frameButtons = Frame(self)
+ frameButtons.pack(side=BOTTOM, fill=X)
+ self.buttonOk = Button(frameButtons, text='OK',
+ width=8, default=ACTIVE, command=self.Ok)
+ self.buttonOk.grid(row=0, column=0, padx=5,pady=5)
+ self.buttonCancel = Button(frameButtons, text='Cancel',
+ width=8, command=self.Cancel)
+ self.buttonCancel.grid(row=0, column=1, padx=5, pady=5)
+
+ def browseFile(self):
+ filetypes = [
+ ("HTML Files", "*.htm *.html", "TEXT"),
+ ("PDF Files", "*.pdf", "TEXT"),
+ ("Windows Help Files", "*.chm"),
+ ("Text Files", "*.txt", "TEXT"),
+ ("All Files", "*")]
+ path = self.path.get()
+ if path:
+ dir, base = os.path.split(path)
+ else:
+ base = None
+ if sys.platform[:3] == 'win':
+ dir = os.path.join(os.path.dirname(sys.executable), 'Doc')
+ if not os.path.isdir(dir):
+ dir = os.getcwd()
+ else:
+ dir = os.getcwd()
+ opendialog = tkFileDialog.Open(parent=self, filetypes=filetypes)
+ file = opendialog.show(initialdir=dir, initialfile=base)
+ if file:
+ self.path.set(file)
+
+ def MenuOk(self):
+ "Simple validity check for a sensible menu item name"
+ menuOk = True
+ menu = self.menu.get()
+ menu.strip()
+ if not menu:
+ tkMessageBox.showerror(title='Menu Item Error',
+ message='No menu item specified',
+ parent=self)
+ self.entryMenu.focus_set()
+ menuOk = False
+ elif len(menu) > 30:
+ tkMessageBox.showerror(title='Menu Item Error',
+ message='Menu item too long:'
+ '\nLimit 30 characters.',
+ parent=self)
+ self.entryMenu.focus_set()
+ menuOk = False
+ return menuOk
+
+ def PathOk(self):
+ "Simple validity check for menu file path"
+ pathOk = True
+ path = self.path.get()
+ path.strip()
+ if not path: #no path specified
+ tkMessageBox.showerror(title='File Path Error',
+ message='No help file path specified.',
+ parent=self)
+ self.entryPath.focus_set()
+ pathOk = False
+ elif path.startswith(('www.', 'http')):
+ pass
+ else:
+ if path[:5] == 'file:':
+ path = path[5:]
+ if not os.path.exists(path):
+ tkMessageBox.showerror(title='File Path Error',
+ message='Help file path does not exist.',
+ parent=self)
+ self.entryPath.focus_set()
+ pathOk = False
+ return pathOk
+
+ def Ok(self, event=None):
+ if self.MenuOk() and self.PathOk():
+ self.result = (self.menu.get().strip(),
+ self.path.get().strip())
+ if sys.platform == 'darwin':
+ path = self.result[1]
+ if path.startswith(('www', 'file:', 'http:')):
+ pass
+ else:
+ # Mac Safari insists on using the URI form for local files
+ self.result = list(self.result)
+ self.result[1] = "file://" + path
+ self.destroy()
+
+ def Cancel(self, event=None):
+ self.result = None
+ self.destroy()
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(GetHelpSourceDialog)
diff --git a/lib/python2.7/idlelib/configSectionNameDialog.py b/lib/python2.7/idlelib/configSectionNameDialog.py
new file mode 100644
index 0000000..c09dca8
--- /dev/null
+++ b/lib/python2.7/idlelib/configSectionNameDialog.py
@@ -0,0 +1,92 @@
+"""
+Dialog that allows user to specify a new config file section name.
+Used to get new highlight theme and keybinding set names.
+The 'return value' for the dialog, used two placed in configDialog.py,
+is the .result attribute set in the Ok and Cancel methods.
+"""
+from Tkinter import *
+import tkMessageBox
+class GetCfgSectionNameDialog(Toplevel):
+ def __init__(self, parent, title, message, used_names, _htest=False):
+ """
+ message - string, informational message to display
+ used_names - string collection, names already in use for validity check
+ _htest - bool, change box location when running htest
+ """
+ Toplevel.__init__(self, parent)
+ self.configure(borderwidth=5)
+ self.resizable(height=FALSE, width=FALSE)
+ self.title(title)
+ self.transient(parent)
+ self.grab_set()
+ self.protocol("WM_DELETE_WINDOW", self.Cancel)
+ self.parent = parent
+ self.message = message
+ self.used_names = used_names
+ self.create_widgets()
+ self.withdraw() #hide while setting geometry
+ self.update_idletasks()
+ #needs to be done here so that the winfo_reqwidth is valid
+ self.messageInfo.config(width=self.frameMain.winfo_reqwidth())
+ self.geometry(
+ "+%d+%d" % (
+ parent.winfo_rootx() +
+ (parent.winfo_width()/2 - self.winfo_reqwidth()/2),
+ parent.winfo_rooty() +
+ ((parent.winfo_height()/2 - self.winfo_reqheight()/2)
+ if not _htest else 100)
+ ) ) #centre dialog over parent (or below htest box)
+ self.deiconify() #geometry set, unhide
+ self.wait_window()
+ def create_widgets(self):
+ self.name = StringVar(self.parent)
+ self.fontSize = StringVar(self.parent)
+ self.frameMain = Frame(self, borderwidth=2, relief=SUNKEN)
+ self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH)
+ self.messageInfo = Message(self.frameMain, anchor=W, justify=LEFT,
+ padx=5, pady=5, text=self.message) #,aspect=200)
+ entryName = Entry(self.frameMain, textvariable=self.name, width=30)
+ entryName.focus_set()
+ self.messageInfo.pack(padx=5, pady=5) #, expand=TRUE, fill=BOTH)
+ entryName.pack(padx=5, pady=5)
+ frameButtons = Frame(self, pady=2)
+ frameButtons.pack(side=BOTTOM)
+ self.buttonOk = Button(frameButtons, text='Ok',
+ width=8, command=self.Ok)
+ self.buttonOk.pack(side=LEFT, padx=5)
+ self.buttonCancel = Button(frameButtons, text='Cancel',
+ width=8, command=self.Cancel)
+ self.buttonCancel.pack(side=RIGHT, padx=5)
+
+ def name_ok(self):
+ ''' After stripping entered name, check that it is a sensible
+ ConfigParser file section name. Return it if it is, '' if not.
+ '''
+ name = self.name.get().strip()
+ if not name: #no name specified
+ tkMessageBox.showerror(title='Name Error',
+ message='No name specified.', parent=self)
+ elif len(name)>30: #name too long
+ tkMessageBox.showerror(title='Name Error',
+ message='Name too long. It should be no more than '+
+ '30 characters.', parent=self)
+ name = ''
+ elif name in self.used_names:
+ tkMessageBox.showerror(title='Name Error',
+ message='This name is already in use.', parent=self)
+ name = ''
+ return name
+ def Ok(self, event=None):
+ name = self.name_ok()
+ if name:
+ self.result = name
+ self.destroy()
+ def Cancel(self, event=None):
+ self.result = ''
+ self.destroy()
+if __name__ == '__main__':
+ import unittest
+ unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False)
+
+ from idlelib.idle_test.htest import run
+ run(GetCfgSectionNameDialog)
diff --git a/lib/python2.7/idlelib/dynOptionMenuWidget.py b/lib/python2.7/idlelib/dynOptionMenuWidget.py
new file mode 100644
index 0000000..beca9e2
--- /dev/null
+++ b/lib/python2.7/idlelib/dynOptionMenuWidget.py
@@ -0,0 +1,57 @@
+"""
+OptionMenu widget modified to allow dynamic menu reconfiguration
+and setting of highlightthickness
+"""
+import copy
+from Tkinter import OptionMenu, _setit, StringVar, Button
+
+class DynOptionMenu(OptionMenu):
+ """
+ unlike OptionMenu, our kwargs can include highlightthickness
+ """
+ def __init__(self, master, variable, value, *values, **kwargs):
+ # TODO copy value instead of whole dict
+ kwargsCopy=copy.copy(kwargs)
+ if 'highlightthickness' in kwargs.keys():
+ del(kwargs['highlightthickness'])
+ OptionMenu.__init__(self, master, variable, value, *values, **kwargs)
+ self.config(highlightthickness=kwargsCopy.get('highlightthickness'))
+ #self.menu=self['menu']
+ self.variable=variable
+ self.command=kwargs.get('command')
+
+ def SetMenu(self,valueList,value=None):
+ """
+ clear and reload the menu with a new set of options.
+ valueList - list of new options
+ value - initial value to set the optionmenu's menubutton to
+ """
+ self['menu'].delete(0,'end')
+ for item in valueList:
+ self['menu'].add_command(label=item,
+ command=_setit(self.variable,item,self.command))
+ if value:
+ self.variable.set(value)
+
+def _dyn_option_menu(parent): # htest #
+ from Tkinter import Toplevel
+
+ top = Toplevel()
+ top.title("Tets dynamic option menu")
+ top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200,
+ parent.winfo_rooty() + 150))
+ top.focus_set()
+
+ var = StringVar(top)
+ var.set("Old option set") #Set the default value
+ dyn = DynOptionMenu(top,var, "old1","old2","old3","old4")
+ dyn.pack()
+
+ def update():
+ dyn.SetMenu(["new1","new2","new3","new4"], value="new option set")
+ button = Button(top, text="Change option set", command=update)
+ button.pack()
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_dyn_option_menu)
diff --git a/lib/python2.7/idlelib/extend.txt b/lib/python2.7/idlelib/extend.txt
new file mode 100644
index 0000000..c9cb2e8
--- /dev/null
+++ b/lib/python2.7/idlelib/extend.txt
@@ -0,0 +1,83 @@
+Writing an IDLE extension
+=========================
+
+An IDLE extension can define new key bindings and menu entries for IDLE
+edit windows. There is a simple mechanism to load extensions when IDLE
+starts up and to attach them to each edit window. (It is also possible
+to make other changes to IDLE, but this must be done by editing the IDLE
+source code.)
+
+The list of extensions loaded at startup time is configured by editing
+the file config-extensions.def. See below for details.
+
+An IDLE extension is defined by a class. Methods of the class define
+actions that are invoked by event bindings or menu entries. Class (or
+instance) variables define the bindings and menu additions; these are
+automatically applied by IDLE when the extension is linked to an edit
+window.
+
+An IDLE extension class is instantiated with a single argument,
+`editwin', an EditorWindow instance. The extension cannot assume much
+about this argument, but it is guaranteed to have the following instance
+variables:
+
+ text a Text instance (a widget)
+ io an IOBinding instance (more about this later)
+ flist the FileList instance (shared by all edit windows)
+
+(There are a few more, but they are rarely useful.)
+
+The extension class must not directly bind Window Manager (e.g. X) events.
+Rather, it must define one or more virtual events, e.g. <<zoom-height>>, and
+corresponding methods, e.g. zoom_height_event(). The virtual events will be
+bound to the corresponding methods, and Window Manager events can then be bound
+to the virtual events. (This indirection is done so that the key bindings can
+easily be changed, and so that other sources of virtual events can exist, such
+as menu entries.)
+
+An extension can define menu entries. This is done with a class or instance
+variable named menudefs; it should be a list of pairs, where each pair is a
+menu name (lowercase) and a list of menu entries. Each menu entry is either
+None (to insert a separator entry) or a pair of strings (menu_label,
+virtual_event). Here, menu_label is the label of the menu entry, and
+virtual_event is the virtual event to be generated when the entry is selected.
+An underscore in the menu label is removed; the character following the
+underscore is displayed underlined, to indicate the shortcut character (for
+Windows).
+
+At the moment, extensions cannot define whole new menus; they must define
+entries in existing menus. Some menus are not present on some windows; such
+entry definitions are then ignored, but key bindings are still applied. (This
+should probably be refined in the future.)
+
+Extensions are not required to define menu entries for all the events they
+implement. (They are also not required to create keybindings, but in that
+case there must be empty bindings in cofig-extensions.def)
+
+Here is a complete example:
+
+class ZoomHeight:
+
+ menudefs = [
+ ('edit', [
+ None, # Separator
+ ('_Zoom Height', '<<zoom-height>>'),
+ ])
+ ]
+
+ def __init__(self, editwin):
+ self.editwin = editwin
+
+ def zoom_height_event(self, event):
+ "...Do what you want here..."
+
+The final piece of the puzzle is the file "config-extensions.def", which is
+used to configure the loading of extensions and to establish key (or, more
+generally, event) bindings to the virtual events defined in the extensions.
+
+See the comments at the top of config-extensions.def for information. It's
+currently necessary to manually modify that file to change IDLE's extension
+loading or extension key bindings.
+
+For further information on binding refer to the Tkinter Resources web page at
+python.org and to the Tk Command "bind" man page.
diff --git a/lib/python2.7/idlelib/help.html b/lib/python2.7/idlelib/help.html
new file mode 100644
index 0000000..1c4598b
--- /dev/null
+++ b/lib/python2.7/idlelib/help.html
@@ -0,0 +1,715 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>24.6. IDLE &mdash; Python 2.7.12 documentation</title>
+
+ <link rel="stylesheet" href="../_static/classic.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '2.7.12',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <script type="text/javascript" src="../_static/sidebar.js"></script>
+ <link rel="search" type="application/opensearchdescription+xml"
+ title="Search within Python 2.7.12 documentation"
+ href="../_static/opensearch.xml"/>
+ <link rel="author" title="About these documents" href="../about.html" />
+ <link rel="copyright" title="Copyright" href="../copyright.html" />
+ <link rel="top" title="Python 2.7.12 documentation" href="../contents.html" />
+ <link rel="up" title="24. Graphical User Interfaces with Tk" href="tk.html" />
+ <link rel="next" title="24.7. Other Graphical User Interface Packages" href="othergui.html" />
+ <link rel="prev" title="24.5. turtle — Turtle graphics for Tk" href="turtle.html" />
+ <link rel="shortcut icon" type="image/png" href="../_static/py.png" />
+ <script type="text/javascript" src="../_static/copybutton.js"></script>
+
+
+
+
+ </head>
+ <body role="document">
+ <div class="related" role="navigation" aria-label="related navigation">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="../py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
+ <li class="right" >
+ <a href="othergui.html" title="24.7. Other Graphical User Interface Packages"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="turtle.html" title="24.5. turtle — Turtle graphics for Tk"
+ accesskey="P">previous</a> |</li>
+ <li><img src="../_static/py.png" alt=""
+ style="vertical-align: middle; margin-top: -1px"/></li>
+ <li><a href="https://www.python.org/">Python</a> &raquo;</li>
+ <li>
+ <a href="../index.html">Python 2.7.12 documentation</a> &raquo;
+ </li>
+
+ <li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> &raquo;</li>
+ <li class="nav-item nav-item-2"><a href="tk.html" accesskey="U">24. Graphical User Interfaces with Tk</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body" role="main">
+
+ <div class="section" id="idle">
+<span id="id1"></span><h1>24.6. IDLE<a class="headerlink" href="#idle" title="Permalink to this headline">¶</a></h1>
+<p id="index-0">IDLE is Python&#8217;s Integrated Development and Learning Environment.</p>
+<p>IDLE has the following features:</p>
+<ul class="simple">
+<li>coded in 100% pure Python, using the <code class="xref py py-mod docutils literal"><span class="pre">tkinter</span></code> GUI toolkit</li>
+<li>cross-platform: works mostly the same on Windows, Unix, and Mac OS X</li>
+<li>Python shell window (interactive interpreter) with colorizing
+of code input, output, and error messages</li>
+<li>multi-window text editor with multiple undo, Python colorizing,
+smart indent, call tips, auto completion, and other features</li>
+<li>search within any window, replace within editor windows, and search
+through multiple files (grep)</li>
+<li>debugger with persistent breakpoints, stepping, and viewing
+of global and local namespaces</li>
+<li>configuration, browsers, and other dialogs</li>
+</ul>
+<div class="section" id="menus">
+<h2>24.6.1. Menus<a class="headerlink" href="#menus" title="Permalink to this headline">¶</a></h2>
+<p>IDLE has two main window types, the Shell window and the Editor window. It is
+possible to have multiple editor windows simultaneously. Output windows, such
+as used for Edit / Find in Files, are a subtype of edit window. They currently
+have the same top menu as Editor windows but a different default title and
+context menu.</p>
+<p>IDLE&#8217;s menus dynamically change based on which window is currently selected.
+Each menu documented below indicates which window type it is associated with.</p>
+<div class="section" id="file-menu-shell-and-editor">
+<h3>24.6.1.1. File menu (Shell and Editor)<a class="headerlink" href="#file-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
+<dl class="docutils">
+<dt>New File</dt>
+<dd>Create a new file editing window.</dd>
+<dt>Open...</dt>
+<dd>Open an existing file with an Open dialog.</dd>
+<dt>Recent Files</dt>
+<dd>Open a list of recent files. Click one to open it.</dd>
+<dt>Open Module...</dt>
+<dd>Open an existing module (searches sys.path).</dd>
+</dl>
+<dl class="docutils" id="index-1">
+<dt>Class Browser</dt>
+<dd>Show functions, classes, and methods in the current Editor file in a
+tree structure. In the shell, open a module first.</dd>
+<dt>Path Browser</dt>
+<dd>Show sys.path directories, modules, functions, classes and methods in a
+tree structure.</dd>
+<dt>Save</dt>
+<dd>Save the current window to the associated file, if there is one. Windows
+that have been changed since being opened or last saved have a * before
+and after the window title. If there is no associated file,
+do Save As instead.</dd>
+<dt>Save As...</dt>
+<dd>Save the current window with a Save As dialog. The file saved becomes the
+new associated file for the window.</dd>
+<dt>Save Copy As...</dt>
+<dd>Save the current window to different file without changing the associated
+file.</dd>
+<dt>Print Window</dt>
+<dd>Print the current window to the default printer.</dd>
+<dt>Close</dt>
+<dd>Close the current window (ask to save if unsaved).</dd>
+<dt>Exit</dt>
+<dd>Close all windows and quit IDLE (ask to save unsaved windows).</dd>
+</dl>
+</div>
+<div class="section" id="edit-menu-shell-and-editor">
+<h3>24.6.1.2. Edit menu (Shell and Editor)<a class="headerlink" href="#edit-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
+<dl class="docutils">
+<dt>Undo</dt>
+<dd>Undo the last change to the current window. A maximum of 1000 changes may
+be undone.</dd>
+<dt>Redo</dt>
+<dd>Redo the last undone change to the current window.</dd>
+<dt>Cut</dt>
+<dd>Copy selection into the system-wide clipboard; then delete the selection.</dd>
+<dt>Copy</dt>
+<dd>Copy selection into the system-wide clipboard.</dd>
+<dt>Paste</dt>
+<dd>Insert contents of the system-wide clipboard into the current window.</dd>
+</dl>
+<p>The clipboard functions are also available in context menus.</p>
+<dl class="docutils">
+<dt>Select All</dt>
+<dd>Select the entire contents of the current window.</dd>
+<dt>Find...</dt>
+<dd>Open a search dialog with many options</dd>
+<dt>Find Again</dt>
+<dd>Repeat the last search, if there is one.</dd>
+<dt>Find Selection</dt>
+<dd>Search for the currently selected string, if there is one.</dd>
+<dt>Find in Files...</dt>
+<dd>Open a file search dialog. Put results in a new output window.</dd>
+<dt>Replace...</dt>
+<dd>Open a search-and-replace dialog.</dd>
+<dt>Go to Line</dt>
+<dd>Move cursor to the line number requested and make that line visible.</dd>
+<dt>Show Completions</dt>
+<dd>Open a scrollable list allowing selection of keywords and attributes. See
+Completions in the Tips sections below.</dd>
+<dt>Expand Word</dt>
+<dd>Expand a prefix you have typed to match a full word in the same window;
+repeat to get a different expansion.</dd>
+<dt>Show call tip</dt>
+<dd>After an unclosed parenthesis for a function, open a small window with
+function parameter hints.</dd>
+<dt>Show surrounding parens</dt>
+<dd>Highlight the surrounding parenthesis.</dd>
+</dl>
+</div>
+<div class="section" id="format-menu-editor-window-only">
+<h3>24.6.1.3. Format menu (Editor window only)<a class="headerlink" href="#format-menu-editor-window-only" title="Permalink to this headline">¶</a></h3>
+<dl class="docutils">
+<dt>Indent Region</dt>
+<dd>Shift selected lines right by the indent width (default 4 spaces).</dd>
+<dt>Dedent Region</dt>
+<dd>Shift selected lines left by the indent width (default 4 spaces).</dd>
+<dt>Comment Out Region</dt>
+<dd>Insert ## in front of selected lines.</dd>
+<dt>Uncomment Region</dt>
+<dd>Remove leading # or ## from selected lines.</dd>
+<dt>Tabify Region</dt>
+<dd>Turn <em>leading</em> stretches of spaces into tabs. (Note: We recommend using
+4 space blocks to indent Python code.)</dd>
+<dt>Untabify Region</dt>
+<dd>Turn <em>all</em> tabs into the correct number of spaces.</dd>
+<dt>Toggle Tabs</dt>
+<dd>Open a dialog to switch between indenting with spaces and tabs.</dd>
+<dt>New Indent Width</dt>
+<dd>Open a dialog to change indent width. The accepted default by the Python
+community is 4 spaces.</dd>
+<dt>Format Paragraph</dt>
+<dd>Reformat the current blank-line-delimited paragraph in comment block or
+multiline string or selected line in a string. All lines in the
+paragraph will be formatted to less than N columns, where N defaults to 72.</dd>
+<dt>Strip trailing whitespace</dt>
+<dd>Remove any space characters after the last non-space character of a line.</dd>
+</dl>
+</div>
+<div class="section" id="run-menu-editor-window-only">
+<span id="index-2"></span><h3>24.6.1.4. Run menu (Editor window only)<a class="headerlink" href="#run-menu-editor-window-only" title="Permalink to this headline">¶</a></h3>
+<dl class="docutils">
+<dt>Python Shell</dt>
+<dd>Open or wake up the Python Shell window.</dd>
+<dt>Check Module</dt>
+<dd>Check the syntax of the module currently open in the Editor window. If the
+module has not been saved IDLE will either prompt the user to save or
+autosave, as selected in the General tab of the Idle Settings dialog. If
+there is a syntax error, the approximate location is indicated in the
+Editor window.</dd>
+<dt>Run Module</dt>
+<dd>Do Check Module (above). If no error, restart the shell to clean the
+environment, then execute the module. Output is displayed in the Shell
+window. Note that output requires use of <code class="docutils literal"><span class="pre">print</span></code> or <code class="docutils literal"><span class="pre">write</span></code>.
+When execution is complete, the Shell retains focus and displays a prompt.
+At this point, one may interactively explore the result of execution.
+This is similar to executing a file with <code class="docutils literal"><span class="pre">python</span> <span class="pre">-i</span> <span class="pre">file</span></code> at a command
+line.</dd>
+</dl>
+</div>
+<div class="section" id="shell-menu-shell-window-only">
+<h3>24.6.1.5. Shell menu (Shell window only)<a class="headerlink" href="#shell-menu-shell-window-only" title="Permalink to this headline">¶</a></h3>
+<dl class="docutils">
+<dt>View Last Restart</dt>
+<dd>Scroll the shell window to the last Shell restart.</dd>
+<dt>Restart Shell</dt>
+<dd>Restart the shell to clean the environment.</dd>
+<dt>Interrupt Execution</dt>
+<dd>Stop a running program.</dd>
+</dl>
+</div>
+<div class="section" id="debug-menu-shell-window-only">
+<h3>24.6.1.6. Debug menu (Shell window only)<a class="headerlink" href="#debug-menu-shell-window-only" title="Permalink to this headline">¶</a></h3>
+<dl class="docutils">
+<dt>Go to File/Line</dt>
+<dd>Look on the current line. with the cursor, and the line above for a filename
+and line number. If found, open the file if not already open, and show the
+line. Use this to view source lines referenced in an exception traceback
+and lines found by Find in Files. Also available in the context menu of
+the Shell window and Output windows.</dd>
+</dl>
+<dl class="docutils" id="index-3">
+<dt>Debugger (toggle)</dt>
+<dd>When actived, code entered in the Shell or run from an Editor will run
+under the debugger. In the Editor, breakpoints can be set with the context
+menu. This feature is still incomplete and somewhat experimental.</dd>
+<dt>Stack Viewer</dt>
+<dd>Show the stack traceback of the last exception in a tree widget, with
+access to locals and globals.</dd>
+<dt>Auto-open Stack Viewer</dt>
+<dd>Toggle automatically opening the stack viewer on an unhandled exception.</dd>
+</dl>
+</div>
+<div class="section" id="options-menu-shell-and-editor">
+<h3>24.6.1.7. Options menu (Shell and Editor)<a class="headerlink" href="#options-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
+<dl class="docutils">
+<dt>Configure IDLE</dt>
+<dd><p class="first">Open a configuration dialog and change preferences for the following:
+fonts, indentation, keybindings, text color themes, startup windows and
+size, additional help sources, and extensions (see below). On OS X,
+open the configuration dialog by selecting Preferences in the application
+menu. To use a new built-in color theme (IDLE Dark) with older IDLEs,
+save it as a new custom theme.</p>
+<p class="last">Non-default user settings are saved in a .idlerc directory in the user&#8217;s
+home directory. Problems caused by bad user configuration files are solved
+by editing or deleting one or more of the files in .idlerc.</p>
+</dd>
+<dt>Code Context (toggle)(Editor Window only)</dt>
+<dd>Open a pane at the top of the edit window which shows the block context
+of the code which has scrolled above the top of the window.</dd>
+</dl>
+</div>
+<div class="section" id="window-menu-shell-and-editor">
+<h3>24.6.1.8. Window menu (Shell and Editor)<a class="headerlink" href="#window-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
+<dl class="docutils">
+<dt>Zoom Height</dt>
+<dd>Toggles the window between normal size and maximum height. The initial size
+defaults to 40 lines by 80 chars unless changed on the General tab of the
+Configure IDLE dialog.</dd>
+</dl>
+<p>The rest of this menu lists the names of all open windows; select one to bring
+it to the foreground (deiconifying it if necessary).</p>
+</div>
+<div class="section" id="help-menu-shell-and-editor">
+<h3>24.6.1.9. Help menu (Shell and Editor)<a class="headerlink" href="#help-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
+<dl class="docutils">
+<dt>About IDLE</dt>
+<dd>Display version, copyright, license, credits, and more.</dd>
+<dt>IDLE Help</dt>
+<dd>Display a help file for IDLE detailing the menu options, basic editing and
+navigation, and other tips.</dd>
+<dt>Python Docs</dt>
+<dd>Access local Python documentation, if installed, or start a web browser
+and open docs.python.org showing the latest Python documentation.</dd>
+<dt>Turtle Demo</dt>
+<dd>Run the turtledemo module with example python code and turtle drawings.</dd>
+</dl>
+<p>Additional help sources may be added here with the Configure IDLE dialog under
+the General tab.</p>
+</div>
+<div class="section" id="context-menus">
+<span id="index-4"></span><h3>24.6.1.10. Context Menus<a class="headerlink" href="#context-menus" title="Permalink to this headline">¶</a></h3>
+<p>Open a context menu by right-clicking in a window (Control-click on OS X).
+Context menus have the standard clipboard functions also on the Edit menu.</p>
+<dl class="docutils">
+<dt>Cut</dt>
+<dd>Copy selection into the system-wide clipboard; then delete the selection.</dd>
+<dt>Copy</dt>
+<dd>Copy selection into the system-wide clipboard.</dd>
+<dt>Paste</dt>
+<dd>Insert contents of the system-wide clipboard into the current window.</dd>
+</dl>
+<p>Editor windows also have breakpoint functions. Lines with a breakpoint set are
+specially marked. Breakpoints only have an effect when running under the
+debugger. Breakpoints for a file are saved in the user&#8217;s .idlerc directory.</p>
+<dl class="docutils">
+<dt>Set Breakpoint</dt>
+<dd>Set a breakpoint on the current line.</dd>
+<dt>Clear Breakpoint</dt>
+<dd>Clear the breakpoint on that line.</dd>
+</dl>
+<p>Shell and Output windows have the following.</p>
+<dl class="docutils">
+<dt>Go to file/line</dt>
+<dd>Same as in Debug menu.</dd>
+</dl>
+</div>
+</div>
+<div class="section" id="editing-and-navigation">
+<h2>24.6.2. Editing and navigation<a class="headerlink" href="#editing-and-navigation" title="Permalink to this headline">¶</a></h2>
+<p>In this section, &#8216;C&#8217; refers to the <code class="kbd docutils literal"><span class="pre">Control</span></code> key on Windows and Unix and
+the <code class="kbd docutils literal"><span class="pre">Command</span></code> key on Mac OSX.</p>
+<ul>
+<li><p class="first"><code class="kbd docutils literal"><span class="pre">Backspace</span></code> deletes to the left; <code class="kbd docutils literal"><span class="pre">Del</span></code> deletes to the right</p>
+</li>
+<li><p class="first"><code class="kbd docutils literal"><span class="pre">C-Backspace</span></code> delete word left; <code class="kbd docutils literal"><span class="pre">C-Del</span></code> delete word to the right</p>
+</li>
+<li><p class="first">Arrow keys and <code class="kbd docutils literal"><span class="pre">Page</span> <span class="pre">Up</span></code>/<code class="kbd docutils literal"><span class="pre">Page</span> <span class="pre">Down</span></code> to move around</p>
+</li>
+<li><p class="first"><code class="kbd docutils literal"><span class="pre">C-LeftArrow</span></code> and <code class="kbd docutils literal"><span class="pre">C-RightArrow</span></code> moves by words</p>
+</li>
+<li><p class="first"><code class="kbd docutils literal"><span class="pre">Home</span></code>/<code class="kbd docutils literal"><span class="pre">End</span></code> go to begin/end of line</p>
+</li>
+<li><p class="first"><code class="kbd docutils literal"><span class="pre">C-Home</span></code>/<code class="kbd docutils literal"><span class="pre">C-End</span></code> go to begin/end of file</p>
+</li>
+<li><p class="first">Some useful Emacs bindings are inherited from Tcl/Tk:</p>
+<blockquote>
+<div><ul class="simple">
+<li><code class="kbd docutils literal"><span class="pre">C-a</span></code> beginning of line</li>
+<li><code class="kbd docutils literal"><span class="pre">C-e</span></code> end of line</li>
+<li><code class="kbd docutils literal"><span class="pre">C-k</span></code> kill line (but doesn&#8217;t put it in clipboard)</li>
+<li><code class="kbd docutils literal"><span class="pre">C-l</span></code> center window around the insertion point</li>
+<li><code class="kbd docutils literal"><span class="pre">C-b</span></code> go backwards one character without deleting (usually you can
+also use the cursor key for this)</li>
+<li><code class="kbd docutils literal"><span class="pre">C-f</span></code> go forward one character without deleting (usually you can
+also use the cursor key for this)</li>
+<li><code class="kbd docutils literal"><span class="pre">C-p</span></code> go up one line (usually you can also use the cursor key for
+this)</li>
+<li><code class="kbd docutils literal"><span class="pre">C-d</span></code> delete next character</li>
+</ul>
+</div></blockquote>
+</li>
+</ul>
+<p>Standard keybindings (like <code class="kbd docutils literal"><span class="pre">C-c</span></code> to copy and <code class="kbd docutils literal"><span class="pre">C-v</span></code> to paste)
+may work. Keybindings are selected in the Configure IDLE dialog.</p>
+<div class="section" id="automatic-indentation">
+<h3>24.6.2.1. Automatic indentation<a class="headerlink" href="#automatic-indentation" title="Permalink to this headline">¶</a></h3>
+<p>After a block-opening statement, the next line is indented by 4 spaces (in the
+Python Shell window by one tab). After certain keywords (break, return etc.)
+the next line is dedented. In leading indentation, <code class="kbd docutils literal"><span class="pre">Backspace</span></code> deletes up
+to 4 spaces if they are there. <code class="kbd docutils literal"><span class="pre">Tab</span></code> inserts spaces (in the Python
+Shell window one tab), number depends on Indent width. Currently tabs
+are restricted to four spaces due to Tcl/Tk limitations.</p>
+<p>See also the indent/dedent region commands in the edit menu.</p>
+</div>
+<div class="section" id="completions">
+<h3>24.6.2.2. Completions<a class="headerlink" href="#completions" title="Permalink to this headline">¶</a></h3>
+<p>Completions are supplied for functions, classes, and attributes of classes,
+both built-in and user-defined. Completions are also provided for
+filenames.</p>
+<p>The AutoCompleteWindow (ACW) will open after a predefined delay (default is
+two seconds) after a &#8216;.&#8217; or (in a string) an os.sep is typed. If after one
+of those characters (plus zero or more other characters) a tab is typed
+the ACW will open immediately if a possible continuation is found.</p>
+<p>If there is only one possible completion for the characters entered, a
+<code class="kbd docutils literal"><span class="pre">Tab</span></code> will supply that completion without opening the ACW.</p>
+<p>&#8216;Show Completions&#8217; will force open a completions window, by default the
+<code class="kbd docutils literal"><span class="pre">C-space</span></code> will open a completions window. In an empty
+string, this will contain the files in the current directory. On a
+blank line, it will contain the built-in and user-defined functions and
+classes in the current name spaces, plus any modules imported. If some
+characters have been entered, the ACW will attempt to be more specific.</p>
+<p>If a string of characters is typed, the ACW selection will jump to the
+entry most closely matching those characters. Entering a <code class="kbd docutils literal"><span class="pre">tab</span></code> will
+cause the longest non-ambiguous match to be entered in the Editor window or
+Shell. Two <code class="kbd docutils literal"><span class="pre">tab</span></code> in a row will supply the current ACW selection, as
+will return or a double click. Cursor keys, Page Up/Down, mouse selection,
+and the scroll wheel all operate on the ACW.</p>
+<p>&#8220;Hidden&#8221; attributes can be accessed by typing the beginning of hidden
+name after a &#8216;.&#8217;, e.g. &#8216;_&#8217;. This allows access to modules with
+<code class="docutils literal"><span class="pre">__all__</span></code> set, or to class-private attributes.</p>
+<p>Completions and the &#8216;Expand Word&#8217; facility can save a lot of typing!</p>
+<p>Completions are currently limited to those in the namespaces. Names in
+an Editor window which are not via <code class="docutils literal"><span class="pre">__main__</span></code> and <a class="reference internal" href="sys.html#sys.modules" title="sys.modules"><code class="xref py py-data docutils literal"><span class="pre">sys.modules</span></code></a> will
+not be found. Run the module once with your imports to correct this situation.
+Note that IDLE itself places quite a few modules in sys.modules, so
+much can be found by default, e.g. the re module.</p>
+<p>If you don&#8217;t like the ACW popping up unbidden, simply make the delay
+longer or disable the extension.</p>
+</div>
+<div class="section" id="calltips">
+<h3>24.6.2.3. Calltips<a class="headerlink" href="#calltips" title="Permalink to this headline">¶</a></h3>
+<p>A calltip is shown when one types <code class="kbd docutils literal"><span class="pre">(</span></code> after the name of an <em>acccessible</em>
+function. A name expression may include dots and subscripts. A calltip
+remains until it is clicked, the cursor is moved out of the argument area,
+or <code class="kbd docutils literal"><span class="pre">)</span></code> is typed. When the cursor is in the argument part of a definition,
+the menu or shortcut display a calltip.</p>
+<p>A calltip consists of the function signature and the first line of the
+docstring. For builtins without an accessible signature, the calltip
+consists of all lines up the fifth line or the first blank line. These
+details may change.</p>
+<p>The set of <em>accessible</em> functions depends on what modules have been imported
+into the user process, including those imported by Idle itself,
+and what definitions have been run, all since the last restart.</p>
+<p>For example, restart the Shell and enter <code class="docutils literal"><span class="pre">itertools.count(</span></code>. A calltip
+appears because Idle imports itertools into the user process for its own use.
+(This could change.) Enter <code class="docutils literal"><span class="pre">turtle.write(</span></code> and nothing appears. Idle does
+not import turtle. The menu or shortcut do nothing either. Enter
+<code class="docutils literal"><span class="pre">import</span> <span class="pre">turtle</span></code> and then <code class="docutils literal"><span class="pre">turtle.write(</span></code> will work.</p>
+<p>In an editor, import statements have no effect until one runs the file. One
+might want to run a file after writing the import statements at the top,
+or immediately run an existing file before editing.</p>
+</div>
+<div class="section" id="python-shell-window">
+<h3>24.6.2.4. Python Shell window<a class="headerlink" href="#python-shell-window" title="Permalink to this headline">¶</a></h3>
+<ul>
+<li><p class="first"><code class="kbd docutils literal"><span class="pre">C-c</span></code> interrupts executing command</p>
+</li>
+<li><p class="first"><code class="kbd docutils literal"><span class="pre">C-d</span></code> sends end-of-file; closes window if typed at a <code class="docutils literal"><span class="pre">&gt;&gt;&gt;</span></code> prompt</p>
+</li>
+<li><p class="first"><code class="kbd docutils literal"><span class="pre">Alt-/</span></code> (Expand word) is also useful to reduce typing</p>
+<p>Command history</p>
+<ul class="simple">
+<li><code class="kbd docutils literal"><span class="pre">Alt-p</span></code> retrieves previous command matching what you have typed. On
+OS X use <code class="kbd docutils literal"><span class="pre">C-p</span></code>.</li>
+<li><code class="kbd docutils literal"><span class="pre">Alt-n</span></code> retrieves next. On OS X use <code class="kbd docutils literal"><span class="pre">C-n</span></code>.</li>
+<li><code class="kbd docutils literal"><span class="pre">Return</span></code> while on any previous command retrieves that command</li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="section" id="text-colors">
+<h3>24.6.2.5. Text colors<a class="headerlink" href="#text-colors" title="Permalink to this headline">¶</a></h3>
+<p>Idle defaults to black on white text, but colors text with special meanings.
+For the shell, these are shell output, shell error, user output, and
+user error. For Python code, at the shell prompt or in an editor, these are
+keywords, builtin class and function names, names following <code class="docutils literal"><span class="pre">class</span></code> and
+<code class="docutils literal"><span class="pre">def</span></code>, strings, and comments. For any text window, these are the cursor (when
+present), found text (when possible), and selected text.</p>
+<p>Text coloring is done in the background, so uncolorized text is occasionally
+visible. To change the color scheme, use the Configure IDLE dialog
+Highlighting tab. The marking of debugger breakpoint lines in the editor and
+text in popups and dialogs is not user-configurable.</p>
+</div>
+</div>
+<div class="section" id="startup-and-code-execution">
+<h2>24.6.3. Startup and code execution<a class="headerlink" href="#startup-and-code-execution" title="Permalink to this headline">¶</a></h2>
+<p>Upon startup with the <code class="docutils literal"><span class="pre">-s</span></code> option, IDLE will execute the file referenced by
+the environment variables <span class="target" id="index-5"></span><code class="xref std std-envvar docutils literal"><span class="pre">IDLESTARTUP</span></code> or <span class="target" id="index-6"></span><a class="reference internal" href="../using/cmdline.html#envvar-PYTHONSTARTUP"><code class="xref std std-envvar docutils literal"><span class="pre">PYTHONSTARTUP</span></code></a>.
+IDLE first checks for <code class="docutils literal"><span class="pre">IDLESTARTUP</span></code>; if <code class="docutils literal"><span class="pre">IDLESTARTUP</span></code> is present the file
+referenced is run. If <code class="docutils literal"><span class="pre">IDLESTARTUP</span></code> is not present, IDLE checks for
+<code class="docutils literal"><span class="pre">PYTHONSTARTUP</span></code>. Files referenced by these environment variables are
+convenient places to store functions that are used frequently from the IDLE
+shell, or for executing import statements to import common modules.</p>
+<p>In addition, <code class="docutils literal"><span class="pre">Tk</span></code> also loads a startup file if it is present. Note that the
+Tk file is loaded unconditionally. This additional file is <code class="docutils literal"><span class="pre">.Idle.py</span></code> and is
+looked for in the user&#8217;s home directory. Statements in this file will be
+executed in the Tk namespace, so this file is not useful for importing
+functions to be used from IDLE&#8217;s Python shell.</p>
+<div class="section" id="command-line-usage">
+<h3>24.6.3.1. Command line usage<a class="headerlink" href="#command-line-usage" title="Permalink to this headline">¶</a></h3>
+<div class="highlight-none"><div class="highlight"><pre><span></span>idle.py [-c command] [-d] [-e] [-h] [-i] [-r file] [-s] [-t title] [-] [arg] ...
+
+-c command run command in the shell window
+-d enable debugger and open shell window
+-e open editor window
+-h print help message with legal combinations and exit
+-i open shell window
+-r file run file in shell window
+-s run $IDLESTARTUP or $PYTHONSTARTUP first, in shell window
+-t title set title of shell window
+- run stdin in shell (- must be last option before args)
+</pre></div>
+</div>
+<p>If there are arguments:</p>
+<ul class="simple">
+<li>If <code class="docutils literal"><span class="pre">-</span></code>, <code class="docutils literal"><span class="pre">-c</span></code>, or <code class="docutils literal"><span class="pre">r</span></code> is used, all arguments are placed in
+<code class="docutils literal"><span class="pre">sys.argv[1:...]</span></code> and <code class="docutils literal"><span class="pre">sys.argv[0]</span></code> is set to <code class="docutils literal"><span class="pre">''</span></code>, <code class="docutils literal"><span class="pre">'-c'</span></code>,
+or <code class="docutils literal"><span class="pre">'-r'</span></code>. No editor window is opened, even if that is the default
+set in the Options dialog.</li>
+<li>Otherwise, arguments are files opened for editing and
+<code class="docutils literal"><span class="pre">sys.argv</span></code> reflects the arguments passed to IDLE itself.</li>
+</ul>
+</div>
+<div class="section" id="idle-console-differences">
+<h3>24.6.3.2. IDLE-console differences<a class="headerlink" href="#idle-console-differences" title="Permalink to this headline">¶</a></h3>
+<p>As much as possible, the result of executing Python code with IDLE is the
+same as executing the same code in a console window. However, the different
+interface and operation occasionally affects visible results. For instance,
+<code class="docutils literal"><span class="pre">sys.modules</span></code> starts with more entries.</p>
+<p>IDLE also replaces <code class="docutils literal"><span class="pre">sys.stdin</span></code>, <code class="docutils literal"><span class="pre">sys.stdout</span></code>, and <code class="docutils literal"><span class="pre">sys.stderr</span></code> with
+objects that get input from and send output to the Shell window.
+When this window has the focus, it controls the keyboard and screen.
+This is normally transparent, but functions that directly access the keyboard
+and screen will not work. If <code class="docutils literal"><span class="pre">sys</span></code> is reset with <code class="docutils literal"><span class="pre">reload(sys)</span></code>,
+IDLE&#8217;s changes are lost and things like <code class="docutils literal"><span class="pre">input</span></code>, <code class="docutils literal"><span class="pre">raw_input</span></code>, and
+<code class="docutils literal"><span class="pre">print</span></code> will not work correctly.</p>
+<p>With IDLE&#8217;s Shell, one enters, edits, and recalls complete statements.
+Some consoles only work with a single physical line at a time. IDLE uses
+<code class="docutils literal"><span class="pre">exec</span></code> to run each statement. As a result, <code class="docutils literal"><span class="pre">'__builtins__'</span></code> is always
+defined for each statement.</p>
+</div>
+<div class="section" id="running-without-a-subprocess">
+<h3>24.6.3.3. Running without a subprocess<a class="headerlink" href="#running-without-a-subprocess" title="Permalink to this headline">¶</a></h3>
+<p>By default, IDLE executes user code in a separate subprocess via a socket,
+which uses the internal loopback interface. This connection is not
+externally visible and no data is sent to or received from the Internet.
+If firewall software complains anyway, you can ignore it.</p>
+<p>If the attempt to make the socket connection fails, Idle will notify you.
+Such failures are sometimes transient, but if persistent, the problem
+may be either a firewall blocking the connecton or misconfiguration of
+a particular system. Until the problem is fixed, one can run Idle with
+the -n command line switch.</p>
+<p>If IDLE is started with the -n command line switch it will run in a
+single process and will not create the subprocess which runs the RPC
+Python execution server. This can be useful if Python cannot create
+the subprocess or the RPC socket interface on your platform. However,
+in this mode user code is not isolated from IDLE itself. Also, the
+environment is not restarted when Run/Run Module (F5) is selected. If
+your code has been modified, you must reload() the affected modules and
+re-import any specific items (e.g. from foo import baz) if the changes
+are to take effect. For these reasons, it is preferable to run IDLE
+with the default subprocess if at all possible.</p>
+<div class="deprecated">
+<p><span class="versionmodified">Deprecated since version 3.4.</span></p>
+</div>
+</div>
+</div>
+<div class="section" id="help-and-preferences">
+<h2>24.6.4. Help and preferences<a class="headerlink" href="#help-and-preferences" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="additional-help-sources">
+<h3>24.6.4.1. Additional help sources<a class="headerlink" href="#additional-help-sources" title="Permalink to this headline">¶</a></h3>
+<p>IDLE includes a help menu entry called &#8220;Python Docs&#8221; that will open the
+extensive sources of help, including tutorials, available at docs.python.org.
+Selected URLs can be added or removed from the help menu at any time using the
+Configure IDLE dialog. See the IDLE help option in the help menu of IDLE for
+more information.</p>
+</div>
+<div class="section" id="setting-preferences">
+<h3>24.6.4.2. Setting preferences<a class="headerlink" href="#setting-preferences" title="Permalink to this headline">¶</a></h3>
+<p>The font preferences, highlighting, keys, and general preferences can be
+changed via Configure IDLE on the Option menu. Keys can be user defined;
+IDLE ships with four built in key sets. In addition a user can create a
+custom key set in the Configure IDLE dialog under the keys tab.</p>
+</div>
+<div class="section" id="extensions">
+<h3>24.6.4.3. Extensions<a class="headerlink" href="#extensions" title="Permalink to this headline">¶</a></h3>
+<p>IDLE contains an extension facility. Peferences for extensions can be
+changed with Configure Extensions. See the beginning of config-extensions.def
+in the idlelib directory for further information. The default extensions
+are currently:</p>
+<ul class="simple">
+<li>FormatParagraph</li>
+<li>AutoExpand</li>
+<li>ZoomHeight</li>
+<li>ScriptBinding</li>
+<li>CallTips</li>
+<li>ParenMatch</li>
+<li>AutoComplete</li>
+<li>CodeContext</li>
+<li>RstripExtension</li>
+</ul>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
+ <div class="sphinxsidebarwrapper">
+ <h3><a href="../contents.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">24.6. IDLE</a><ul>
+<li><a class="reference internal" href="#menus">24.6.1. Menus</a><ul>
+<li><a class="reference internal" href="#file-menu-shell-and-editor">24.6.1.1. File menu (Shell and Editor)</a></li>
+<li><a class="reference internal" href="#edit-menu-shell-and-editor">24.6.1.2. Edit menu (Shell and Editor)</a></li>
+<li><a class="reference internal" href="#format-menu-editor-window-only">24.6.1.3. Format menu (Editor window only)</a></li>
+<li><a class="reference internal" href="#run-menu-editor-window-only">24.6.1.4. Run menu (Editor window only)</a></li>
+<li><a class="reference internal" href="#shell-menu-shell-window-only">24.6.1.5. Shell menu (Shell window only)</a></li>
+<li><a class="reference internal" href="#debug-menu-shell-window-only">24.6.1.6. Debug menu (Shell window only)</a></li>
+<li><a class="reference internal" href="#options-menu-shell-and-editor">24.6.1.7. Options menu (Shell and Editor)</a></li>
+<li><a class="reference internal" href="#window-menu-shell-and-editor">24.6.1.8. Window menu (Shell and Editor)</a></li>
+<li><a class="reference internal" href="#help-menu-shell-and-editor">24.6.1.9. Help menu (Shell and Editor)</a></li>
+<li><a class="reference internal" href="#context-menus">24.6.1.10. Context Menus</a></li>
+</ul>
+</li>
+<li><a class="reference internal" href="#editing-and-navigation">24.6.2. Editing and navigation</a><ul>
+<li><a class="reference internal" href="#automatic-indentation">24.6.2.1. Automatic indentation</a></li>
+<li><a class="reference internal" href="#completions">24.6.2.2. Completions</a></li>
+<li><a class="reference internal" href="#calltips">24.6.2.3. Calltips</a></li>
+<li><a class="reference internal" href="#python-shell-window">24.6.2.4. Python Shell window</a></li>
+<li><a class="reference internal" href="#text-colors">24.6.2.5. Text colors</a></li>
+</ul>
+</li>
+<li><a class="reference internal" href="#startup-and-code-execution">24.6.3. Startup and code execution</a><ul>
+<li><a class="reference internal" href="#command-line-usage">24.6.3.1. Command line usage</a></li>
+<li><a class="reference internal" href="#idle-console-differences">24.6.3.2. IDLE-console differences</a></li>
+<li><a class="reference internal" href="#running-without-a-subprocess">24.6.3.3. Running without a subprocess</a></li>
+</ul>
+</li>
+<li><a class="reference internal" href="#help-and-preferences">24.6.4. Help and preferences</a><ul>
+<li><a class="reference internal" href="#additional-help-sources">24.6.4.1. Additional help sources</a></li>
+<li><a class="reference internal" href="#setting-preferences">24.6.4.2. Setting preferences</a></li>
+<li><a class="reference internal" href="#extensions">24.6.4.3. Extensions</a></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+
+ <h4>Previous topic</h4>
+ <p class="topless"><a href="turtle.html"
+ title="previous chapter">24.5. <code class="docutils literal"><span class="pre">turtle</span></code> &#8212; Turtle graphics for Tk</a></p>
+ <h4>Next topic</h4>
+ <p class="topless"><a href="othergui.html"
+ title="next chapter">24.7. Other Graphical User Interface Packages</a></p>
+<h3>This Page</h3>
+<ul class="this-page-menu">
+ <li><a href="../bugs.html">Report a Bug</a></li>
+ <li><a href="../_sources/library/idle.txt"
+ rel="nofollow">Show Source</a></li>
+</ul>
+
+<div id="searchbox" style="display: none" role="search">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="related" role="navigation" aria-label="related navigation">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ >index</a></li>
+ <li class="right" >
+ <a href="../py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
+ <li class="right" >
+ <a href="othergui.html" title="24.7. Other Graphical User Interface Packages"
+ >next</a> |</li>
+ <li class="right" >
+ <a href="turtle.html" title="24.5. turtle — Turtle graphics for Tk"
+ >previous</a> |</li>
+ <li><img src="../_static/py.png" alt=""
+ style="vertical-align: middle; margin-top: -1px"/></li>
+ <li><a href="https://www.python.org/">Python</a> &raquo;</li>
+ <li>
+ <a href="../index.html">Python 2.7.12 documentation</a> &raquo;
+ </li>
+
+ <li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> &raquo;</li>
+ <li class="nav-item nav-item-2"><a href="tk.html" >24. Graphical User Interfaces with Tk</a> &raquo;</li>
+ </ul>
+ </div>
+ <div class="footer">
+ &copy; <a href="../copyright.html">Copyright</a> 1990-2016, Python Software Foundation.
+ <br />
+ The Python Software Foundation is a non-profit corporation.
+ <a href="https://www.python.org/psf/donations/">Please donate.</a>
+ <br />
+ Last updated on Sep 12, 2016.
+ <a href="../bugs.html">Found a bug</a>?
+ <br />
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.3.6.
+ </div>
+
+ </body>
+</html>
diff --git a/lib/python2.7/idlelib/help.py b/lib/python2.7/idlelib/help.py
new file mode 100644
index 0000000..3ab4851
--- /dev/null
+++ b/lib/python2.7/idlelib/help.py
@@ -0,0 +1,277 @@
+""" help.py: Implement the Idle help menu.
+Contents are subject to revision at any time, without notice.
+
+
+Help => About IDLE: diplay About Idle dialog
+
+<to be moved here from aboutDialog.py>
+
+
+Help => IDLE Help: Display help.html with proper formatting.
+Doc/library/idle.rst (Sphinx)=> Doc/build/html/library/idle.html
+(help.copy_strip)=> Lib/idlelib/help.html
+
+HelpParser - Parse help.html and render to tk Text.
+
+HelpText - Display formatted help.html.
+
+HelpFrame - Contain text, scrollbar, and table-of-contents.
+(This will be needed for display in a future tabbed window.)
+
+HelpWindow - Display HelpFrame in a standalone window.
+
+copy_strip - Copy idle.html to help.html, rstripping each line.
+
+show_idlehelp - Create HelpWindow. Called in EditorWindow.help_dialog.
+"""
+from HTMLParser import HTMLParser
+from os.path import abspath, dirname, isdir, isfile, join
+from platform import python_version
+from Tkinter import Tk, Toplevel, Frame, Text, Scrollbar, Menu, Menubutton
+import tkFont as tkfont
+from idlelib.configHandler import idleConf
+
+use_ttk = False # until available to import
+if use_ttk:
+ from tkinter.ttk import Menubutton
+
+## About IDLE ##
+
+
+## IDLE Help ##
+
+class HelpParser(HTMLParser):
+ """Render help.html into a text widget.
+
+ The overridden handle_xyz methods handle a subset of html tags.
+ The supplied text should have the needed tag configurations.
+ The behavior for unsupported tags, such as table, is undefined.
+ If the tags generated by Sphinx change, this class, especially
+ the handle_starttag and handle_endtags methods, might have to also.
+ """
+ def __init__(self, text):
+ HTMLParser.__init__(self)
+ self.text = text # text widget we're rendering into
+ self.tags = '' # current block level text tags to apply
+ self.chartags = '' # current character level text tags
+ self.show = False # used so we exclude page navigation
+ self.hdrlink = False # used so we don't show header links
+ self.level = 0 # indentation level
+ self.pre = False # displaying preformatted text
+ self.hprefix = '' # prefix such as '25.5' to strip from headings
+ self.nested_dl = False # if we're in a nested <dl>
+ self.simplelist = False # simple list (no double spacing)
+ self.toc = [] # pair headers with text indexes for toc
+ self.header = '' # text within header tags for toc
+
+ def indent(self, amt=1):
+ self.level += amt
+ self.tags = '' if self.level == 0 else 'l'+str(self.level)
+
+ def handle_starttag(self, tag, attrs):
+ "Handle starttags in help.html."
+ class_ = ''
+ for a, v in attrs:
+ if a == 'class':
+ class_ = v
+ s = ''
+ if tag == 'div' and class_ == 'section':
+ self.show = True # start of main content
+ elif tag == 'div' and class_ == 'sphinxsidebar':
+ self.show = False # end of main content
+ elif tag == 'p' and class_ != 'first':
+ s = '\n\n'
+ elif tag == 'span' and class_ == 'pre':
+ self.chartags = 'pre'
+ elif tag == 'span' and class_ == 'versionmodified':
+ self.chartags = 'em'
+ elif tag == 'em':
+ self.chartags = 'em'
+ elif tag in ['ul', 'ol']:
+ if class_.find('simple') != -1:
+ s = '\n'
+ self.simplelist = True
+ else:
+ self.simplelist = False
+ self.indent()
+ elif tag == 'dl':
+ if self.level > 0:
+ self.nested_dl = True
+ elif tag == 'li':
+ s = '\n* ' if self.simplelist else '\n\n* '
+ elif tag == 'dt':
+ s = '\n\n' if not self.nested_dl else '\n' # avoid extra line
+ self.nested_dl = False
+ elif tag == 'dd':
+ self.indent()
+ s = '\n'
+ elif tag == 'pre':
+ self.pre = True
+ if self.show:
+ self.text.insert('end', '\n\n')
+ self.tags = 'preblock'
+ elif tag == 'a' and class_ == 'headerlink':
+ self.hdrlink = True
+ elif tag == 'h1':
+ self.tags = tag
+ elif tag in ['h2', 'h3']:
+ if self.show:
+ self.header = ''
+ self.text.insert('end', '\n\n')
+ self.tags = tag
+ if self.show:
+ self.text.insert('end', s, (self.tags, self.chartags))
+
+ def handle_endtag(self, tag):
+ "Handle endtags in help.html."
+ if tag in ['h1', 'h2', 'h3']:
+ self.indent(0) # clear tag, reset indent
+ if self.show:
+ self.toc.append((self.header, self.text.index('insert')))
+ elif tag in ['span', 'em']:
+ self.chartags = ''
+ elif tag == 'a':
+ self.hdrlink = False
+ elif tag == 'pre':
+ self.pre = False
+ self.tags = ''
+ elif tag in ['ul', 'dd', 'ol']:
+ self.indent(amt=-1)
+
+ def handle_data(self, data):
+ "Handle date segments in help.html."
+ if self.show and not self.hdrlink:
+ d = data if self.pre else data.replace('\n', ' ')
+ if self.tags == 'h1':
+ self.hprefix = d[0:d.index(' ')]
+ if self.tags in ['h1', 'h2', 'h3'] and self.hprefix != '':
+ if d[0:len(self.hprefix)] == self.hprefix:
+ d = d[len(self.hprefix):].strip()
+ self.header += d
+ self.text.insert('end', d, (self.tags, self.chartags))
+
+ def handle_charref(self, name):
+ if self.show:
+ self.text.insert('end', unichr(int(name)))
+
+
+class HelpText(Text):
+ "Display help.html."
+ def __init__(self, parent, filename):
+ "Configure tags and feed file to parser."
+ uwide = idleConf.GetOption('main', 'EditorWindow', 'width', type='int')
+ uhigh = idleConf.GetOption('main', 'EditorWindow', 'height', type='int')
+ uhigh = 3 * uhigh // 4 # lines average 4/3 of editor line height
+ Text.__init__(self, parent, wrap='word', highlightthickness=0,
+ padx=5, borderwidth=0, width=uwide, height=uhigh)
+
+ normalfont = self.findfont(['TkDefaultFont', 'arial', 'helvetica'])
+ fixedfont = self.findfont(['TkFixedFont', 'monaco', 'courier'])
+ self['font'] = (normalfont, 12)
+ self.tag_configure('em', font=(normalfont, 12, 'italic'))
+ self.tag_configure('h1', font=(normalfont, 20, 'bold'))
+ self.tag_configure('h2', font=(normalfont, 18, 'bold'))
+ self.tag_configure('h3', font=(normalfont, 15, 'bold'))
+ self.tag_configure('pre', font=(fixedfont, 12), background='#f6f6ff')
+ self.tag_configure('preblock', font=(fixedfont, 10), lmargin1=25,
+ borderwidth=1, relief='solid', background='#eeffcc')
+ self.tag_configure('l1', lmargin1=25, lmargin2=25)
+ self.tag_configure('l2', lmargin1=50, lmargin2=50)
+ self.tag_configure('l3', lmargin1=75, lmargin2=75)
+ self.tag_configure('l4', lmargin1=100, lmargin2=100)
+
+ self.parser = HelpParser(self)
+ with open(filename) as f:
+ contents = f.read().decode(encoding='utf-8')
+ self.parser.feed(contents)
+ self['state'] = 'disabled'
+
+ def findfont(self, names):
+ "Return name of first font family derived from names."
+ for name in names:
+ if name.lower() in (x.lower() for x in tkfont.names(root=self)):
+ font = tkfont.Font(name=name, exists=True, root=self)
+ return font.actual()['family']
+ elif name.lower() in (x.lower()
+ for x in tkfont.families(root=self)):
+ return name
+
+
+class HelpFrame(Frame):
+ "Display html text, scrollbar, and toc."
+ def __init__(self, parent, filename):
+ Frame.__init__(self, parent)
+ text = HelpText(self, filename)
+ self['background'] = text['background']
+ scroll = Scrollbar(self, command=text.yview)
+ text['yscrollcommand'] = scroll.set
+ self.rowconfigure(0, weight=1)
+ self.columnconfigure(1, weight=1) # text
+ self.toc_menu(text).grid(column=0, row=0, sticky='nw')
+ text.grid(column=1, row=0, sticky='nsew')
+ scroll.grid(column=2, row=0, sticky='ns')
+
+ def toc_menu(self, text):
+ "Create table of contents as drop-down menu."
+ toc = Menubutton(self, text='TOC')
+ drop = Menu(toc, tearoff=False)
+ for lbl, dex in text.parser.toc:
+ drop.add_command(label=lbl, command=lambda dex=dex:text.yview(dex))
+ toc['menu'] = drop
+ return toc
+
+
+class HelpWindow(Toplevel):
+ "Display frame with rendered html."
+ def __init__(self, parent, filename, title):
+ Toplevel.__init__(self, parent)
+ self.wm_title(title)
+ self.protocol("WM_DELETE_WINDOW", self.destroy)
+ HelpFrame(self, filename).grid(column=0, row=0, sticky='nsew')
+ self.grid_columnconfigure(0, weight=1)
+ self.grid_rowconfigure(0, weight=1)
+
+
+def copy_strip():
+ """Copy idle.html to idlelib/help.html, stripping trailing whitespace.
+
+ Files with trailing whitespace cannot be pushed to the hg cpython
+ repository. For 3.x (on Windows), help.html is generated, after
+ editing idle.rst in the earliest maintenance version, with
+ sphinx-build -bhtml . build/html
+ python_d.exe -c "from idlelib.help import copy_strip; copy_strip()"
+ After refreshing TortoiseHG workshop to generate a diff,
+ check both the diff and displayed text. Push the diff along with
+ the idle.rst change and merge both into default (or an intermediate
+ maintenance version).
+
+ When the 'earlist' version gets its final maintenance release,
+ do an update as described above, without editing idle.rst, to
+ rebase help.html on the next version of idle.rst. Do not worry
+ about version changes as version is not displayed. Examine other
+ changes and the result of Help -> IDLE Help.
+
+ If maintenance and default versions of idle.rst diverge, and
+ merging does not go smoothly, then consider generating
+ separate help.html files from separate idle.htmls.
+ """
+ src = join(abspath(dirname(dirname(dirname(__file__)))),
+ 'Doc', 'build', 'html', 'library', 'idle.html')
+ dst = join(abspath(dirname(__file__)), 'help.html')
+ with open(src, 'r') as inn,\
+ open(dst, 'w') as out:
+ for line in inn:
+ out.write(line.rstrip() + '\n')
+ print('idle.html copied to help.html')
+
+def show_idlehelp(parent):
+ "Create HelpWindow; called from Idle Help event handler."
+ filename = join(abspath(dirname(__file__)), 'help.html')
+ if not isfile(filename):
+ # try copy_strip, present message
+ return
+ HelpWindow(parent, filename, 'IDLE Help (%s)' % python_version())
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(show_idlehelp)
diff --git a/lib/python2.7/idlelib/help.txt b/lib/python2.7/idlelib/help.txt
new file mode 100644
index 0000000..296c78b
--- /dev/null
+++ b/lib/python2.7/idlelib/help.txt
@@ -0,0 +1,302 @@
+This file, idlelib/help.txt is out-of-date and no longer used by Idle.
+It is deprecated and will be removed in the future, possibly in 3.6
+----------------------------------------------------------------------
+
+[See the end of this file for ** TIPS ** on using IDLE !!]
+
+File Menu:
+
+ New File -- Create a new editing window
+ Open... -- Open an existing file
+ Recent Files... -- Open a list of recent files
+ Open Module... -- Open an existing module (searches sys.path)
+ Class Browser -- Show classes and methods in current file
+ Path Browser -- Show sys.path directories, modules, classes
+ and methods
+ ---
+ Save -- Save current window to the associated file (unsaved
+ windows have a * before and after the window title)
+
+ Save As... -- Save current window to new file, which becomes
+ the associated file
+ Save Copy As... -- Save current window to different file
+ without changing the associated file
+ ---
+ Print Window -- Print the current window
+ ---
+ Close -- Close current window (asks to save if unsaved)
+ Exit -- Close all windows, quit (asks to save if unsaved)
+
+Edit Menu:
+
+ Undo -- Undo last change to current window
+ (A maximum of 1000 changes may be undone)
+ Redo -- Redo last undone change to current window
+ ---
+ Cut -- Copy a selection into system-wide clipboard,
+ then delete the selection
+ Copy -- Copy selection into system-wide clipboard
+ Paste -- Insert system-wide clipboard into window
+ Select All -- Select the entire contents of the edit buffer
+ ---
+ Find... -- Open a search dialog box with many options
+ Find Again -- Repeat last search
+ Find Selection -- Search for the string in the selection
+ Find in Files... -- Open a search dialog box for searching files
+ Replace... -- Open a search-and-replace dialog box
+ Go to Line -- Ask for a line number and show that line
+ Show Calltip -- Open a small window with function param hints
+ Show Completions -- Open a scroll window allowing selection keywords
+ and attributes. (see '*TIPS*', below)
+ Show Parens -- Highlight the surrounding parenthesis
+ Expand Word -- Expand the word you have typed to match another
+ word in the same buffer; repeat to get a
+ different expansion
+
+Format Menu (only in Edit window):
+
+ Indent Region -- Shift selected lines right 4 spaces
+ Dedent Region -- Shift selected lines left 4 spaces
+ Comment Out Region -- Insert ## in front of selected lines
+ Uncomment Region -- Remove leading # or ## from selected lines
+ Tabify Region -- Turns *leading* stretches of spaces into tabs
+ (Note: We recommend using 4 space blocks to indent Python code.)
+ Untabify Region -- Turn *all* tabs into the right number of spaces
+ New Indent Width... -- Open dialog to change indent width
+ Format Paragraph -- Reformat the current blank-line-separated
+ paragraph
+
+Run Menu (only in Edit window):
+
+ Python Shell -- Open or wake up the Python shell window
+ ---
+ Check Module -- Run a syntax check on the module
+ Run Module -- Execute the current file in the __main__ namespace
+
+Shell Menu (only in Shell window):
+
+ View Last Restart -- Scroll the shell window to the last restart
+ Restart Shell -- Restart the interpreter with a fresh environment
+
+Debug Menu (only in Shell window):
+
+ Go to File/Line -- look around the insert point for a filename
+ and line number, open the file, and show the line
+ Debugger (toggle) -- Run commands in the shell under the debugger
+ Stack Viewer -- Show the stack traceback of the last exception
+ Auto-open Stack Viewer (toggle) -- Open stack viewer on traceback
+
+Options Menu:
+
+ Configure IDLE -- Open a configuration dialog. Fonts, indentation,
+ keybindings, and color themes may be altered.
+ Startup Preferences may be set, and Additional Help
+ Sources can be specified. On OS X, open the
+ configuration dialog by selecting Preferences
+ in the application menu.
+ ---
+ Code Context -- Open a pane at the top of the edit window which
+ shows the block context of the section of code
+ which is scrolling off the top or the window.
+ (Not present in Shell window.)
+
+Window Menu:
+
+ Zoom Height -- toggles the window between configured size
+ and maximum height.
+ ---
+ The rest of this menu lists the names of all open windows;
+ select one to bring it to the foreground (deiconifying it if
+ necessary).
+
+Help Menu:
+
+ About IDLE -- Version, copyright, license, credits
+ IDLE Readme -- Background discussion and change details
+ ---
+ IDLE Help -- Display this file
+ Python Docs -- Access local Python documentation, if
+ installed. Otherwise, access www.python.org.
+ ---
+ (Additional Help Sources may be added here)
+
+Edit context menu (Right-click / Control-click on OS X in Edit window):
+
+ Cut -- Copy a selection into system-wide clipboard,
+ then delete the selection
+ Copy -- Copy selection into system-wide clipboard
+ Paste -- Insert system-wide clipboard into window
+ Set Breakpoint -- Sets a breakpoint (when debugger open)
+ Clear Breakpoint -- Clears the breakpoint on that line
+
+Shell context menu (Right-click / Control-click on OS X in Shell window):
+
+ Cut -- Copy a selection into system-wide clipboard,
+ then delete the selection
+ Copy -- Copy selection into system-wide clipboard
+ Paste -- Insert system-wide clipboard into window
+ ---
+ Go to file/line -- Same as in Debug menu
+
+
+** TIPS **
+==========
+
+Additional Help Sources:
+
+ Windows users can Google on zopeshelf.chm to access Zope help files in
+ the Windows help format. The Additional Help Sources feature of the
+ configuration GUI supports .chm, along with any other filetypes
+ supported by your browser. Supply a Menu Item title, and enter the
+ location in the Help File Path slot of the New Help Source dialog. Use
+ http:// and/or www. to identify external URLs, or download the file and
+ browse for its path on your machine using the Browse button.
+
+ All users can access the extensive sources of help, including
+ tutorials, available at www.python.org/doc. Selected URLs can be added
+ or removed from the Help menu at any time using Configure IDLE.
+
+Basic editing and navigation:
+
+ Backspace deletes char to the left; DEL deletes char to the right.
+ Control-backspace deletes word left, Control-DEL deletes word right.
+ Arrow keys and Page Up/Down move around.
+ Control-left/right Arrow moves by words in a strange but useful way.
+ Home/End go to begin/end of line.
+ Control-Home/End go to begin/end of file.
+ Some useful Emacs bindings are inherited from Tcl/Tk:
+ Control-a beginning of line
+ Control-e end of line
+ Control-k kill line (but doesn't put it in clipboard)
+ Control-l center window around the insertion point
+ Standard Windows bindings may work on that platform.
+ Keybindings are selected in the Settings Dialog, look there.
+
+Automatic indentation:
+
+ After a block-opening statement, the next line is indented by 4 spaces
+ (in the Python Shell window by one tab). After certain keywords
+ (break, return etc.) the next line is dedented. In leading
+ indentation, Backspace deletes up to 4 spaces if they are there. Tab
+ inserts spaces (in the Python Shell window one tab), number depends on
+ Indent Width. (N.B. Currently tabs are restricted to four spaces due
+ to Tcl/Tk issues.)
+
+ See also the indent/dedent region commands in the edit menu.
+
+Completions:
+
+ Completions are supplied for functions, classes, and attributes of
+ classes, both built-in and user-defined. Completions are also provided
+ for filenames.
+
+ The AutoCompleteWindow (ACW) will open after a predefined delay
+ (default is two seconds) after a '.' or (in a string) an os.sep is
+ typed. If after one of those characters (plus zero or more other
+ characters) you type a Tab the ACW will open immediately if a possible
+ continuation is found.
+
+ If there is only one possible completion for the characters entered, a
+ Tab will supply that completion without opening the ACW.
+
+ 'Show Completions' will force open a completions window. In an empty
+ string, this will contain the files in the current directory. On a
+ blank line, it will contain the built-in and user-defined functions and
+ classes in the current name spaces, plus any modules imported. If some
+ characters have been entered, the ACW will attempt to be more specific.
+
+ If string of characters is typed, the ACW selection will jump to the
+ entry most closely matching those characters. Entering a Tab will cause
+ the longest non-ambiguous match to be entered in the Edit window or
+ Shell. Two Tabs in a row will supply the current ACW selection, as
+ will Return or a double click. Cursor keys, Page Up/Down, mouse
+ selection, and the scrollwheel all operate on the ACW.
+
+ 'Hidden' attributes can be accessed by typing the beginning of hidden
+ name after a '.'. e.g. '_'. This allows access to modules with
+ '__all__' set, or to class-private attributes.
+
+ Completions and the 'Expand Word' facility can save a lot of typing!
+
+ Completions are currently limited to those in the namespaces. Names in
+ an Edit window which are not via __main__ or sys.modules will not be
+ found. Run the module once with your imports to correct this
+ situation. Note that IDLE itself places quite a few modules in
+ sys.modules, so much can be found by default, e.g. the re module.
+
+ If you don't like the ACW popping up unbidden, simply make the delay
+ longer or disable the extension. OTOH, you could make the delay zero.
+
+ You could also switch off the CallTips extension. (We will be adding
+ a delay to the call tip window.)
+
+Python Shell window:
+
+ Control-c interrupts executing command.
+ Control-d sends end-of-file; closes window if typed at >>> prompt.
+
+ Command history:
+
+ Alt-p retrieves previous command matching what you have typed.
+ Alt-n retrieves next.
+ (These are Control-p, Control-n on OS X)
+ Return while cursor is on a previous command retrieves that command.
+ Expand word is also useful to reduce typing.
+
+ Syntax colors:
+
+ The coloring is applied in a background "thread", so you may
+ occasionally see uncolorized text. To change the color
+ scheme, use the Configure IDLE / Highlighting dialog.
+
+ Python default syntax colors:
+
+ Keywords orange
+ Builtins royal purple
+ Strings green
+ Comments red
+ Definitions blue
+
+ Shell default colors:
+
+ Console output brown
+ stdout blue
+ stderr red
+ stdin black
+
+Other preferences:
+
+ The font preferences, keybinding, and startup preferences can
+ be changed using the Settings dialog.
+
+Command line usage:
+
+ Enter idle -h at the command prompt to get a usage message.
+
+Running without a subprocess:
+
+ If IDLE is started with the -n command line switch it will run in a
+ single process and will not create the subprocess which runs the RPC
+ Python execution server. This can be useful if Python cannot create
+ the subprocess or the RPC socket interface on your platform. However,
+ in this mode user code is not isolated from IDLE itself. Also, the
+ environment is not restarted when Run/Run Module (F5) is selected. If
+ your code has been modified, you must reload() the affected modules and
+ re-import any specific items (e.g. from foo import baz) if the changes
+ are to take effect. For these reasons, it is preferable to run IDLE
+ with the default subprocess if at all possible.
+
+Extensions:
+
+ IDLE contains an extension facility. See the beginning of
+ config-extensions.def in the idlelib directory for further information.
+ The default extensions are currently:
+
+ FormatParagraph
+ AutoExpand
+ ZoomHeight
+ ScriptBinding
+ CallTips
+ ParenMatch
+ AutoComplete
+ CodeContext
diff --git a/lib/python2.7/idlelib/idle.bat b/lib/python2.7/idlelib/idle.bat
new file mode 100644
index 0000000..e77b96e
--- /dev/null
+++ b/lib/python2.7/idlelib/idle.bat
@@ -0,0 +1,4 @@
+@echo off
+rem Start IDLE using the appropriate Python interpreter
+set CURRDIR=%~dp0
+start "IDLE" "%CURRDIR%..\..\pythonw.exe" "%CURRDIR%idle.pyw" %1 %2 %3 %4 %5 %6 %7 %8 %9
diff --git a/lib/python2.7/idlelib/idle.py b/lib/python2.7/idlelib/idle.py
new file mode 100644
index 0000000..141534d
--- /dev/null
+++ b/lib/python2.7/idlelib/idle.py
@@ -0,0 +1,13 @@
+import os.path
+import sys
+
+# Enable running IDLE with idlelib in a non-standard location.
+# This was once used to run development versions of IDLE.
+# Because PEP 434 declared idle.py a public interface,
+# removal should require deprecation.
+idlelib_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+if idlelib_dir not in sys.path:
+ sys.path.insert(0, idlelib_dir)
+
+from idlelib.PyShell import main # This is subject to change
+main()
diff --git a/lib/python2.7/idlelib/idle.pyw b/lib/python2.7/idlelib/idle.pyw
new file mode 100644
index 0000000..9ce4c9f
--- /dev/null
+++ b/lib/python2.7/idlelib/idle.pyw
@@ -0,0 +1,17 @@
+try:
+ import idlelib.PyShell
+except ImportError:
+ # IDLE is not installed, but maybe PyShell is on sys.path:
+ import PyShell
+ import os
+ idledir = os.path.dirname(os.path.abspath(PyShell.__file__))
+ if idledir != os.getcwd():
+ # We're not in the IDLE directory, help the subprocess find run.py
+ pypath = os.environ.get('PYTHONPATH', '')
+ if pypath:
+ os.environ['PYTHONPATH'] = pypath + ':' + idledir
+ else:
+ os.environ['PYTHONPATH'] = idledir
+ PyShell.main()
+else:
+ idlelib.PyShell.main()
diff --git a/lib/python2.7/idlelib/idle_test/README.txt b/lib/python2.7/idlelib/idle_test/README.txt
new file mode 100644
index 0000000..6967d70
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/README.txt
@@ -0,0 +1,150 @@
+README FOR IDLE TESTS IN IDLELIB.IDLE_TEST
+
+0. Quick Start
+
+Automated unit tests were added in 2.7 for Python 2.x and 3.3 for Python 3.x.
+To run the tests from a command line:
+
+python -m test.test_idle
+
+Human-mediated tests were added later in 2.7 and in 3.4.
+
+python -m idlelib.idle_test.htest
+
+
+1. Test Files
+
+The idle directory, idlelib, has over 60 xyz.py files. The idle_test
+subdirectory should contain a test_xyz.py for each, where 'xyz' is lowercased
+even if xyz.py is not. Here is a possible template, with the blanks after
+'.' and 'as', and before and after '_' to be filled in.
+
+import unittest
+from test.support import requires
+import idlelib. as
+
+class _Test(unittest.TestCase):
+
+ def test_(self):
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
+
+Add the following at the end of xyy.py, with the appropriate name added after
+'test_'. Some files already have something like this for htest. If so, insert
+the import and unittest.main lines before the htest lines.
+
+if __name__ == "__main__":
+ import unittest
+ unittest.main('idlelib.idle_test.test_', verbosity=2, exit=False)
+
+
+
+2. GUI Tests
+
+When run as part of the Python test suite, Idle GUI tests need to run
+test.test_support.requires('gui') (test.support in 3.x). A test is a GUI test
+if it creates a Tk root or master object either directly or indirectly by
+instantiating a tkinter or idle class. For the benefit of test processes that
+either have no graphical environment available or are not allowed to use it, GUI
+tests must be 'guarded' by "requires('gui')" in a setUp function or method.
+This will typically be setUpClass.
+
+To avoid interfering with other GUI tests, all GUI objects must be destroyed and
+deleted by the end of the test. The Tk root created in a setUpX function should
+be destroyed in the corresponding tearDownX and the module or class attribute
+deleted. Others widgets should descend from the single root and the attributes
+deleted BEFORE root is destroyed. See https://bugs.python.org/issue20567.
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = tk.Tk()
+ cls.text = tk.Text(root)
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.text
+ cls.root.destroy()
+ del cls.root
+
+WARNING: In 2.7, "requires('gui') MUST NOT be called at module scope.
+See https://bugs.python.org/issue18910
+
+Requires('gui') causes the test(s) it guards to be skipped if any of
+these conditions are met:
+
+ - The tests are being run by regrtest.py, and it was started without enabling
+ the "gui" resource with the "-u" command line option.
+
+ - The tests are being run on Windows by a service that is not allowed to
+ interact with the graphical environment.
+
+ - The tests are being run on Linux and X Windows is not available.
+
+ - The tests are being run on Mac OSX in a process that cannot make a window
+ manager connection.
+
+ - tkinter.Tk cannot be successfully instantiated for some reason.
+
+ - test.support.use_resources has been set by something other than
+ regrtest.py and does not contain "gui".
+
+Tests of non-GUI operations should avoid creating tk widgets. Incidental uses of
+tk variables and messageboxes can be replaced by the mock classes in
+idle_test/mock_tk.py. The mock text handles some uses of the tk Text widget.
+
+
+3. Running Unit Tests
+
+Assume that xyz.py and test_xyz.py both end with a unittest.main() call.
+Running either from an Idle editor runs all tests in the test_xyz file with the
+version of Python running Idle. Test output appears in the Shell window. The
+'verbosity=2' option lists all test methods in the file, which is appropriate
+when developing tests. The 'exit=False' option is needed in xyx.py files when an
+htest follows.
+
+The following command lines also run all test methods, including
+GUI tests, in test_xyz.py. (Both '-m idlelib' and '-m idlelib.idle' start
+Idle and so cannot run tests.)
+
+python -m idlelib.xyz
+python -m idlelib.idle_test.test_xyz
+
+The following runs all idle_test/test_*.py tests interactively.
+
+>>> import unittest
+>>> unittest.main('idlelib.idle_test', verbosity=2)
+
+The following run all Idle tests at a command line. Option '-v' is the same as
+'verbosity=2'. (For 2.7, replace 'test' in the second line with
+'test.regrtest'.)
+
+python -m unittest -v idlelib.idle_test
+python -m test -v -ugui test_idle
+python -m test.test_idle
+
+The idle tests are 'discovered' by idlelib.idle_test.__init__.load_tests,
+which is also imported into test.test_idle. Normally, neither file should be
+changed when working on individual test modules. The third command runs
+unittest indirectly through regrtest. The same happens when the entire test
+suite is run with 'python -m test'. So that command must work for buildbots
+to stay green. Idle tests must not disturb the environment in a way that
+makes other tests fail (issue 18081).
+
+To run an individual Testcase or test method, extend the dotted name given to
+unittest on the command line.
+
+python -m unittest -v idlelib.idle_test.test_xyz.Test_case.test_meth
+
+
+4. Human-mediated Tests
+
+Human-mediated tests are widget tests that cannot be automated but need human
+verification. They are contained in idlelib/idle_test/htest.py, which has
+instructions. (Some modules need an auxiliary function, identified with # htest
+# on the header line.) The set is about complete, though some tests need
+improvement. To run all htests, run the htest file from an editor or from the
+command line with:
+
+python -m idlelib.idle_test.htest
diff --git a/lib/python2.7/idlelib/idle_test/__init__.py b/lib/python2.7/idlelib/idle_test/__init__.py
new file mode 100644
index 0000000..845c92d
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/__init__.py
@@ -0,0 +1,15 @@
+'''idlelib.idle_test is a private implementation of test.test_idle,
+which tests the IDLE application as part of the stdlib test suite.
+Run IDLE tests alone with "python -m test.test_idle".
+This package and its contained modules are subject to change and
+any direct use is at your own risk.
+'''
+from os.path import dirname
+
+def load_tests(loader, standard_tests, pattern):
+ this_dir = dirname(__file__)
+ top_dir = dirname(dirname(this_dir))
+ package_tests = loader.discover(start_dir=this_dir, pattern='test*.py',
+ top_level_dir=top_dir)
+ standard_tests.addTests(package_tests)
+ return standard_tests
diff --git a/lib/python2.7/idlelib/idle_test/htest.py b/lib/python2.7/idlelib/idle_test/htest.py
new file mode 100644
index 0000000..f341409
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/htest.py
@@ -0,0 +1,403 @@
+'''Run human tests of Idle's window, dialog, and popup widgets.
+
+run(*tests)
+Create a master Tk window. Within that, run each callable in tests
+after finding the matching test spec in this file. If tests is empty,
+run an htest for each spec dict in this file after finding the matching
+callable in the module named in the spec. Close the window to skip or
+end the test.
+
+In a tested module, let X be a global name bound to a callable (class
+or function) whose .__name__ attrubute is also X (the usual situation).
+The first parameter of X must be 'parent'. When called, the parent
+argument will be the root window. X must create a child Toplevel
+window (or subclass thereof). The Toplevel may be a test widget or
+dialog, in which case the callable is the corresonding class. Or the
+Toplevel may contain the widget to be tested or set up a context in
+which a test widget is invoked. In this latter case, the callable is a
+wrapper function that sets up the Toplevel and other objects. Wrapper
+function names, such as _editor_window', should start with '_'.
+
+
+End the module with
+
+if __name__ == '__main__':
+ <unittest, if there is one>
+ from idlelib.idle_test.htest import run
+ run(X)
+
+To have wrapper functions and test invocation code ignored by coveragepy
+reports, put '# htest #' on the def statement header line.
+
+def _wrapper(parent): # htest #
+
+Also make sure that the 'if __name__' line matches the above. Then have
+make sure that .coveragerc includes the following.
+
+[report]
+exclude_lines =
+ .*# htest #
+ if __name__ == .__main__.:
+
+(The "." instead of "'" is intentional and necessary.)
+
+
+To run any X, this file must contain a matching instance of the
+following template, with X.__name__ prepended to '_spec'.
+When all tests are run, the prefix is use to get X.
+
+_spec = {
+ 'file': '',
+ 'kwds': {'title': ''},
+ 'msg': ""
+ }
+
+file (no .py): run() imports file.py.
+kwds: augmented with {'parent':root} and passed to X as **kwds.
+title: an example kwd; some widgets need this, delete if not.
+msg: master window hints about testing the widget.
+
+
+Modules and classes not being tested at the moment:
+PyShell.PyShellEditorWindow
+Debugger.Debugger
+AutoCompleteWindow.AutoCompleteWindow
+OutputWindow.OutputWindow (indirectly being tested with grep test)
+'''
+
+from importlib import import_module
+from idlelib.macosxSupport import _initializeTkVariantTests
+import Tkinter as tk
+
+AboutDialog_spec = {
+ 'file': 'aboutDialog',
+ 'kwds': {'title': 'aboutDialog test',
+ '_htest': True,
+ },
+ 'msg': "Test every button. Ensure Python, TK and IDLE versions "
+ "are correctly displayed.\n [Close] to exit.",
+ }
+
+_calltip_window_spec = {
+ 'file': 'CallTipWindow',
+ 'kwds': {},
+ 'msg': "Typing '(' should display a calltip.\n"
+ "Typing ') should hide the calltip.\n"
+ }
+
+_class_browser_spec = {
+ 'file': 'ClassBrowser',
+ 'kwds': {},
+ 'msg': "Inspect names of module, class(with superclass if "
+ "applicable), methods and functions.\nToggle nested items.\n"
+ "Double clicking on items prints a traceback for an exception "
+ "that is ignored."
+ }
+
+_color_delegator_spec = {
+ 'file': 'ColorDelegator',
+ 'kwds': {},
+ 'msg': "The text is sample Python code.\n"
+ "Ensure components like comments, keywords, builtins,\n"
+ "string, definitions, and break are correctly colored.\n"
+ "The default color scheme is in idlelib/config-highlight.def"
+ }
+
+ConfigDialog_spec = {
+ 'file': 'configDialog',
+ 'kwds': {'title': 'ConfigDialogTest',
+ '_htest': True,},
+ 'msg': "IDLE preferences dialog.\n"
+ "In the 'Fonts/Tabs' tab, changing font face, should update the "
+ "font face of the text in the area below it.\nIn the "
+ "'Highlighting' tab, try different color schemes. Clicking "
+ "items in the sample program should update the choices above it."
+ "\nIn the 'Keys', 'General' and 'Extensions' tabs, test settings"
+ "of interest."
+ "\n[Ok] to close the dialog.[Apply] to apply the settings and "
+ "and [Cancel] to revert all changes.\nRe-run the test to ensure "
+ "changes made have persisted."
+ }
+
+# TODO Improve message
+_dyn_option_menu_spec = {
+ 'file': 'dynOptionMenuWidget',
+ 'kwds': {},
+ 'msg': "Select one of the many options in the 'old option set'.\n"
+ "Click the button to change the option set.\n"
+ "Select one of the many options in the 'new option set'."
+ }
+
+# TODO edit wrapper
+_editor_window_spec = {
+ 'file': 'EditorWindow',
+ 'kwds': {},
+ 'msg': "Test editor functions of interest.\n"
+ "Best to close editor first."
+ }
+
+GetCfgSectionNameDialog_spec = {
+ 'file': 'configSectionNameDialog',
+ 'kwds': {'title':'Get Name',
+ 'message':'Enter something',
+ 'used_names': {'abc'},
+ '_htest': True},
+ 'msg': "After the text entered with [Ok] is stripped, <nothing>, "
+ "'abc', or more that 30 chars are errors.\n"
+ "Close 'Get Name' with a valid entry (printed to Shell), "
+ "[Cancel], or [X]",
+ }
+
+GetHelpSourceDialog_spec = {
+ 'file': 'configHelpSourceEdit',
+ 'kwds': {'title': 'Get helpsource',
+ '_htest': True},
+ 'msg': "Enter menu item name and help file path\n "
+ "<nothing> and more than 30 chars are invalid menu item names.\n"
+ "<nothing>, file does not exist are invalid path items.\n"
+ "Test for incomplete web address for help file path.\n"
+ "A valid entry will be printed to shell with [0k].\n"
+ "[Cancel] will print None to shell",
+ }
+
+# Update once issue21519 is resolved.
+GetKeysDialog_spec = {
+ 'file': 'keybindingDialog',
+ 'kwds': {'title': 'Test keybindings',
+ 'action': 'find-again',
+ 'currentKeySequences': [''] ,
+ '_htest': True,
+ },
+ 'msg': "Test for different key modifier sequences.\n"
+ "<nothing> is invalid.\n"
+ "No modifier key is invalid.\n"
+ "Shift key with [a-z],[0-9], function key, move key, tab, space"
+ "is invalid.\nNo validitity checking if advanced key binding "
+ "entry is used."
+ }
+
+_grep_dialog_spec = {
+ 'file': 'GrepDialog',
+ 'kwds': {},
+ 'msg': "Click the 'Show GrepDialog' button.\n"
+ "Test the various 'Find-in-files' functions.\n"
+ "The results should be displayed in a new '*Output*' window.\n"
+ "'Right-click'->'Goto file/line' anywhere in the search results "
+ "should open that file \nin a new EditorWindow."
+ }
+
+_io_binding_spec = {
+ 'file': 'IOBinding',
+ 'kwds': {},
+ 'msg': "Test the following bindings.\n"
+ "<Control-o> to open file from dialog.\n"
+ "Edit the file.\n"
+ "<Control-p> to print the file.\n"
+ "<Control-s> to save the file.\n"
+ "<Alt-s> to save-as another file.\n"
+ "<Control-c> to save-copy-as another file.\n"
+ "Check that changes were saved by opening the file elsewhere."
+ }
+
+_multi_call_spec = {
+ 'file': 'MultiCall',
+ 'kwds': {},
+ 'msg': "The following actions should trigger a print to console or IDLE"
+ " Shell.\nEntering and leaving the text area, key entry, "
+ "<Control-Key>,\n<Alt-Key-a>, <Control-Key-a>, "
+ "<Alt-Control-Key-a>, \n<Control-Button-1>, <Alt-Button-1> and "
+ "focusing out of the window\nare sequences to be tested."
+ }
+
+_multistatus_bar_spec = {
+ 'file': 'MultiStatusBar',
+ 'kwds': {},
+ 'msg': "Ensure presence of multi-status bar below text area.\n"
+ "Click 'Update Status' to change the multi-status text"
+ }
+
+_object_browser_spec = {
+ 'file': 'ObjectBrowser',
+ 'kwds': {},
+ 'msg': "Double click on items upto the lowest level.\n"
+ "Attributes of the objects and related information "
+ "will be displayed side-by-side at each level."
+ }
+
+_path_browser_spec = {
+ 'file': 'PathBrowser',
+ 'kwds': {},
+ 'msg': "Test for correct display of all paths in sys.path.\n"
+ "Toggle nested items upto the lowest level.\n"
+ "Double clicking on an item prints a traceback\n"
+ "for an exception that is ignored."
+ }
+
+_percolator_spec = {
+ 'file': 'Percolator',
+ 'kwds': {},
+ 'msg': "There are two tracers which can be toggled using a checkbox.\n"
+ "Toggling a tracer 'on' by checking it should print tracer"
+ "output to the console or to the IDLE shell.\n"
+ "If both the tracers are 'on', the output from the tracer which "
+ "was switched 'on' later, should be printed first\n"
+ "Test for actions like text entry, and removal."
+ }
+
+_replace_dialog_spec = {
+ 'file': 'ReplaceDialog',
+ 'kwds': {},
+ 'msg': "Click the 'Replace' button.\n"
+ "Test various replace options in the 'Replace dialog'.\n"
+ "Click [Close] or [X] to close the 'Replace Dialog'."
+ }
+
+_search_dialog_spec = {
+ 'file': 'SearchDialog',
+ 'kwds': {},
+ 'msg': "Click the 'Search' button.\n"
+ "Test various search options in the 'Search dialog'.\n"
+ "Click [Close] or [X] to close the 'Search Dialog'."
+ }
+
+_scrolled_list_spec = {
+ 'file': 'ScrolledList',
+ 'kwds': {},
+ 'msg': "You should see a scrollable list of items\n"
+ "Selecting (clicking) or double clicking an item "
+ "prints the name to the console or Idle shell.\n"
+ "Right clicking an item will display a popup."
+ }
+
+show_idlehelp_spec = {
+ 'file': 'help',
+ 'kwds': {},
+ 'msg': "If the help text displays, this works.\n"
+ "Text is selectable. Window is scrollable."
+ }
+
+_stack_viewer_spec = {
+ 'file': 'StackViewer',
+ 'kwds': {},
+ 'msg': "A stacktrace for a NameError exception.\n"
+ "Expand 'idlelib ...' and '<locals>'.\n"
+ "Check that exc_value, exc_tb, and exc_type are correct.\n"
+ }
+
+_tabbed_pages_spec = {
+ 'file': 'tabbedpages',
+ 'kwds': {},
+ 'msg': "Toggle between the two tabs 'foo' and 'bar'\n"
+ "Add a tab by entering a suitable name for it.\n"
+ "Remove an existing tab by entering its name.\n"
+ "Remove all existing tabs.\n"
+ "<nothing> is an invalid add page and remove page name.\n"
+ }
+
+TextViewer_spec = {
+ 'file': 'textView',
+ 'kwds': {'title': 'Test textView',
+ 'text':'The quick brown fox jumps over the lazy dog.\n'*35,
+ '_htest': True},
+ 'msg': "Test for read-only property of text.\n"
+ "Text is selectable. Window is scrollable.",
+ }
+
+_tooltip_spec = {
+ 'file': 'ToolTip',
+ 'kwds': {},
+ 'msg': "Place mouse cursor over both the buttons\n"
+ "A tooltip should appear with some text."
+ }
+
+_tree_widget_spec = {
+ 'file': 'TreeWidget',
+ 'kwds': {},
+ 'msg': "The canvas is scrollable.\n"
+ "Click on folders upto to the lowest level."
+ }
+
+_undo_delegator_spec = {
+ 'file': 'UndoDelegator',
+ 'kwds': {},
+ 'msg': "Click [Undo] to undo any action.\n"
+ "Click [Redo] to redo any action.\n"
+ "Click [Dump] to dump the current state "
+ "by printing to the console or the IDLE shell.\n"
+ }
+
+_widget_redirector_spec = {
+ 'file': 'WidgetRedirector',
+ 'kwds': {},
+ 'msg': "Every text insert should be printed to the console."
+ "or the IDLE shell."
+ }
+
+def run(*tests):
+ root = tk.Tk()
+ root.title('IDLE htest')
+ root.resizable(0, 0)
+ _initializeTkVariantTests(root)
+
+ # a scrollable Label like constant width text widget.
+ frameLabel = tk.Frame(root, padx=10)
+ frameLabel.pack()
+ text = tk.Text(frameLabel, wrap='word')
+ text.configure(bg=root.cget('bg'), relief='flat', height=4, width=70)
+ scrollbar = tk.Scrollbar(frameLabel, command=text.yview)
+ text.config(yscrollcommand=scrollbar.set)
+ scrollbar.pack(side='right', fill='y', expand=False)
+ text.pack(side='left', fill='both', expand=True)
+
+ test_list = [] # List of tuples of the form (spec, callable widget)
+ if tests:
+ for test in tests:
+ test_spec = globals()[test.__name__ + '_spec']
+ test_spec['name'] = test.__name__
+ test_list.append((test_spec, test))
+ else:
+ for k, d in globals().items():
+ if k.endswith('_spec'):
+ test_name = k[:-5]
+ test_spec = d
+ test_spec['name'] = test_name
+ mod = import_module('idlelib.' + test_spec['file'])
+ test = getattr(mod, test_name)
+ test_list.append((test_spec, test))
+
+ test_name = [tk.StringVar('')]
+ callable_object = [None]
+ test_kwds = [None]
+
+
+ def next():
+ if len(test_list) == 1:
+ next_button.pack_forget()
+ test_spec, callable_object[0] = test_list.pop()
+ test_kwds[0] = test_spec['kwds']
+ test_kwds[0]['parent'] = root
+ test_name[0].set('Test ' + test_spec['name'])
+
+ text.configure(state='normal') # enable text editing
+ text.delete('1.0','end')
+ text.insert("1.0",test_spec['msg'])
+ text.configure(state='disabled') # preserve read-only property
+
+ def run_test():
+ widget = callable_object[0](**test_kwds[0])
+ try:
+ print(widget.result)
+ except AttributeError:
+ pass
+
+ button = tk.Button(root, textvariable=test_name[0], command=run_test)
+ button.pack()
+ next_button = tk.Button(root, text="Next", command=next)
+ next_button.pack()
+
+ next()
+
+ root.mainloop()
+
+if __name__ == '__main__':
+ run()
diff --git a/lib/python2.7/idlelib/idle_test/mock_idle.py b/lib/python2.7/idlelib/idle_test/mock_idle.py
new file mode 100644
index 0000000..7b09f83
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/mock_idle.py
@@ -0,0 +1,55 @@
+'''Mock classes that imitate idlelib modules or classes.
+
+Attributes and methods will be added as needed for tests.
+'''
+
+from idlelib.idle_test.mock_tk import Text
+
+class Func(object):
+ '''Mock function captures args and returns result set by test.
+
+ Attributes:
+ self.called - records call even if no args, kwds passed.
+ self.result - set by init, returned by call.
+ self.args - captures positional arguments.
+ self.kwds - captures keyword arguments.
+
+ Most common use will probably be to mock methods.
+ Mock_tk.Var and Mbox_func are special variants of this.
+ '''
+ def __init__(self, result=None):
+ self.called = False
+ self.result = result
+ self.args = None
+ self.kwds = None
+ def __call__(self, *args, **kwds):
+ self.called = True
+ self.args = args
+ self.kwds = kwds
+ if isinstance(self.result, BaseException):
+ raise self.result
+ else:
+ return self.result
+
+
+class Editor(object):
+ '''Minimally imitate EditorWindow.EditorWindow class.
+ '''
+ def __init__(self, flist=None, filename=None, key=None, root=None):
+ self.text = Text()
+ self.undo = UndoDelegator()
+
+ def get_selection_indices(self):
+ first = self.text.index('1.0')
+ last = self.text.index('end')
+ return first, last
+
+
+class UndoDelegator(object):
+ '''Minimally imitate UndoDelegator,UndoDelegator class.
+ '''
+ # A real undo block is only needed for user interaction.
+ def undo_block_start(*args):
+ pass
+ def undo_block_stop(*args):
+ pass
diff --git a/lib/python2.7/idlelib/idle_test/mock_tk.py b/lib/python2.7/idlelib/idle_test/mock_tk.py
new file mode 100644
index 0000000..f42a039
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/mock_tk.py
@@ -0,0 +1,298 @@
+"""Classes that replace tkinter gui objects used by an object being tested.
+
+A gui object is anything with a master or parent parameter, which is
+typically required in spite of what the doc strings say.
+"""
+
+class Event(object):
+ '''Minimal mock with attributes for testing event handlers.
+
+ This is not a gui object, but is used as an argument for callbacks
+ that access attributes of the event passed. If a callback ignores
+ the event, other than the fact that is happened, pass 'event'.
+
+ Keyboard, mouse, window, and other sources generate Event instances.
+ Event instances have the following attributes: serial (number of
+ event), time (of event), type (of event as number), widget (in which
+ event occurred), and x,y (position of mouse). There are other
+ attributes for specific events, such as keycode for key events.
+ tkinter.Event.__doc__ has more but is still not complete.
+ '''
+ def __init__(self, **kwds):
+ "Create event with attributes needed for test"
+ self.__dict__.update(kwds)
+
+class Var(object):
+ "Use for String/Int/BooleanVar: incomplete"
+ def __init__(self, master=None, value=None, name=None):
+ self.master = master
+ self.value = value
+ self.name = name
+ def set(self, value):
+ self.value = value
+ def get(self):
+ return self.value
+
+class Mbox_func(object):
+ """Generic mock for messagebox functions, which all have the same signature.
+
+ Instead of displaying a message box, the mock's call method saves the
+ arguments as instance attributes, which test functions can then examime.
+ The test can set the result returned to ask function
+ """
+ def __init__(self, result=None):
+ self.result = result # Return None for all show funcs
+ def __call__(self, title, message, *args, **kwds):
+ # Save all args for possible examination by tester
+ self.title = title
+ self.message = message
+ self.args = args
+ self.kwds = kwds
+ return self.result # Set by tester for ask functions
+
+class Mbox(object):
+ """Mock for tkinter.messagebox with an Mbox_func for each function.
+
+ This module was 'tkMessageBox' in 2.x; hence the 'import as' in 3.x.
+ Example usage in test_module.py for testing functions in module.py:
+ ---
+from idlelib.idle_test.mock_tk import Mbox
+import module
+
+orig_mbox = module.tkMessageBox
+showerror = Mbox.showerror # example, for attribute access in test methods
+
+class Test(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ module.tkMessageBox = Mbox
+
+ @classmethod
+ def tearDownClass(cls):
+ module.tkMessageBox = orig_mbox
+ ---
+ For 'ask' functions, set func.result return value before calling the method
+ that uses the message function. When tkMessageBox functions are the
+ only gui alls in a method, this replacement makes the method gui-free,
+ """
+ askokcancel = Mbox_func() # True or False
+ askquestion = Mbox_func() # 'yes' or 'no'
+ askretrycancel = Mbox_func() # True or False
+ askyesno = Mbox_func() # True or False
+ askyesnocancel = Mbox_func() # True, False, or None
+ showerror = Mbox_func() # None
+ showinfo = Mbox_func() # None
+ showwarning = Mbox_func() # None
+
+from _tkinter import TclError
+
+class Text(object):
+ """A semi-functional non-gui replacement for tkinter.Text text editors.
+
+ The mock's data model is that a text is a list of \n-terminated lines.
+ The mock adds an empty string at the beginning of the list so that the
+ index of actual lines start at 1, as with Tk. The methods never see this.
+ Tk initializes files with a terminal \n that cannot be deleted. It is
+ invisible in the sense that one cannot move the cursor beyond it.
+
+ This class is only tested (and valid) with strings of ascii chars.
+ For testing, we are not concerned with Tk Text's treatment of,
+ for instance, 0-width characters or character + accent.
+ """
+ def __init__(self, master=None, cnf={}, **kw):
+ '''Initialize mock, non-gui, text-only Text widget.
+
+ At present, all args are ignored. Almost all affect visual behavior.
+ There are just a few Text-only options that affect text behavior.
+ '''
+ self.data = ['', '\n']
+
+ def index(self, index):
+ "Return string version of index decoded according to current text."
+ return "%s.%s" % self._decode(index, endflag=1)
+
+ def _decode(self, index, endflag=0):
+ """Return a (line, char) tuple of int indexes into self.data.
+
+ This implements .index without converting the result back to a string.
+ The result is contrained by the number of lines and linelengths of
+ self.data. For many indexes, the result is initially (1, 0).
+
+ The input index may have any of several possible forms:
+ * line.char float: converted to 'line.char' string;
+ * 'line.char' string, where line and char are decimal integers;
+ * 'line.char lineend', where lineend='lineend' (and char is ignored);
+ * 'line.end', where end='end' (same as above);
+ * 'insert', the positions before terminal \n;
+ * 'end', whose meaning depends on the endflag passed to ._endex.
+ * 'sel.first' or 'sel.last', where sel is a tag -- not implemented.
+ """
+ if isinstance(index, (float, bytes)):
+ index = str(index)
+ try:
+ index=index.lower()
+ except AttributeError:
+ raise TclError('bad text index "%s"' % index)
+
+ lastline = len(self.data) - 1 # same as number of text lines
+ if index == 'insert':
+ return lastline, len(self.data[lastline]) - 1
+ elif index == 'end':
+ return self._endex(endflag)
+
+ line, char = index.split('.')
+ line = int(line)
+
+ # Out of bounds line becomes first or last ('end') index
+ if line < 1:
+ return 1, 0
+ elif line > lastline:
+ return self._endex(endflag)
+
+ linelength = len(self.data[line]) -1 # position before/at \n
+ if char.endswith(' lineend') or char == 'end':
+ return line, linelength
+ # Tk requires that ignored chars before ' lineend' be valid int
+
+ # Out of bounds char becomes first or last index of line
+ char = int(char)
+ if char < 0:
+ char = 0
+ elif char > linelength:
+ char = linelength
+ return line, char
+
+ def _endex(self, endflag):
+ '''Return position for 'end' or line overflow corresponding to endflag.
+
+ -1: position before terminal \n; for .insert(), .delete
+ 0: position after terminal \n; for .get, .delete index 1
+ 1: same viewed as beginning of non-existent next line (for .index)
+ '''
+ n = len(self.data)
+ if endflag == 1:
+ return n, 0
+ else:
+ n -= 1
+ return n, len(self.data[n]) + endflag
+
+
+ def insert(self, index, chars):
+ "Insert chars before the character at index."
+
+ if not chars: # ''.splitlines() is [], not ['']
+ return
+ chars = chars.splitlines(True)
+ if chars[-1][-1] == '\n':
+ chars.append('')
+ line, char = self._decode(index, -1)
+ before = self.data[line][:char]
+ after = self.data[line][char:]
+ self.data[line] = before + chars[0]
+ self.data[line+1:line+1] = chars[1:]
+ self.data[line+len(chars)-1] += after
+
+
+ def get(self, index1, index2=None):
+ "Return slice from index1 to index2 (default is 'index1+1')."
+
+ startline, startchar = self._decode(index1)
+ if index2 is None:
+ endline, endchar = startline, startchar+1
+ else:
+ endline, endchar = self._decode(index2)
+
+ if startline == endline:
+ return self.data[startline][startchar:endchar]
+ else:
+ lines = [self.data[startline][startchar:]]
+ for i in range(startline+1, endline):
+ lines.append(self.data[i])
+ lines.append(self.data[endline][:endchar])
+ return ''.join(lines)
+
+
+ def delete(self, index1, index2=None):
+ '''Delete slice from index1 to index2 (default is 'index1+1').
+
+ Adjust default index2 ('index+1) for line ends.
+ Do not delete the terminal \n at the very end of self.data ([-1][-1]).
+ '''
+ startline, startchar = self._decode(index1, -1)
+ if index2 is None:
+ if startchar < len(self.data[startline])-1:
+ # not deleting \n
+ endline, endchar = startline, startchar+1
+ elif startline < len(self.data) - 1:
+ # deleting non-terminal \n, convert 'index1+1 to start of next line
+ endline, endchar = startline+1, 0
+ else:
+ # do not delete terminal \n if index1 == 'insert'
+ return
+ else:
+ endline, endchar = self._decode(index2, -1)
+ # restricting end position to insert position excludes terminal \n
+
+ if startline == endline and startchar < endchar:
+ self.data[startline] = self.data[startline][:startchar] + \
+ self.data[startline][endchar:]
+ elif startline < endline:
+ self.data[startline] = self.data[startline][:startchar] + \
+ self.data[endline][endchar:]
+ startline += 1
+ for i in range(startline, endline+1):
+ del self.data[startline]
+
+ def compare(self, index1, op, index2):
+ line1, char1 = self._decode(index1)
+ line2, char2 = self._decode(index2)
+ if op == '<':
+ return line1 < line2 or line1 == line2 and char1 < char2
+ elif op == '<=':
+ return line1 < line2 or line1 == line2 and char1 <= char2
+ elif op == '>':
+ return line1 > line2 or line1 == line2 and char1 > char2
+ elif op == '>=':
+ return line1 > line2 or line1 == line2 and char1 >= char2
+ elif op == '==':
+ return line1 == line2 and char1 == char2
+ elif op == '!=':
+ return line1 != line2 or char1 != char2
+ else:
+ raise TclError('''bad comparison operator "%s":'''
+ '''must be <, <=, ==, >=, >, or !=''' % op)
+
+ # The following Text methods normally do something and return None.
+ # Whether doing nothing is sufficient for a test will depend on the test.
+
+ def mark_set(self, name, index):
+ "Set mark *name* before the character at index."
+ pass
+
+ def mark_unset(self, *markNames):
+ "Delete all marks in markNames."
+
+ def tag_remove(self, tagName, index1, index2=None):
+ "Remove tag tagName from all characters between index1 and index2."
+ pass
+
+ # The following Text methods affect the graphics screen and return None.
+ # Doing nothing should always be sufficient for tests.
+
+ def scan_dragto(self, x, y):
+ "Adjust the view of the text according to scan_mark"
+
+ def scan_mark(self, x, y):
+ "Remember the current X, Y coordinates."
+
+ def see(self, index):
+ "Scroll screen to make the character at INDEX is visible."
+ pass
+
+ # The following is a Misc method inherited by Text.
+ # It should properly go in a Misc mock, but is included here for now.
+
+ def bind(sequence=None, func=None, add=None):
+ "Bind to this widget at event sequence a call to function func."
+ pass
diff --git a/lib/python2.7/idlelib/idle_test/test_autocomplete.py b/lib/python2.7/idlelib/idle_test/test_autocomplete.py
new file mode 100644
index 0000000..002751e
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_autocomplete.py
@@ -0,0 +1,140 @@
+import unittest
+from test.test_support import requires
+from Tkinter import Tk, Text
+
+import idlelib.AutoComplete as ac
+import idlelib.AutoCompleteWindow as acw
+from idlelib.idle_test.mock_idle import Func
+from idlelib.idle_test.mock_tk import Event
+
+class AutoCompleteWindow:
+ def complete():
+ return
+
+class DummyEditwin:
+ def __init__(self, root, text):
+ self.root = root
+ self.text = text
+ self.indentwidth = 8
+ self.tabwidth = 8
+ self.context_use_ps1 = True
+
+
+class AutoCompleteTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ cls.text = Text(cls.root)
+ cls.editor = DummyEditwin(cls.root, cls.text)
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.editor, cls.text
+ cls.root.destroy()
+ del cls.root
+
+ def setUp(self):
+ self.editor.text.delete('1.0', 'end')
+ self.autocomplete = ac.AutoComplete(self.editor)
+
+ def test_init(self):
+ self.assertEqual(self.autocomplete.editwin, self.editor)
+
+ def test_make_autocomplete_window(self):
+ testwin = self.autocomplete._make_autocomplete_window()
+ self.assertIsInstance(testwin, acw.AutoCompleteWindow)
+
+ def test_remove_autocomplete_window(self):
+ self.autocomplete.autocompletewindow = (
+ self.autocomplete._make_autocomplete_window())
+ self.autocomplete._remove_autocomplete_window()
+ self.assertIsNone(self.autocomplete.autocompletewindow)
+
+ def test_force_open_completions_event(self):
+ # Test that force_open_completions_event calls _open_completions
+ o_cs = Func()
+ self.autocomplete.open_completions = o_cs
+ self.autocomplete.force_open_completions_event('event')
+ self.assertEqual(o_cs.args, (True, False, True))
+
+ def test_try_open_completions_event(self):
+ Equal = self.assertEqual
+ autocomplete = self.autocomplete
+ trycompletions = self.autocomplete.try_open_completions_event
+ o_c_l = Func()
+ autocomplete._open_completions_later = o_c_l
+
+ # _open_completions_later should not be called with no text in editor
+ trycompletions('event')
+ Equal(o_c_l.args, None)
+
+ # _open_completions_later should be called with COMPLETE_ATTRIBUTES (1)
+ self.text.insert('1.0', 're.')
+ trycompletions('event')
+ Equal(o_c_l.args, (False, False, False, 1))
+
+ # _open_completions_later should be called with COMPLETE_FILES (2)
+ self.text.delete('1.0', 'end')
+ self.text.insert('1.0', '"./Lib/')
+ trycompletions('event')
+ Equal(o_c_l.args, (False, False, False, 2))
+
+ def test_autocomplete_event(self):
+ Equal = self.assertEqual
+ autocomplete = self.autocomplete
+
+ # Test that the autocomplete event is ignored if user is pressing a
+ # modifier key in addition to the tab key
+ ev = Event(mc_state=True)
+ self.assertIsNone(autocomplete.autocomplete_event(ev))
+ del ev.mc_state
+
+ # If autocomplete window is open, complete() method is called
+ self.text.insert('1.0', 're.')
+ # This must call autocomplete._make_autocomplete_window()
+ Equal(self.autocomplete.autocomplete_event(ev), 'break')
+
+ # If autocomplete window is not active or does not exist,
+ # open_completions is called. Return depends on its return.
+ autocomplete._remove_autocomplete_window()
+ o_cs = Func() # .result = None
+ autocomplete.open_completions = o_cs
+ Equal(self.autocomplete.autocomplete_event(ev), None)
+ Equal(o_cs.args, (False, True, True))
+ o_cs.result = True
+ Equal(self.autocomplete.autocomplete_event(ev), 'break')
+ Equal(o_cs.args, (False, True, True))
+
+ def test_open_completions_later(self):
+ # Test that autocomplete._delayed_completion_id is set
+ pass
+
+ def test_delayed_open_completions(self):
+ # Test that autocomplete._delayed_completion_id set to None and that
+ # open_completions only called if insertion index is the same as
+ # _delayed_completion_index
+ pass
+
+ def test_open_completions(self):
+ # Test completions of files and attributes as well as non-completion
+ # of errors
+ pass
+
+ def test_fetch_completions(self):
+ # Test that fetch_completions returns 2 lists:
+ # For attribute completion, a large list containing all variables, and
+ # a small list containing non-private variables.
+ # For file completion, a large list containing all files in the path,
+ # and a small list containing files that do not start with '.'
+ pass
+
+ def test_get_entity(self):
+ # Test that a name is in the namespace of sys.modules and
+ # __main__.__dict__
+ pass
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/lib/python2.7/idlelib/idle_test/test_autoexpand.py b/lib/python2.7/idlelib/idle_test/test_autoexpand.py
new file mode 100644
index 0000000..6be4fbf
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_autoexpand.py
@@ -0,0 +1,141 @@
+"""Unit tests for idlelib.AutoExpand"""
+import unittest
+from test.test_support import requires
+from Tkinter import Text, Tk
+#from idlelib.idle_test.mock_tk import Text
+from idlelib.AutoExpand import AutoExpand
+
+
+class Dummy_Editwin:
+ # AutoExpand.__init__ only needs .text
+ def __init__(self, text):
+ self.text = text
+
+class AutoExpandTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ if 'Tkinter' in str(Text):
+ requires('gui')
+ cls.tk = Tk()
+ cls.text = Text(cls.tk)
+ else:
+ cls.text = Text()
+ cls.auto_expand = AutoExpand(Dummy_Editwin(cls.text))
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.text, cls.auto_expand
+ if hasattr(cls, 'tk'):
+ cls.tk.destroy()
+ del cls.tk
+
+ def tearDown(self):
+ self.text.delete('1.0', 'end')
+
+ def test_get_prevword(self):
+ text = self.text
+ previous = self.auto_expand.getprevword
+ equal = self.assertEqual
+
+ equal(previous(), '')
+
+ text.insert('insert', 't')
+ equal(previous(), 't')
+
+ text.insert('insert', 'his')
+ equal(previous(), 'this')
+
+ text.insert('insert', ' ')
+ equal(previous(), '')
+
+ text.insert('insert', 'is')
+ equal(previous(), 'is')
+
+ text.insert('insert', '\nsample\nstring')
+ equal(previous(), 'string')
+
+ text.delete('3.0', 'insert')
+ equal(previous(), '')
+
+ text.delete('1.0', 'end')
+ equal(previous(), '')
+
+ def test_before_only(self):
+ previous = self.auto_expand.getprevword
+ expand = self.auto_expand.expand_word_event
+ equal = self.assertEqual
+
+ self.text.insert('insert', 'ab ac bx ad ab a')
+ equal(self.auto_expand.getwords(), ['ab', 'ad', 'ac', 'a'])
+ expand('event')
+ equal(previous(), 'ab')
+ expand('event')
+ equal(previous(), 'ad')
+ expand('event')
+ equal(previous(), 'ac')
+ expand('event')
+ equal(previous(), 'a')
+
+ def test_after_only(self):
+ # Also add punctuation 'noise' that shoud be ignored.
+ text = self.text
+ previous = self.auto_expand.getprevword
+ expand = self.auto_expand.expand_word_event
+ equal = self.assertEqual
+
+ text.insert('insert', 'a, [ab] ac: () bx"" cd ac= ad ya')
+ text.mark_set('insert', '1.1')
+ equal(self.auto_expand.getwords(), ['ab', 'ac', 'ad', 'a'])
+ expand('event')
+ equal(previous(), 'ab')
+ expand('event')
+ equal(previous(), 'ac')
+ expand('event')
+ equal(previous(), 'ad')
+ expand('event')
+ equal(previous(), 'a')
+
+ def test_both_before_after(self):
+ text = self.text
+ previous = self.auto_expand.getprevword
+ expand = self.auto_expand.expand_word_event
+ equal = self.assertEqual
+
+ text.insert('insert', 'ab xy yz\n')
+ text.insert('insert', 'a ac by ac')
+
+ text.mark_set('insert', '2.1')
+ equal(self.auto_expand.getwords(), ['ab', 'ac', 'a'])
+ expand('event')
+ equal(previous(), 'ab')
+ expand('event')
+ equal(previous(), 'ac')
+ expand('event')
+ equal(previous(), 'a')
+
+ def test_other_expand_cases(self):
+ text = self.text
+ expand = self.auto_expand.expand_word_event
+ equal = self.assertEqual
+
+ # no expansion candidate found
+ equal(self.auto_expand.getwords(), [])
+ equal(expand('event'), 'break')
+
+ text.insert('insert', 'bx cy dz a')
+ equal(self.auto_expand.getwords(), [])
+
+ # reset state by successfully expanding once
+ # move cursor to another position and expand again
+ text.insert('insert', 'ac xy a ac ad a')
+ text.mark_set('insert', '1.7')
+ expand('event')
+ initial_state = self.auto_expand.state
+ text.mark_set('insert', '1.end')
+ expand('event')
+ new_state = self.auto_expand.state
+ self.assertNotEqual(initial_state, new_state)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/lib/python2.7/idlelib/idle_test/test_calltips.py b/lib/python2.7/idlelib/idle_test/test_calltips.py
new file mode 100644
index 0000000..147119c
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_calltips.py
@@ -0,0 +1,185 @@
+import unittest
+import idlelib.CallTips as ct
+CTi = ct.CallTips() # needed for get_entity test in 2.7
+import textwrap
+import types
+import warnings
+
+default_tip = ''
+
+# Test Class TC is used in multiple get_argspec test methods
+class TC(object):
+ 'doc'
+ tip = "(ai=None, *args)"
+ def __init__(self, ai=None, *b): 'doc'
+ __init__.tip = "(self, ai=None, *args)"
+ def t1(self): 'doc'
+ t1.tip = "(self)"
+ def t2(self, ai, b=None): 'doc'
+ t2.tip = "(self, ai, b=None)"
+ def t3(self, ai, *args): 'doc'
+ t3.tip = "(self, ai, *args)"
+ def t4(self, *args): 'doc'
+ t4.tip = "(self, *args)"
+ def t5(self, ai, b=None, *args, **kw): 'doc'
+ t5.tip = "(self, ai, b=None, *args, **kwargs)"
+ def t6(no, self): 'doc'
+ t6.tip = "(no, self)"
+ def __call__(self, ci): 'doc'
+ __call__.tip = "(self, ci)"
+ # attaching .tip to wrapped methods does not work
+ @classmethod
+ def cm(cls, a): 'doc'
+ @staticmethod
+ def sm(b): 'doc'
+
+tc = TC()
+
+signature = ct.get_arg_text # 2.7 and 3.x use different functions
+class Get_signatureTest(unittest.TestCase):
+ # The signature function must return a string, even if blank.
+ # Test a variety of objects to be sure that none cause it to raise
+ # (quite aside from getting as correct an answer as possible).
+ # The tests of builtins may break if the docstrings change,
+ # but a red buildbot is better than a user crash (as has happened).
+ # For a simple mismatch, change the expected output to the actual.
+
+ def test_builtins(self):
+ # 2.7 puts '()\n' where 3.x does not, other minor differences
+
+ # Python class that inherits builtin methods
+ class List(list): "List() doc"
+ # Simulate builtin with no docstring for default argspec test
+ class SB: __call__ = None
+
+ def gtest(obj, out):
+ self.assertEqual(signature(obj), out)
+
+ if List.__doc__ is not None:
+ gtest(List, '()\n' + List.__doc__)
+ gtest(list.__new__,
+ 'T.__new__(S, ...) -> a new object with type S, a subtype of T')
+ gtest(list.__init__,
+ 'x.__init__(...) initializes x; see help(type(x)) for signature')
+ append_doc = "L.append(object) -- append object to end"
+ gtest(list.append, append_doc)
+ gtest([].append, append_doc)
+ gtest(List.append, append_doc)
+
+ gtest(types.MethodType, '()\ninstancemethod(function, instance, class)')
+ gtest(SB(), default_tip)
+
+ def test_signature_wrap(self):
+ # This is also a test of an old-style class
+ if textwrap.TextWrapper.__doc__ is not None:
+ self.assertEqual(signature(textwrap.TextWrapper), '''\
+(width=70, initial_indent='', subsequent_indent='', expand_tabs=True,
+ replace_whitespace=True, fix_sentence_endings=False, break_long_words=True,
+ drop_whitespace=True, break_on_hyphens=True)''')
+
+ def test_docline_truncation(self):
+ def f(): pass
+ f.__doc__ = 'a'*300
+ self.assertEqual(signature(f), '()\n' + 'a' * (ct._MAX_COLS-3) + '...')
+
+ def test_multiline_docstring(self):
+ # Test fewer lines than max.
+ self.assertEqual(signature(list),
+ "()\nlist() -> new empty list\n"
+ "list(iterable) -> new list initialized from iterable's items")
+
+ # Test max lines and line (currently) too long.
+ def f():
+ pass
+ s = 'a\nb\nc\nd\n'
+ f.__doc__ = s + 300 * 'e' + 'f'
+ self.assertEqual(signature(f),
+ '()\n' + s + (ct._MAX_COLS - 3) * 'e' + '...')
+
+ def test_functions(self):
+ def t1(): 'doc'
+ t1.tip = "()"
+ def t2(a, b=None): 'doc'
+ t2.tip = "(a, b=None)"
+ def t3(a, *args): 'doc'
+ t3.tip = "(a, *args)"
+ def t4(*args): 'doc'
+ t4.tip = "(*args)"
+ def t5(a, b=None, *args, **kwds): 'doc'
+ t5.tip = "(a, b=None, *args, **kwargs)"
+
+ doc = '\ndoc' if t1.__doc__ is not None else ''
+ for func in (t1, t2, t3, t4, t5, TC):
+ self.assertEqual(signature(func), func.tip + doc)
+
+ def test_methods(self):
+ doc = '\ndoc' if TC.__doc__ is not None else ''
+ for meth in (TC.t1, TC.t2, TC.t3, TC.t4, TC.t5, TC.t6, TC.__call__):
+ self.assertEqual(signature(meth), meth.tip + doc)
+ self.assertEqual(signature(TC.cm), "(a)" + doc)
+ self.assertEqual(signature(TC.sm), "(b)" + doc)
+
+ def test_bound_methods(self):
+ # test that first parameter is correctly removed from argspec
+ doc = '\ndoc' if TC.__doc__ is not None else ''
+ for meth, mtip in ((tc.t1, "()"), (tc.t4, "(*args)"), (tc.t6, "(self)"),
+ (tc.__call__, '(ci)'), (tc, '(ci)'), (TC.cm, "(a)"),):
+ self.assertEqual(signature(meth), mtip + doc)
+
+ def test_starred_parameter(self):
+ # test that starred first parameter is *not* removed from argspec
+ class C:
+ def m1(*args): pass
+ def m2(**kwds): pass
+ def f1(args, kwargs, *a, **k): pass
+ def f2(args, kwargs, args1, kwargs1, *a, **k): pass
+ c = C()
+ self.assertEqual(signature(C.m1), '(*args)')
+ self.assertEqual(signature(c.m1), '(*args)')
+ self.assertEqual(signature(C.m2), '(**kwargs)')
+ self.assertEqual(signature(c.m2), '(**kwargs)')
+ self.assertEqual(signature(f1), '(args, kwargs, *args1, **kwargs1)')
+ self.assertEqual(signature(f2),
+ '(args, kwargs, args1, kwargs1, *args2, **kwargs2)')
+
+ def test_no_docstring(self):
+ def nd(s): pass
+ TC.nd = nd
+ self.assertEqual(signature(nd), "(s)")
+ self.assertEqual(signature(TC.nd), "(s)")
+ self.assertEqual(signature(tc.nd), "()")
+
+ def test_attribute_exception(self):
+ class NoCall(object):
+ def __getattr__(self, name):
+ raise BaseException
+ class Call(NoCall):
+ def __call__(self, ci):
+ pass
+ for meth, mtip in ((NoCall, '()'), (Call, '()'),
+ (NoCall(), ''), (Call(), '(ci)')):
+ self.assertEqual(signature(meth), mtip)
+
+ def test_non_callables(self):
+ for obj in (0, 0.0, '0', b'0', [], {}):
+ self.assertEqual(signature(obj), '')
+
+class Get_entityTest(unittest.TestCase):
+ # In 3.x, get_entity changed from 'instance method' to module function
+ # since 'self' not used. Use dummy instance until change 2.7 also.
+ def test_bad_entity(self):
+ self.assertIsNone(CTi.get_entity('1//0'))
+ def test_good_entity(self):
+ self.assertIs(CTi.get_entity('int'), int)
+
+class Py2Test(unittest.TestCase):
+ def test_paramtuple_float(self):
+ # 18539: (a,b) becomes '.0' in code object; change that but not 0.0
+ with warnings.catch_warnings():
+ # Suppess message of py3 deprecation of parameter unpacking
+ warnings.simplefilter("ignore")
+ exec "def f((a,b), c=0.0): pass"
+ self.assertEqual(signature(f), '(<tuple>, c=0.0)')
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=False)
diff --git a/lib/python2.7/idlelib/idle_test/test_config_name.py b/lib/python2.7/idlelib/idle_test/test_config_name.py
new file mode 100644
index 0000000..4403f87
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_config_name.py
@@ -0,0 +1,75 @@
+"""Unit tests for idlelib.configSectionNameDialog"""
+import unittest
+from idlelib.idle_test.mock_tk import Var, Mbox
+from idlelib import configSectionNameDialog as name_dialog_module
+
+name_dialog = name_dialog_module.GetCfgSectionNameDialog
+
+class Dummy_name_dialog(object):
+ # Mock for testing the following methods of name_dialog
+ name_ok = name_dialog.name_ok.im_func
+ Ok = name_dialog.Ok.im_func
+ Cancel = name_dialog.Cancel.im_func
+ # Attributes, constant or variable, needed for tests
+ used_names = ['used']
+ name = Var()
+ result = None
+ destroyed = False
+ def destroy(self):
+ self.destroyed = True
+
+# name_ok calls Mbox.showerror if name is not ok
+orig_mbox = name_dialog_module.tkMessageBox
+showerror = Mbox.showerror
+
+class ConfigNameTest(unittest.TestCase):
+ dialog = Dummy_name_dialog()
+
+ @classmethod
+ def setUpClass(cls):
+ name_dialog_module.tkMessageBox = Mbox
+
+ @classmethod
+ def tearDownClass(cls):
+ name_dialog_module.tkMessageBox = orig_mbox
+
+ def test_blank_name(self):
+ self.dialog.name.set(' ')
+ self.assertEqual(self.dialog.name_ok(), '')
+ self.assertEqual(showerror.title, 'Name Error')
+ self.assertIn('No', showerror.message)
+
+ def test_used_name(self):
+ self.dialog.name.set('used')
+ self.assertEqual(self.dialog.name_ok(), '')
+ self.assertEqual(showerror.title, 'Name Error')
+ self.assertIn('use', showerror.message)
+
+ def test_long_name(self):
+ self.dialog.name.set('good'*8)
+ self.assertEqual(self.dialog.name_ok(), '')
+ self.assertEqual(showerror.title, 'Name Error')
+ self.assertIn('too long', showerror.message)
+
+ def test_good_name(self):
+ self.dialog.name.set(' good ')
+ showerror.title = 'No Error' # should not be called
+ self.assertEqual(self.dialog.name_ok(), 'good')
+ self.assertEqual(showerror.title, 'No Error')
+
+ def test_ok(self):
+ self.dialog.destroyed = False
+ self.dialog.name.set('good')
+ self.dialog.Ok()
+ self.assertEqual(self.dialog.result, 'good')
+ self.assertTrue(self.dialog.destroyed)
+
+ def test_cancel(self):
+ self.dialog.destroyed = False
+ self.dialog.Cancel()
+ self.assertEqual(self.dialog.result, '')
+ self.assertTrue(self.dialog.destroyed)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=False)
diff --git a/lib/python2.7/idlelib/idle_test/test_configdialog.py b/lib/python2.7/idlelib/idle_test/test_configdialog.py
new file mode 100644
index 0000000..ba65100
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_configdialog.py
@@ -0,0 +1,33 @@
+'''Unittests for idlelib/configHandler.py
+
+Coverage: 46% just by creating dialog. The other half is change code.
+
+'''
+import unittest
+from test.test_support import requires
+from Tkinter import Tk
+from idlelib.configDialog import ConfigDialog
+from idlelib.macosxSupport import _initializeTkVariantTests
+
+
+class ConfigDialogTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ cls.root.withdraw()
+ _initializeTkVariantTests(cls.root)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.root
+
+ def test_dialog(self):
+ d = ConfigDialog(self.root, 'Test', _utest=True)
+ d.remove_var_callbacks()
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/lib/python2.7/idlelib/idle_test/test_delegator.py b/lib/python2.7/idlelib/idle_test/test_delegator.py
new file mode 100644
index 0000000..b8ae5ee
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_delegator.py
@@ -0,0 +1,37 @@
+import unittest
+from idlelib.Delegator import Delegator
+
+class DelegatorTest(unittest.TestCase):
+
+ def test_mydel(self):
+ # test a simple use scenario
+
+ # initialize
+ mydel = Delegator(int)
+ self.assertIs(mydel.delegate, int)
+ self.assertEqual(mydel._Delegator__cache, set())
+
+ # add an attribute:
+ self.assertRaises(AttributeError, mydel.__getattr__, 'xyz')
+ bl = mydel.bit_length
+ self.assertIs(bl, int.bit_length)
+ self.assertIs(mydel.__dict__['bit_length'], int.bit_length)
+ self.assertEqual(mydel._Delegator__cache, {'bit_length'})
+
+ # add a second attribute
+ mydel.numerator
+ self.assertEqual(mydel._Delegator__cache, {'bit_length', 'numerator'})
+
+ # delete the second (which, however, leaves it in the name cache)
+ del mydel.numerator
+ self.assertNotIn('numerator', mydel.__dict__)
+ self.assertIn('numerator', mydel._Delegator__cache)
+
+ # reset by calling .setdelegate, which calls .resetcache
+ mydel.setdelegate(float)
+ self.assertIs(mydel.delegate, float)
+ self.assertNotIn('bit_length', mydel.__dict__)
+ self.assertEqual(mydel._Delegator__cache, set())
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=2)
diff --git a/lib/python2.7/idlelib/idle_test/test_editmenu.py b/lib/python2.7/idlelib/idle_test/test_editmenu.py
new file mode 100644
index 0000000..51d5c16
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_editmenu.py
@@ -0,0 +1,101 @@
+'''Test (selected) IDLE Edit menu items.
+
+Edit modules have their own test files files
+'''
+from test.test_support import requires
+import Tkinter as tk
+import unittest
+from idlelib import PyShell
+
+
+class PasteTest(unittest.TestCase):
+ '''Test pasting into widgets that allow pasting.
+
+ On X11, replacing selections requires tk fix.
+ '''
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = root = tk.Tk()
+ root.withdraw()
+ PyShell.fix_x11_paste(root)
+ cls.text = tk.Text(root)
+ cls.entry = tk.Entry(root)
+ cls.spin = tk.Spinbox(root)
+ root.clipboard_clear()
+ root.clipboard_append('two')
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.text, cls.entry, cls.spin
+ cls.root.clipboard_clear()
+ cls.root.update_idletasks()
+ cls.root.update()
+ cls.root.destroy()
+ del cls.root
+
+ def test_paste_text_no_selection(self):
+ "Test pasting into text without a selection."
+ text = self.text
+ tag, ans = '', 'onetwo\n'
+ text.delete('1.0', 'end')
+ text.insert('1.0', 'one', tag)
+ text.event_generate('<<Paste>>')
+ self.assertEqual(text.get('1.0', 'end'), ans)
+
+ def test_paste_text_selection(self):
+ "Test pasting into text with a selection."
+ text = self.text
+ tag, ans = 'sel', 'two\n'
+ text.delete('1.0', 'end')
+ text.insert('1.0', 'one', tag)
+ text.event_generate('<<Paste>>')
+ self.assertEqual(text.get('1.0', 'end'), ans)
+
+ def test_paste_entry_no_selection(self):
+ "Test pasting into an entry without a selection."
+ # On 3.6, generated <<Paste>> fails without empty select range
+ # for 'no selection'. Live widget works fine.
+ entry = self.entry
+ end, ans = 0, 'onetwo'
+ entry.delete(0, 'end')
+ entry.insert(0, 'one')
+ entry.select_range(0, end) # see note
+ entry.event_generate('<<Paste>>')
+ self.assertEqual(entry.get(), ans)
+
+ def test_paste_entry_selection(self):
+ "Test pasting into an entry with a selection."
+ entry = self.entry
+ end, ans = 'end', 'two'
+ entry.delete(0, 'end')
+ entry.insert(0, 'one')
+ entry.select_range(0, end)
+ entry.event_generate('<<Paste>>')
+ self.assertEqual(entry.get(), ans)
+
+ def test_paste_spin_no_selection(self):
+ "Test pasting into a spinbox without a selection."
+ # See note above for entry.
+ spin = self.spin
+ end, ans = 0, 'onetwo'
+ spin.delete(0, 'end')
+ spin.insert(0, 'one')
+ spin.selection('range', 0, end) # see note
+ spin.event_generate('<<Paste>>')
+ self.assertEqual(spin.get(), ans)
+
+ def test_paste_spin_selection(self):
+ "Test pasting into a spinbox with a selection."
+ spin = self.spin
+ end, ans = 'end', 'two'
+ spin.delete(0, 'end')
+ spin.insert(0, 'one')
+ spin.selection('range', 0, end)
+ spin.event_generate('<<Paste>>')
+ self.assertEqual(spin.get(), ans)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/lib/python2.7/idlelib/idle_test/test_formatparagraph.py b/lib/python2.7/idlelib/idle_test/test_formatparagraph.py
new file mode 100644
index 0000000..068ae38
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_formatparagraph.py
@@ -0,0 +1,376 @@
+# Test the functions and main class method of FormatParagraph.py
+import unittest
+from idlelib import FormatParagraph as fp
+from idlelib.EditorWindow import EditorWindow
+from Tkinter import Tk, Text
+from test.test_support import requires
+
+
+class Is_Get_Test(unittest.TestCase):
+ """Test the is_ and get_ functions"""
+ test_comment = '# This is a comment'
+ test_nocomment = 'This is not a comment'
+ trailingws_comment = '# This is a comment '
+ leadingws_comment = ' # This is a comment'
+ leadingws_nocomment = ' This is not a comment'
+
+ def test_is_all_white(self):
+ self.assertTrue(fp.is_all_white(''))
+ self.assertTrue(fp.is_all_white('\t\n\r\f\v'))
+ self.assertFalse(fp.is_all_white(self.test_comment))
+
+ def test_get_indent(self):
+ Equal = self.assertEqual
+ Equal(fp.get_indent(self.test_comment), '')
+ Equal(fp.get_indent(self.trailingws_comment), '')
+ Equal(fp.get_indent(self.leadingws_comment), ' ')
+ Equal(fp.get_indent(self.leadingws_nocomment), ' ')
+
+ def test_get_comment_header(self):
+ Equal = self.assertEqual
+ # Test comment strings
+ Equal(fp.get_comment_header(self.test_comment), '#')
+ Equal(fp.get_comment_header(self.trailingws_comment), '#')
+ Equal(fp.get_comment_header(self.leadingws_comment), ' #')
+ # Test non-comment strings
+ Equal(fp.get_comment_header(self.leadingws_nocomment), ' ')
+ Equal(fp.get_comment_header(self.test_nocomment), '')
+
+
+class FindTest(unittest.TestCase):
+ """Test the find_paragraph function in FormatParagraph.
+
+ Using the runcase() function, find_paragraph() is called with 'mark' set at
+ multiple indexes before and inside the test paragraph.
+
+ It appears that code with the same indentation as a quoted string is grouped
+ as part of the same paragraph, which is probably incorrect behavior.
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ from idlelib.idle_test.mock_tk import Text
+ cls.text = Text()
+
+ def runcase(self, inserttext, stopline, expected):
+ # Check that find_paragraph returns the expected paragraph when
+ # the mark index is set to beginning, middle, end of each line
+ # up to but not including the stop line
+ text = self.text
+ text.insert('1.0', inserttext)
+ for line in range(1, stopline):
+ linelength = int(text.index("%d.end" % line).split('.')[1])
+ for col in (0, linelength//2, linelength):
+ tempindex = "%d.%d" % (line, col)
+ self.assertEqual(fp.find_paragraph(text, tempindex), expected)
+ text.delete('1.0', 'end')
+
+ def test_find_comment(self):
+ comment = (
+ "# Comment block with no blank lines before\n"
+ "# Comment line\n"
+ "\n")
+ self.runcase(comment, 3, ('1.0', '3.0', '#', comment[0:58]))
+
+ comment = (
+ "\n"
+ "# Comment block with whitespace line before and after\n"
+ "# Comment line\n"
+ "\n")
+ self.runcase(comment, 4, ('2.0', '4.0', '#', comment[1:70]))
+
+ comment = (
+ "\n"
+ " # Indented comment block with whitespace before and after\n"
+ " # Comment line\n"
+ "\n")
+ self.runcase(comment, 4, ('2.0', '4.0', ' #', comment[1:82]))
+
+ comment = (
+ "\n"
+ "# Single line comment\n"
+ "\n")
+ self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:23]))
+
+ comment = (
+ "\n"
+ " # Single line comment with leading whitespace\n"
+ "\n")
+ self.runcase(comment, 3, ('2.0', '3.0', ' #', comment[1:51]))
+
+ comment = (
+ "\n"
+ "# Comment immediately followed by code\n"
+ "x = 42\n"
+ "\n")
+ self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:40]))
+
+ comment = (
+ "\n"
+ " # Indented comment immediately followed by code\n"
+ "x = 42\n"
+ "\n")
+ self.runcase(comment, 3, ('2.0', '3.0', ' #', comment[1:53]))
+
+ comment = (
+ "\n"
+ "# Comment immediately followed by indented code\n"
+ " x = 42\n"
+ "\n")
+ self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:49]))
+
+ def test_find_paragraph(self):
+ teststring = (
+ '"""String with no blank lines before\n'
+ 'String line\n'
+ '"""\n'
+ '\n')
+ self.runcase(teststring, 4, ('1.0', '4.0', '', teststring[0:53]))
+
+ teststring = (
+ "\n"
+ '"""String with whitespace line before and after\n'
+ 'String line.\n'
+ '"""\n'
+ '\n')
+ self.runcase(teststring, 5, ('2.0', '5.0', '', teststring[1:66]))
+
+ teststring = (
+ '\n'
+ ' """Indented string with whitespace before and after\n'
+ ' Comment string.\n'
+ ' """\n'
+ '\n')
+ self.runcase(teststring, 5, ('2.0', '5.0', ' ', teststring[1:85]))
+
+ teststring = (
+ '\n'
+ '"""Single line string."""\n'
+ '\n')
+ self.runcase(teststring, 3, ('2.0', '3.0', '', teststring[1:27]))
+
+ teststring = (
+ '\n'
+ ' """Single line string with leading whitespace."""\n'
+ '\n')
+ self.runcase(teststring, 3, ('2.0', '3.0', ' ', teststring[1:55]))
+
+
+class ReformatFunctionTest(unittest.TestCase):
+ """Test the reformat_paragraph function without the editor window."""
+
+ def test_reformat_paragraph(self):
+ Equal = self.assertEqual
+ reform = fp.reformat_paragraph
+ hw = "O hello world"
+ Equal(reform(' ', 1), ' ')
+ Equal(reform("Hello world", 20), "Hello world")
+
+ # Test without leading newline
+ Equal(reform(hw, 1), "O\nhello\nworld")
+ Equal(reform(hw, 6), "O\nhello\nworld")
+ Equal(reform(hw, 7), "O hello\nworld")
+ Equal(reform(hw, 12), "O hello\nworld")
+ Equal(reform(hw, 13), "O hello world")
+
+ # Test with leading newline
+ hw = "\nO hello world"
+ Equal(reform(hw, 1), "\nO\nhello\nworld")
+ Equal(reform(hw, 6), "\nO\nhello\nworld")
+ Equal(reform(hw, 7), "\nO hello\nworld")
+ Equal(reform(hw, 12), "\nO hello\nworld")
+ Equal(reform(hw, 13), "\nO hello world")
+
+
+class ReformatCommentTest(unittest.TestCase):
+ """Test the reformat_comment function without the editor window."""
+
+ def test_reformat_comment(self):
+ Equal = self.assertEqual
+
+ # reformat_comment formats to a minimum of 20 characters
+ test_string = (
+ " \"\"\"this is a test of a reformat for a triple quoted string"
+ " will it reformat to less than 70 characters for me?\"\"\"")
+ result = fp.reformat_comment(test_string, 70, " ")
+ expected = (
+ " \"\"\"this is a test of a reformat for a triple quoted string will it\n"
+ " reformat to less than 70 characters for me?\"\"\"")
+ Equal(result, expected)
+
+ test_comment = (
+ "# this is a test of a reformat for a triple quoted string will "
+ "it reformat to less than 70 characters for me?")
+ result = fp.reformat_comment(test_comment, 70, "#")
+ expected = (
+ "# this is a test of a reformat for a triple quoted string will it\n"
+ "# reformat to less than 70 characters for me?")
+ Equal(result, expected)
+
+
+class FormatClassTest(unittest.TestCase):
+ def test_init_close(self):
+ instance = fp.FormatParagraph('editor')
+ self.assertEqual(instance.editwin, 'editor')
+ instance.close()
+ self.assertEqual(instance.editwin, None)
+
+
+# For testing format_paragraph_event, Initialize FormatParagraph with
+# a mock Editor with .text and .get_selection_indices. The text must
+# be a Text wrapper that adds two methods
+
+# A real EditorWindow creates unneeded, time-consuming baggage and
+# sometimes emits shutdown warnings like this:
+# "warning: callback failed in WindowList <class '_tkinter.TclError'>
+# : invalid command name ".55131368.windows".
+# Calling EditorWindow._close in tearDownClass prevents this but causes
+# other problems (windows left open).
+
+class TextWrapper:
+ def __init__(self, master):
+ self.text = Text(master=master)
+ def __getattr__(self, name):
+ return getattr(self.text, name)
+ def undo_block_start(self): pass
+ def undo_block_stop(self): pass
+
+class Editor:
+ def __init__(self, root):
+ self.text = TextWrapper(root)
+ get_selection_indices = EditorWindow. get_selection_indices.im_func
+
+class FormatEventTest(unittest.TestCase):
+ """Test the formatting of text inside a Text widget.
+
+ This is done with FormatParagraph.format.paragraph_event,
+ which calls functions in the module as appropriate.
+ """
+ test_string = (
+ " '''this is a test of a reformat for a triple "
+ "quoted string will it reformat to less than 70 "
+ "characters for me?'''\n")
+ multiline_test_string = (
+ " '''The first line is under the max width.\n"
+ " The second line's length is way over the max width. It goes "
+ "on and on until it is over 100 characters long.\n"
+ " Same thing with the third line. It is also way over the max "
+ "width, but FormatParagraph will fix it.\n"
+ " '''\n")
+ multiline_test_comment = (
+ "# The first line is under the max width.\n"
+ "# The second line's length is way over the max width. It goes on "
+ "and on until it is over 100 characters long.\n"
+ "# Same thing with the third line. It is also way over the max "
+ "width, but FormatParagraph will fix it.\n"
+ "# The fourth line is short like the first line.")
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ editor = Editor(root=cls.root)
+ cls.text = editor.text.text # Test code does not need the wrapper.
+ cls.formatter = fp.FormatParagraph(editor).format_paragraph_event
+ # Sets the insert mark just after the re-wrapped and inserted text.
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.text, cls.formatter
+ cls.root.destroy()
+ del cls.root
+
+ def test_short_line(self):
+ self.text.insert('1.0', "Short line\n")
+ self.formatter("Dummy")
+ self.assertEqual(self.text.get('1.0', 'insert'), "Short line\n" )
+ self.text.delete('1.0', 'end')
+
+ def test_long_line(self):
+ text = self.text
+
+ # Set cursor ('insert' mark) to '1.0', within text.
+ text.insert('1.0', self.test_string)
+ text.mark_set('insert', '1.0')
+ self.formatter('ParameterDoesNothing', limit=70)
+ result = text.get('1.0', 'insert')
+ # find function includes \n
+ expected = (
+" '''this is a test of a reformat for a triple quoted string will it\n"
+" reformat to less than 70 characters for me?'''\n") # yes
+ self.assertEqual(result, expected)
+ text.delete('1.0', 'end')
+
+ # Select from 1.11 to line end.
+ text.insert('1.0', self.test_string)
+ text.tag_add('sel', '1.11', '1.end')
+ self.formatter('ParameterDoesNothing', limit=70)
+ result = text.get('1.0', 'insert')
+ # selection excludes \n
+ expected = (
+" '''this is a test of a reformat for a triple quoted string will it reformat\n"
+" to less than 70 characters for me?'''") # no
+ self.assertEqual(result, expected)
+ text.delete('1.0', 'end')
+
+ def test_multiple_lines(self):
+ text = self.text
+ # Select 2 long lines.
+ text.insert('1.0', self.multiline_test_string)
+ text.tag_add('sel', '2.0', '4.0')
+ self.formatter('ParameterDoesNothing', limit=70)
+ result = text.get('2.0', 'insert')
+ expected = (
+" The second line's length is way over the max width. It goes on and\n"
+" on until it is over 100 characters long. Same thing with the third\n"
+" line. It is also way over the max width, but FormatParagraph will\n"
+" fix it.\n")
+ self.assertEqual(result, expected)
+ text.delete('1.0', 'end')
+
+ def test_comment_block(self):
+ text = self.text
+
+ # Set cursor ('insert') to '1.0', within block.
+ text.insert('1.0', self.multiline_test_comment)
+ self.formatter('ParameterDoesNothing', limit=70)
+ result = text.get('1.0', 'insert')
+ expected = (
+"# The first line is under the max width. The second line's length is\n"
+"# way over the max width. It goes on and on until it is over 100\n"
+"# characters long. Same thing with the third line. It is also way over\n"
+"# the max width, but FormatParagraph will fix it. The fourth line is\n"
+"# short like the first line.\n")
+ self.assertEqual(result, expected)
+ text.delete('1.0', 'end')
+
+ # Select line 2, verify line 1 unaffected.
+ text.insert('1.0', self.multiline_test_comment)
+ text.tag_add('sel', '2.0', '3.0')
+ self.formatter('ParameterDoesNothing', limit=70)
+ result = text.get('1.0', 'insert')
+ expected = (
+"# The first line is under the max width.\n"
+"# The second line's length is way over the max width. It goes on and\n"
+"# on until it is over 100 characters long.\n")
+ self.assertEqual(result, expected)
+ text.delete('1.0', 'end')
+
+# The following block worked with EditorWindow but fails with the mock.
+# Lines 2 and 3 get pasted together even though the previous block left
+# the previous line alone. More investigation is needed.
+## # Select lines 3 and 4
+## text.insert('1.0', self.multiline_test_comment)
+## text.tag_add('sel', '3.0', '5.0')
+## self.formatter('ParameterDoesNothing')
+## result = text.get('3.0', 'insert')
+## expected = (
+##"# Same thing with the third line. It is also way over the max width,\n"
+##"# but FormatParagraph will fix it. The fourth line is short like the\n"
+##"# first line.\n")
+## self.assertEqual(result, expected)
+## text.delete('1.0', 'end')
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=2)
diff --git a/lib/python2.7/idlelib/idle_test/test_grep.py b/lib/python2.7/idlelib/idle_test/test_grep.py
new file mode 100644
index 0000000..e9f4f22
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_grep.py
@@ -0,0 +1,82 @@
+""" !Changing this line will break Test_findfile.test_found!
+Non-gui unit tests for idlelib.GrepDialog methods.
+dummy_command calls grep_it calls findfiles.
+An exception raised in one method will fail callers.
+Otherwise, tests are mostly independent.
+*** Currently only test grep_it.
+"""
+import unittest
+from test.test_support import captured_stdout, findfile
+from idlelib.idle_test.mock_tk import Var
+from idlelib.GrepDialog import GrepDialog
+import re
+
+__file__ = findfile('idlelib/idle_test') + '/test_grep.py'
+
+class Dummy_searchengine:
+ '''GrepDialog.__init__ calls parent SearchDiabolBase which attaches the
+ passed in SearchEngine instance as attribute 'engine'. Only a few of the
+ many possible self.engine.x attributes are needed here.
+ '''
+ def getpat(self):
+ return self._pat
+
+searchengine = Dummy_searchengine()
+
+class Dummy_grep:
+ # Methods tested
+ #default_command = GrepDialog.default_command
+ grep_it = GrepDialog.grep_it.im_func
+ findfiles = GrepDialog.findfiles.im_func
+ # Other stuff needed
+ recvar = Var(False)
+ engine = searchengine
+ def close(self): # gui method
+ pass
+
+grep = Dummy_grep()
+
+class FindfilesTest(unittest.TestCase):
+ # findfiles is really a function, not a method, could be iterator
+ # test that filename return filename
+ # test that idlelib has many .py files
+ # test that recursive flag adds idle_test .py files
+ pass
+
+class Grep_itTest(unittest.TestCase):
+ # Test captured reports with 0 and some hits.
+ # Should test file names, but Windows reports have mixed / and \ separators
+ # from incomplete replacement, so 'later'.
+
+ def report(self, pat):
+ grep.engine._pat = pat
+ with captured_stdout() as s:
+ grep.grep_it(re.compile(pat), __file__)
+ lines = s.getvalue().split('\n')
+ lines.pop() # remove bogus '' after last \n
+ return lines
+
+ def test_unfound(self):
+ pat = 'xyz*'*7
+ lines = self.report(pat)
+ self.assertEqual(len(lines), 2)
+ self.assertIn(pat, lines[0])
+ self.assertEqual(lines[1], 'No hits.')
+
+ def test_found(self):
+
+ pat = '""" !Changing this line will break Test_findfile.test_found!'
+ lines = self.report(pat)
+ self.assertEqual(len(lines), 5)
+ self.assertIn(pat, lines[0])
+ self.assertIn('py: 1:', lines[1]) # line number 1
+ self.assertIn('2', lines[3]) # hits found 2
+ self.assertTrue(lines[4].startswith('(Hint:'))
+
+class Default_commandTest(unittest.TestCase):
+ # To write this, mode OutputWindow import to top of GrepDialog
+ # so it can be replaced by captured_stdout in class setup/teardown.
+ pass
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=False)
diff --git a/lib/python2.7/idlelib/idle_test/test_helpabout.py b/lib/python2.7/idlelib/idle_test/test_helpabout.py
new file mode 100644
index 0000000..0046f87
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_helpabout.py
@@ -0,0 +1,52 @@
+'''Test idlelib.help_about.
+
+Coverage:
+'''
+from idlelib import aboutDialog as help_about
+from idlelib import textView as textview
+from idlelib.idle_test.mock_idle import Func
+from idlelib.idle_test.mock_tk import Mbox
+import unittest
+
+About = help_about.AboutDialog
+class Dummy_about_dialog():
+ # Dummy class for testing file display functions.
+ idle_credits = About.ShowIDLECredits.im_func
+ idle_readme = About.ShowIDLEAbout.im_func
+ idle_news = About.ShowIDLENEWS.im_func
+ # Called by the above
+ display_file_text = About.display_file_text.im_func
+
+
+class DisplayFileTest(unittest.TestCase):
+ "Test that .txt files are found and properly decoded."
+ dialog = Dummy_about_dialog()
+
+ @classmethod
+ def setUpClass(cls):
+ cls.orig_mbox = textview.tkMessageBox
+ cls.orig_view = textview.view_text
+ cls.mbox = Mbox()
+ cls.view = Func()
+ textview.tkMessageBox = cls.mbox
+ textview.view_text = cls.view
+ cls.About = Dummy_about_dialog()
+
+ @classmethod
+ def tearDownClass(cls):
+ textview.tkMessageBox = cls.orig_mbox
+ textview.view_text = cls.orig_view.im_func
+
+ def test_file_isplay(self):
+ for handler in (self.dialog.idle_credits,
+ self.dialog.idle_readme,
+ self.dialog.idle_news):
+ self.mbox.showerror.message = ''
+ self.view.called = False
+ handler()
+ self.assertEqual(self.mbox.showerror.message, '')
+ self.assertEqual(self.view.called, True)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/lib/python2.7/idlelib/idle_test/test_hyperparser.py b/lib/python2.7/idlelib/idle_test/test_hyperparser.py
new file mode 100644
index 0000000..0a1809d
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_hyperparser.py
@@ -0,0 +1,192 @@
+"""Unittest for idlelib.HyperParser"""
+import unittest
+from test.test_support import requires
+from Tkinter import Tk, Text
+from idlelib.EditorWindow import EditorWindow
+from idlelib.HyperParser import HyperParser
+
+class DummyEditwin:
+ def __init__(self, text):
+ self.text = text
+ self.indentwidth = 8
+ self.tabwidth = 8
+ self.context_use_ps1 = True
+ self.num_context_lines = 50, 500, 1000
+
+ _build_char_in_string_func = EditorWindow._build_char_in_string_func.im_func
+ is_char_in_string = EditorWindow.is_char_in_string.im_func
+
+
+class HyperParserTest(unittest.TestCase):
+ code = (
+ '"""This is a module docstring"""\n'
+ '# this line is a comment\n'
+ 'x = "this is a string"\n'
+ "y = 'this is also a string'\n"
+ 'l = [i for i in range(10)]\n'
+ 'm = [py*py for # comment\n'
+ ' py in l]\n'
+ 'x.__len__\n'
+ "z = ((r'asdf')+('a')))\n"
+ '[x for x in\n'
+ 'for = False\n'
+ )
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ cls.root.withdraw()
+ cls.text = Text(cls.root)
+ cls.editwin = DummyEditwin(cls.text)
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.text, cls.editwin
+ cls.root.destroy()
+ del cls.root
+
+ def setUp(self):
+ self.text.insert('insert', self.code)
+
+ def tearDown(self):
+ self.text.delete('1.0', 'end')
+ self.editwin.context_use_ps1 = True
+
+ def get_parser(self, index):
+ """
+ Return a parser object with index at 'index'
+ """
+ return HyperParser(self.editwin, index)
+
+ def test_init(self):
+ """
+ test corner cases in the init method
+ """
+ with self.assertRaises(ValueError) as ve:
+ self.text.tag_add('console', '1.0', '1.end')
+ p = self.get_parser('1.5')
+ self.assertIn('precedes', str(ve.exception))
+
+ # test without ps1
+ self.editwin.context_use_ps1 = False
+
+ # number of lines lesser than 50
+ p = self.get_parser('end')
+ self.assertEqual(p.rawtext, self.text.get('1.0', 'end'))
+
+ # number of lines greater than 50
+ self.text.insert('end', self.text.get('1.0', 'end')*4)
+ p = self.get_parser('54.5')
+
+ def test_is_in_string(self):
+ get = self.get_parser
+
+ p = get('1.0')
+ self.assertFalse(p.is_in_string())
+ p = get('1.4')
+ self.assertTrue(p.is_in_string())
+ p = get('2.3')
+ self.assertFalse(p.is_in_string())
+ p = get('3.3')
+ self.assertFalse(p.is_in_string())
+ p = get('3.7')
+ self.assertTrue(p.is_in_string())
+ p = get('4.6')
+ self.assertTrue(p.is_in_string())
+
+ def test_is_in_code(self):
+ get = self.get_parser
+
+ p = get('1.0')
+ self.assertTrue(p.is_in_code())
+ p = get('1.1')
+ self.assertFalse(p.is_in_code())
+ p = get('2.5')
+ self.assertFalse(p.is_in_code())
+ p = get('3.4')
+ self.assertTrue(p.is_in_code())
+ p = get('3.6')
+ self.assertFalse(p.is_in_code())
+ p = get('4.14')
+ self.assertFalse(p.is_in_code())
+
+ def test_get_surrounding_bracket(self):
+ get = self.get_parser
+
+ def without_mustclose(parser):
+ # a utility function to get surrounding bracket
+ # with mustclose=False
+ return parser.get_surrounding_brackets(mustclose=False)
+
+ def with_mustclose(parser):
+ # a utility function to get surrounding bracket
+ # with mustclose=True
+ return parser.get_surrounding_brackets(mustclose=True)
+
+ p = get('3.2')
+ self.assertIsNone(with_mustclose(p))
+ self.assertIsNone(without_mustclose(p))
+
+ p = get('5.6')
+ self.assertTupleEqual(without_mustclose(p), ('5.4', '5.25'))
+ self.assertTupleEqual(without_mustclose(p), with_mustclose(p))
+
+ p = get('5.23')
+ self.assertTupleEqual(without_mustclose(p), ('5.21', '5.24'))
+ self.assertTupleEqual(without_mustclose(p), with_mustclose(p))
+
+ p = get('6.15')
+ self.assertTupleEqual(without_mustclose(p), ('6.4', '6.end'))
+ self.assertIsNone(with_mustclose(p))
+
+ p = get('9.end')
+ self.assertIsNone(with_mustclose(p))
+ self.assertIsNone(without_mustclose(p))
+
+ def test_get_expression(self):
+ get = self.get_parser
+
+ p = get('4.2')
+ self.assertEqual(p.get_expression(), 'y ')
+
+ p = get('4.7')
+ with self.assertRaises(ValueError) as ve:
+ p.get_expression()
+ self.assertIn('is inside a code', str(ve.exception))
+
+ p = get('5.25')
+ self.assertEqual(p.get_expression(), 'range(10)')
+
+ p = get('6.7')
+ self.assertEqual(p.get_expression(), 'py')
+
+ p = get('6.8')
+ self.assertEqual(p.get_expression(), '')
+
+ p = get('7.9')
+ self.assertEqual(p.get_expression(), 'py')
+
+ p = get('8.end')
+ self.assertEqual(p.get_expression(), 'x.__len__')
+
+ p = get('9.13')
+ self.assertEqual(p.get_expression(), "r'asdf'")
+
+ p = get('9.17')
+ with self.assertRaises(ValueError) as ve:
+ p.get_expression()
+ self.assertIn('is inside a code', str(ve.exception))
+
+ p = get('10.0')
+ self.assertEqual(p.get_expression(), '')
+
+ p = get('11.3')
+ self.assertEqual(p.get_expression(), '')
+
+ p = get('11.11')
+ self.assertEqual(p.get_expression(), 'False')
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/lib/python2.7/idlelib/idle_test/test_idlehistory.py b/lib/python2.7/idlelib/idle_test/test_idlehistory.py
new file mode 100644
index 0000000..b076757
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_idlehistory.py
@@ -0,0 +1,168 @@
+import unittest
+from test.test_support import requires
+
+import Tkinter as tk
+from Tkinter import Text as tkText
+from idlelib.idle_test.mock_tk import Text as mkText
+from idlelib.IdleHistory import History
+from idlelib.configHandler import idleConf
+
+line1 = 'a = 7'
+line2 = 'b = a'
+
+class StoreTest(unittest.TestCase):
+ '''Tests History.__init__ and History.store with mock Text'''
+
+ @classmethod
+ def setUpClass(cls):
+ cls.text = mkText()
+ cls.history = History(cls.text)
+
+ def tearDown(self):
+ self.text.delete('1.0', 'end')
+ self.history.history = []
+
+ def test_init(self):
+ self.assertIs(self.history.text, self.text)
+ self.assertEqual(self.history.history, [])
+ self.assertIsNone(self.history.prefix)
+ self.assertIsNone(self.history.pointer)
+ self.assertEqual(self.history.cyclic,
+ idleConf.GetOption("main", "History", "cyclic", 1, "bool"))
+
+ def test_store_short(self):
+ self.history.store('a')
+ self.assertEqual(self.history.history, [])
+ self.history.store(' a ')
+ self.assertEqual(self.history.history, [])
+
+ def test_store_dup(self):
+ self.history.store(line1)
+ self.assertEqual(self.history.history, [line1])
+ self.history.store(line2)
+ self.assertEqual(self.history.history, [line1, line2])
+ self.history.store(line1)
+ self.assertEqual(self.history.history, [line2, line1])
+
+ def test_store_reset(self):
+ self.history.prefix = line1
+ self.history.pointer = 0
+ self.history.store(line2)
+ self.assertIsNone(self.history.prefix)
+ self.assertIsNone(self.history.pointer)
+
+
+class TextWrapper:
+ def __init__(self, master):
+ self.text = tkText(master=master)
+ self._bell = False
+ def __getattr__(self, name):
+ return getattr(self.text, name)
+ def bell(self):
+ self._bell = True
+
+class FetchTest(unittest.TestCase):
+ '''Test History.fetch with wrapped tk.Text.
+ '''
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = tk.Tk()
+ cls.root.withdraw()
+
+ def setUp(self):
+ self.text = text = TextWrapper(self.root)
+ text.insert('1.0', ">>> ")
+ text.mark_set('iomark', '1.4')
+ text.mark_gravity('iomark', 'left')
+ self.history = History(text)
+ self.history.history = [line1, line2]
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.root
+
+ def fetch_test(self, reverse, line, prefix, index, bell=False):
+ # Perform one fetch as invoked by Alt-N or Alt-P
+ # Test the result. The line test is the most important.
+ # The last two are diagnostic of fetch internals.
+ History = self.history
+ History.fetch(reverse)
+
+ Equal = self.assertEqual
+ Equal(self.text.get('iomark', 'end-1c'), line)
+ Equal(self.text._bell, bell)
+ if bell:
+ self.text._bell = False
+ Equal(History.prefix, prefix)
+ Equal(History.pointer, index)
+ Equal(self.text.compare("insert", '==', "end-1c"), 1)
+
+ def test_fetch_prev_cyclic(self):
+ prefix = ''
+ test = self.fetch_test
+ test(True, line2, prefix, 1)
+ test(True, line1, prefix, 0)
+ test(True, prefix, None, None, bell=True)
+
+ def test_fetch_next_cyclic(self):
+ prefix = ''
+ test = self.fetch_test
+ test(False, line1, prefix, 0)
+ test(False, line2, prefix, 1)
+ test(False, prefix, None, None, bell=True)
+
+ # Prefix 'a' tests skip line2, which starts with 'b'
+ def test_fetch_prev_prefix(self):
+ prefix = 'a'
+ self.text.insert('iomark', prefix)
+ self.fetch_test(True, line1, prefix, 0)
+ self.fetch_test(True, prefix, None, None, bell=True)
+
+ def test_fetch_next_prefix(self):
+ prefix = 'a'
+ self.text.insert('iomark', prefix)
+ self.fetch_test(False, line1, prefix, 0)
+ self.fetch_test(False, prefix, None, None, bell=True)
+
+ def test_fetch_prev_noncyclic(self):
+ prefix = ''
+ self.history.cyclic = False
+ test = self.fetch_test
+ test(True, line2, prefix, 1)
+ test(True, line1, prefix, 0)
+ test(True, line1, prefix, 0, bell=True)
+
+ def test_fetch_next_noncyclic(self):
+ prefix = ''
+ self.history.cyclic = False
+ test = self.fetch_test
+ test(False, prefix, None, None, bell=True)
+ test(True, line2, prefix, 1)
+ test(False, prefix, None, None, bell=True)
+ test(False, prefix, None, None, bell=True)
+
+ def test_fetch_cursor_move(self):
+ # Move cursor after fetch
+ self.history.fetch(reverse=True) # initialization
+ self.text.mark_set('insert', 'iomark')
+ self.fetch_test(True, line2, None, None, bell=True)
+
+ def test_fetch_edit(self):
+ # Edit after fetch
+ self.history.fetch(reverse=True) # initialization
+ self.text.delete('iomark', 'insert', )
+ self.text.insert('iomark', 'a =')
+ self.fetch_test(True, line1, 'a =', 0) # prefix is reset
+
+ def test_history_prev_next(self):
+ # Minimally test functions bound to events
+ self.history.history_prev('dummy event')
+ self.assertEqual(self.history.pointer, 1)
+ self.history.history_next('dummy event')
+ self.assertEqual(self.history.pointer, None)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=2)
diff --git a/lib/python2.7/idlelib/idle_test/test_io.py b/lib/python2.7/idlelib/idle_test/test_io.py
new file mode 100644
index 0000000..ee017bb
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_io.py
@@ -0,0 +1,267 @@
+import unittest
+import io
+from idlelib.PyShell import PseudoInputFile, PseudoOutputFile
+from test import test_support as support
+
+
+class Base(object):
+ def __str__(self):
+ return '%s:str' % type(self).__name__
+ def __unicode__(self):
+ return '%s:unicode' % type(self).__name__
+ def __len__(self):
+ return 3
+ def __iter__(self):
+ return iter('abc')
+ def __getitem__(self, *args):
+ return '%s:item' % type(self).__name__
+ def __getslice__(self, *args):
+ return '%s:slice' % type(self).__name__
+
+class S(Base, str):
+ pass
+
+class U(Base, unicode):
+ pass
+
+class BA(Base, bytearray):
+ pass
+
+class MockShell:
+ def __init__(self):
+ self.reset()
+
+ def write(self, *args):
+ self.written.append(args)
+
+ def readline(self):
+ return self.lines.pop()
+
+ def close(self):
+ pass
+
+ def reset(self):
+ self.written = []
+
+ def push(self, lines):
+ self.lines = list(lines)[::-1]
+
+
+class PseudeOutputFilesTest(unittest.TestCase):
+ def test_misc(self):
+ shell = MockShell()
+ f = PseudoOutputFile(shell, 'stdout', 'utf-8')
+ self.assertIsInstance(f, io.TextIOBase)
+ self.assertEqual(f.encoding, 'utf-8')
+ self.assertIsNone(f.errors)
+ self.assertIsNone(f.newlines)
+ self.assertEqual(f.name, '<stdout>')
+ self.assertFalse(f.closed)
+ self.assertTrue(f.isatty())
+ self.assertFalse(f.readable())
+ self.assertTrue(f.writable())
+ self.assertFalse(f.seekable())
+
+ def test_unsupported(self):
+ shell = MockShell()
+ f = PseudoOutputFile(shell, 'stdout', 'utf-8')
+ self.assertRaises(IOError, f.fileno)
+ self.assertRaises(IOError, f.tell)
+ self.assertRaises(IOError, f.seek, 0)
+ self.assertRaises(IOError, f.read, 0)
+ self.assertRaises(IOError, f.readline, 0)
+
+ def test_write(self):
+ shell = MockShell()
+ f = PseudoOutputFile(shell, 'stdout', 'utf-8')
+ f.write('test')
+ self.assertEqual(shell.written, [('test', 'stdout')])
+ shell.reset()
+ f.write('t\xe8st')
+ self.assertEqual(shell.written, [('t\xe8st', 'stdout')])
+ shell.reset()
+ f.write(u't\xe8st')
+ self.assertEqual(shell.written, [(u't\xe8st', 'stdout')])
+ shell.reset()
+
+ f.write(S('t\xe8st'))
+ self.assertEqual(shell.written, [('t\xe8st', 'stdout')])
+ self.assertEqual(type(shell.written[0][0]), str)
+ shell.reset()
+ f.write(BA('t\xe8st'))
+ self.assertEqual(shell.written, [('t\xe8st', 'stdout')])
+ self.assertEqual(type(shell.written[0][0]), str)
+ shell.reset()
+ f.write(U(u't\xe8st'))
+ self.assertEqual(shell.written, [(u't\xe8st', 'stdout')])
+ self.assertEqual(type(shell.written[0][0]), unicode)
+ shell.reset()
+
+ self.assertRaises(TypeError, f.write)
+ self.assertEqual(shell.written, [])
+ self.assertRaises(TypeError, f.write, 123)
+ self.assertEqual(shell.written, [])
+ self.assertRaises(TypeError, f.write, 'test', 'spam')
+ self.assertEqual(shell.written, [])
+
+ def test_writelines(self):
+ shell = MockShell()
+ f = PseudoOutputFile(shell, 'stdout', 'utf-8')
+ f.writelines([])
+ self.assertEqual(shell.written, [])
+ shell.reset()
+ f.writelines(['one\n', 'two'])
+ self.assertEqual(shell.written,
+ [('one\n', 'stdout'), ('two', 'stdout')])
+ shell.reset()
+ f.writelines(['on\xe8\n', 'tw\xf2'])
+ self.assertEqual(shell.written,
+ [('on\xe8\n', 'stdout'), ('tw\xf2', 'stdout')])
+ shell.reset()
+ f.writelines([u'on\xe8\n', u'tw\xf2'])
+ self.assertEqual(shell.written,
+ [(u'on\xe8\n', 'stdout'), (u'tw\xf2', 'stdout')])
+ shell.reset()
+
+ f.writelines([S('t\xe8st')])
+ self.assertEqual(shell.written, [('t\xe8st', 'stdout')])
+ self.assertEqual(type(shell.written[0][0]), str)
+ shell.reset()
+ f.writelines([BA('t\xe8st')])
+ self.assertEqual(shell.written, [('t\xe8st', 'stdout')])
+ self.assertEqual(type(shell.written[0][0]), str)
+ shell.reset()
+ f.writelines([U(u't\xe8st')])
+ self.assertEqual(shell.written, [(u't\xe8st', 'stdout')])
+ self.assertEqual(type(shell.written[0][0]), unicode)
+ shell.reset()
+
+ self.assertRaises(TypeError, f.writelines)
+ self.assertEqual(shell.written, [])
+ self.assertRaises(TypeError, f.writelines, 123)
+ self.assertEqual(shell.written, [])
+ self.assertRaises(TypeError, f.writelines, [123])
+ self.assertEqual(shell.written, [])
+ self.assertRaises(TypeError, f.writelines, [], [])
+ self.assertEqual(shell.written, [])
+
+ def test_close(self):
+ shell = MockShell()
+ f = PseudoOutputFile(shell, 'stdout', 'utf-8')
+ self.assertFalse(f.closed)
+ f.write('test')
+ f.close()
+ self.assertTrue(f.closed)
+ self.assertRaises(ValueError, f.write, 'x')
+ self.assertEqual(shell.written, [('test', 'stdout')])
+ f.close()
+ self.assertRaises(TypeError, f.close, 1)
+
+
+class PseudeInputFilesTest(unittest.TestCase):
+ def test_misc(self):
+ shell = MockShell()
+ f = PseudoInputFile(shell, 'stdin', 'utf-8')
+ self.assertIsInstance(f, io.TextIOBase)
+ self.assertEqual(f.encoding, 'utf-8')
+ self.assertIsNone(f.errors)
+ self.assertIsNone(f.newlines)
+ self.assertEqual(f.name, '<stdin>')
+ self.assertFalse(f.closed)
+ self.assertTrue(f.isatty())
+ self.assertTrue(f.readable())
+ self.assertFalse(f.writable())
+ self.assertFalse(f.seekable())
+
+ def test_unsupported(self):
+ shell = MockShell()
+ f = PseudoInputFile(shell, 'stdin', 'utf-8')
+ self.assertRaises(IOError, f.fileno)
+ self.assertRaises(IOError, f.tell)
+ self.assertRaises(IOError, f.seek, 0)
+ self.assertRaises(IOError, f.write, 'x')
+ self.assertRaises(IOError, f.writelines, ['x'])
+
+ def test_read(self):
+ shell = MockShell()
+ f = PseudoInputFile(shell, 'stdin', 'utf-8')
+ shell.push(['one\n', 'two\n', ''])
+ self.assertEqual(f.read(), 'one\ntwo\n')
+ shell.push(['one\n', 'two\n', ''])
+ self.assertEqual(f.read(-1), 'one\ntwo\n')
+ shell.push(['one\n', 'two\n', ''])
+ self.assertEqual(f.read(None), 'one\ntwo\n')
+ shell.push(['one\n', 'two\n', 'three\n', ''])
+ self.assertEqual(f.read(2), 'on')
+ self.assertEqual(f.read(3), 'e\nt')
+ self.assertEqual(f.read(10), 'wo\nthree\n')
+
+ shell.push(['one\n', 'two\n'])
+ self.assertEqual(f.read(0), '')
+ self.assertRaises(TypeError, f.read, 1.5)
+ self.assertRaises(TypeError, f.read, '1')
+ self.assertRaises(TypeError, f.read, 1, 1)
+
+ def test_readline(self):
+ shell = MockShell()
+ f = PseudoInputFile(shell, 'stdin', 'utf-8')
+ shell.push(['one\n', 'two\n', 'three\n', 'four\n'])
+ self.assertEqual(f.readline(), 'one\n')
+ self.assertEqual(f.readline(-1), 'two\n')
+ self.assertEqual(f.readline(None), 'three\n')
+ shell.push(['one\ntwo\n'])
+ self.assertEqual(f.readline(), 'one\n')
+ self.assertEqual(f.readline(), 'two\n')
+ shell.push(['one', 'two', 'three'])
+ self.assertEqual(f.readline(), 'one')
+ self.assertEqual(f.readline(), 'two')
+ shell.push(['one\n', 'two\n', 'three\n'])
+ self.assertEqual(f.readline(2), 'on')
+ self.assertEqual(f.readline(1), 'e')
+ self.assertEqual(f.readline(1), '\n')
+ self.assertEqual(f.readline(10), 'two\n')
+
+ shell.push(['one\n', 'two\n'])
+ self.assertEqual(f.readline(0), '')
+ self.assertRaises(TypeError, f.readlines, 1.5)
+ self.assertRaises(TypeError, f.readlines, '1')
+ self.assertRaises(TypeError, f.readlines, 1, 1)
+
+ def test_readlines(self):
+ shell = MockShell()
+ f = PseudoInputFile(shell, 'stdin', 'utf-8')
+ shell.push(['one\n', 'two\n', ''])
+ self.assertEqual(f.readlines(), ['one\n', 'two\n'])
+ shell.push(['one\n', 'two\n', ''])
+ self.assertEqual(f.readlines(-1), ['one\n', 'two\n'])
+ shell.push(['one\n', 'two\n', ''])
+ self.assertEqual(f.readlines(None), ['one\n', 'two\n'])
+ shell.push(['one\n', 'two\n', ''])
+ self.assertEqual(f.readlines(0), ['one\n', 'two\n'])
+ shell.push(['one\n', 'two\n', ''])
+ self.assertEqual(f.readlines(3), ['one\n'])
+ shell.push(['one\n', 'two\n', ''])
+ self.assertEqual(f.readlines(4), ['one\n', 'two\n'])
+
+ shell.push(['one\n', 'two\n', ''])
+ self.assertRaises(TypeError, f.readlines, 1.5)
+ self.assertRaises(TypeError, f.readlines, '1')
+ self.assertRaises(TypeError, f.readlines, 1, 1)
+
+ def test_close(self):
+ shell = MockShell()
+ f = PseudoInputFile(shell, 'stdin', 'utf-8')
+ shell.push(['one\n', 'two\n', ''])
+ self.assertFalse(f.closed)
+ self.assertEqual(f.readline(), 'one\n')
+ f.close()
+ self.assertFalse(f.closed)
+ self.assertEqual(f.readline(), 'two\n')
+ self.assertRaises(TypeError, f.close, 1)
+
+
+def test_main():
+ support.run_unittest(PseudeOutputFilesTest, PseudeInputFilesTest)
+
+if __name__ == '__main__':
+ test_main()
diff --git a/lib/python2.7/idlelib/idle_test/test_parenmatch.py b/lib/python2.7/idlelib/idle_test/test_parenmatch.py
new file mode 100644
index 0000000..1621981
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_parenmatch.py
@@ -0,0 +1,121 @@
+"""Test idlelib.ParenMatch."""
+# This must currently be a gui test because ParenMatch methods use
+# several text methods not defined on idlelib.idle_test.mock_tk.Text.
+
+import unittest
+from test.test_support import requires
+from Tkinter import Tk, Text
+from idlelib.ParenMatch import ParenMatch
+
+class Mock: # 2.7 does not have unittest.mock
+ def __init__(self, *args, **kwargs):
+ self.called = False
+
+ def __call__(self, *args, **kwargs):
+ self.called = True
+
+ def reset_mock(self, *args, **kwargs):
+ self.called = False
+
+ def after(self, *args, **kwargs):
+ pass
+
+class DummyEditwin:
+ def __init__(self, text):
+ self.text = text
+ self.indentwidth = 8
+ self.tabwidth = 8
+ self.context_use_ps1 = True
+
+
+class ParenMatchTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ cls.text = Text(cls.root)
+ cls.editwin = DummyEditwin(cls.text)
+ cls.editwin.text_frame = Mock()
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.text, cls.editwin
+ cls.root.destroy()
+ del cls.root
+
+ def tearDown(self):
+ self.text.delete('1.0', 'end')
+
+ def test_paren_expression(self):
+ """
+ Test ParenMatch with 'expression' style.
+ """
+ text = self.text
+ pm = ParenMatch(self.editwin)
+ pm.set_style('expression')
+
+ text.insert('insert', 'def foobar(a, b')
+ pm.flash_paren_event('event')
+ self.assertIn('<<parenmatch-check-restore>>', text.event_info())
+ self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
+ ('1.10', '1.15'))
+ text.insert('insert', ')')
+ pm.restore_event()
+ self.assertNotIn('<<parenmatch-check-restore>>', text.event_info())
+ self.assertEqual(text.tag_prevrange('paren', 'end'), ())
+
+ # paren_closed_event can only be tested as below
+ pm.paren_closed_event('event')
+ self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
+ ('1.10', '1.16'))
+
+ def test_paren_default(self):
+ """
+ Test ParenMatch with 'default' style.
+ """
+ text = self.text
+ pm = ParenMatch(self.editwin)
+ pm.set_style('default')
+
+ text.insert('insert', 'def foobar(a, b')
+ pm.flash_paren_event('event')
+ self.assertIn('<<parenmatch-check-restore>>', text.event_info())
+ self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
+ ('1.10', '1.11'))
+ text.insert('insert', ')')
+ pm.restore_event()
+ self.assertNotIn('<<parenmatch-check-restore>>', text.event_info())
+ self.assertEqual(text.tag_prevrange('paren', 'end'), ())
+
+ def test_paren_corner(self):
+ """
+ Test corner cases in flash_paren_event and paren_closed_event.
+
+ These cases force conditional expression and alternate paths.
+ """
+ text = self.text
+ pm = ParenMatch(self.editwin)
+
+ text.insert('insert', '# this is a commen)')
+ self.assertIsNone(pm.paren_closed_event('event'))
+
+ text.insert('insert', '\ndef')
+ self.assertIsNone(pm.flash_paren_event('event'))
+ self.assertIsNone(pm.paren_closed_event('event'))
+
+ text.insert('insert', ' a, *arg)')
+ self.assertIsNone(pm.paren_closed_event('event'))
+
+ def test_handle_restore_timer(self):
+ pm = ParenMatch(self.editwin)
+ pm.restore_event = Mock()
+ pm.handle_restore_timer(0)
+ self.assertTrue(pm.restore_event.called)
+ pm.restore_event.reset_mock()
+ pm.handle_restore_timer(1)
+ self.assertFalse(pm.restore_event.called)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/lib/python2.7/idlelib/idle_test/test_pathbrowser.py b/lib/python2.7/idlelib/idle_test/test_pathbrowser.py
new file mode 100644
index 0000000..f028414
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_pathbrowser.py
@@ -0,0 +1,28 @@
+import unittest
+import os
+import sys
+import idlelib
+from idlelib import PathBrowser
+
+class PathBrowserTest(unittest.TestCase):
+
+ def test_DirBrowserTreeItem(self):
+ # Issue16226 - make sure that getting a sublist works
+ d = PathBrowser.DirBrowserTreeItem('')
+ d.GetSubList()
+ self.assertEqual('', d.GetText())
+
+ dir = os.path.split(os.path.abspath(idlelib.__file__))[0]
+ self.assertEqual(d.ispackagedir(dir), True)
+ self.assertEqual(d.ispackagedir(dir + '/Icons'), False)
+
+ def test_PathBrowserTreeItem(self):
+ p = PathBrowser.PathBrowserTreeItem()
+ self.assertEqual(p.GetText(), 'sys.path')
+ sub = p.GetSubList()
+ self.assertEqual(len(sub), len(sys.path))
+ # Following fails in 2.7 because old-style class
+ #self.assertEqual(type(sub[0]), PathBrowser.DirBrowserTreeItem)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=False)
diff --git a/lib/python2.7/idlelib/idle_test/test_rstrip.py b/lib/python2.7/idlelib/idle_test/test_rstrip.py
new file mode 100644
index 0000000..1c90b93
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_rstrip.py
@@ -0,0 +1,49 @@
+import unittest
+import idlelib.RstripExtension as rs
+from idlelib.idle_test.mock_idle import Editor
+
+class rstripTest(unittest.TestCase):
+
+ def test_rstrip_line(self):
+ editor = Editor()
+ text = editor.text
+ do_rstrip = rs.RstripExtension(editor).do_rstrip
+
+ do_rstrip()
+ self.assertEqual(text.get('1.0', 'insert'), '')
+ text.insert('1.0', ' ')
+ do_rstrip()
+ self.assertEqual(text.get('1.0', 'insert'), '')
+ text.insert('1.0', ' \n')
+ do_rstrip()
+ self.assertEqual(text.get('1.0', 'insert'), '\n')
+
+ def test_rstrip_multiple(self):
+ editor = Editor()
+ # Uncomment following to verify that test passes with real widgets.
+## from idlelib.EditorWindow import EditorWindow as Editor
+## from tkinter import Tk
+## editor = Editor(root=Tk())
+ text = editor.text
+ do_rstrip = rs.RstripExtension(editor).do_rstrip
+
+ original = (
+ "Line with an ending tab \n"
+ "Line ending in 5 spaces \n"
+ "Linewithnospaces\n"
+ " indented line\n"
+ " indented line with trailing space \n"
+ " ")
+ stripped = (
+ "Line with an ending tab\n"
+ "Line ending in 5 spaces\n"
+ "Linewithnospaces\n"
+ " indented line\n"
+ " indented line with trailing space\n")
+
+ text.insert('1.0', original)
+ do_rstrip()
+ self.assertEqual(text.get('1.0', 'insert'), stripped)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=False)
diff --git a/lib/python2.7/idlelib/idle_test/test_searchdialogbase.py b/lib/python2.7/idlelib/idle_test/test_searchdialogbase.py
new file mode 100644
index 0000000..32abfe6
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_searchdialogbase.py
@@ -0,0 +1,164 @@
+'''Unittests for idlelib/SearchDialogBase.py
+
+Coverage: 99%. The only thing not covered is inconsequential --
+testing skipping of suite when self.needwrapbutton is false.
+
+'''
+import unittest
+from test.test_support import requires
+from Tkinter import Tk, Toplevel, Frame ## BooleanVar, StringVar
+from idlelib import SearchEngine as se
+from idlelib import SearchDialogBase as sdb
+from idlelib.idle_test.mock_idle import Func
+##from idlelib.idle_test.mock_tk import Var
+
+# The ## imports above & following could help make some tests gui-free.# However, they currently make radiobutton tests fail.
+##def setUpModule():
+## # Replace tk objects used to initialize se.SearchEngine.
+## se.BooleanVar = Var
+## se.StringVar = Var
+##
+##def tearDownModule():
+## se.BooleanVar = BooleanVar
+## se.StringVar = StringVar
+
+class SearchDialogBaseTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.root
+
+ def setUp(self):
+ self.engine = se.SearchEngine(self.root) # None also seems to work
+ self.dialog = sdb.SearchDialogBase(root=self.root, engine=self.engine)
+
+ def tearDown(self):
+ self.dialog.close()
+
+ def test_open_and_close(self):
+ # open calls create_widgets, which needs default_command
+ self.dialog.default_command = None
+
+ # Since text parameter of .open is not used in base class,
+ # pass dummy 'text' instead of tk.Text().
+ self.dialog.open('text')
+ self.assertEqual(self.dialog.top.state(), 'normal')
+ self.dialog.close()
+ self.assertEqual(self.dialog.top.state(), 'withdrawn')
+
+ self.dialog.open('text', searchphrase="hello")
+ self.assertEqual(self.dialog.ent.get(), 'hello')
+ self.dialog.close()
+
+ def test_create_widgets(self):
+ self.dialog.create_entries = Func()
+ self.dialog.create_option_buttons = Func()
+ self.dialog.create_other_buttons = Func()
+ self.dialog.create_command_buttons = Func()
+
+ self.dialog.default_command = None
+ self.dialog.create_widgets()
+
+ self.assertTrue(self.dialog.create_entries.called)
+ self.assertTrue(self.dialog.create_option_buttons.called)
+ self.assertTrue(self.dialog.create_other_buttons.called)
+ self.assertTrue(self.dialog.create_command_buttons.called)
+
+ def test_make_entry(self):
+ equal = self.assertEqual
+ self.dialog.row = 0
+ self.dialog.top = Toplevel(self.root)
+ entry, label = self.dialog.make_entry("Test:", 'hello')
+ equal(label['text'], 'Test:')
+
+ self.assertIn(entry.get(), 'hello')
+ egi = entry.grid_info()
+ equal(int(egi['row']), 0)
+ equal(int(egi['column']), 1)
+ equal(int(egi['rowspan']), 1)
+ equal(int(egi['columnspan']), 1)
+ equal(self.dialog.row, 1)
+
+ def test_create_entries(self):
+ self.dialog.row = 0
+ self.engine.setpat('hello')
+ self.dialog.create_entries()
+ self.assertIn(self.dialog.ent.get(), 'hello')
+
+ def test_make_frame(self):
+ self.dialog.row = 0
+ self.dialog.top = Toplevel(self.root)
+ frame, label = self.dialog.make_frame()
+ self.assertEqual(label, '')
+ self.assertIsInstance(frame, Frame)
+
+ frame, label = self.dialog.make_frame('testlabel')
+ self.assertEqual(label['text'], 'testlabel')
+ self.assertIsInstance(frame, Frame)
+
+ def btn_test_setup(self, meth):
+ self.dialog.top = Toplevel(self.root)
+ self.dialog.row = 0
+ return meth()
+
+ def test_create_option_buttons(self):
+ e = self.engine
+ for state in (0, 1):
+ for var in (e.revar, e.casevar, e.wordvar, e.wrapvar):
+ var.set(state)
+ frame, options = self.btn_test_setup(
+ self.dialog.create_option_buttons)
+ for spec, button in zip (options, frame.pack_slaves()):
+ var, label = spec
+ self.assertEqual(button['text'], label)
+ self.assertEqual(var.get(), state)
+ if state == 1:
+ button.deselect()
+ else:
+ button.select()
+ self.assertEqual(var.get(), 1 - state)
+
+ def test_create_other_buttons(self):
+ for state in (False, True):
+ var = self.engine.backvar
+ var.set(state)
+ frame, others = self.btn_test_setup(
+ self.dialog.create_other_buttons)
+ buttons = frame.pack_slaves()
+ for spec, button in zip(others, buttons):
+ val, label = spec
+ self.assertEqual(button['text'], label)
+ if val == state:
+ # hit other button, then this one
+ # indexes depend on button order
+ self.assertEqual(var.get(), state)
+ buttons[val].select()
+ self.assertEqual(var.get(), 1 - state)
+ buttons[1-val].select()
+ self.assertEqual(var.get(), state)
+
+ def test_make_button(self):
+ self.dialog.top = Toplevel(self.root)
+ self.dialog.buttonframe = Frame(self.dialog.top)
+ btn = self.dialog.make_button('Test', self.dialog.close)
+ self.assertEqual(btn['text'], 'Test')
+
+ def test_create_command_buttons(self):
+ self.dialog.create_command_buttons()
+ # Look for close button command in buttonframe
+ closebuttoncommand = ''
+ for child in self.dialog.buttonframe.winfo_children():
+ if child['text'] == 'close':
+ closebuttoncommand = child['command']
+ self.assertIn('close', closebuttoncommand)
+
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=2)
diff --git a/lib/python2.7/idlelib/idle_test/test_searchengine.py b/lib/python2.7/idlelib/idle_test/test_searchengine.py
new file mode 100644
index 0000000..8bf9d47
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_searchengine.py
@@ -0,0 +1,329 @@
+'''Test functions and SearchEngine class in SearchEngine.py.'''
+
+# With mock replacements, the module does not use any gui widgets.
+# The use of tk.Text is avoided (for now, until mock Text is improved)
+# by patching instances with an index function returning what is needed.
+# This works because mock Text.get does not use .index.
+
+import re
+import unittest
+#from test.test_support import requires
+from Tkinter import BooleanVar, StringVar, TclError # ,Tk, Text
+import tkMessageBox
+from idlelib import SearchEngine as se
+from idlelib.idle_test.mock_tk import Var, Mbox
+from idlelib.idle_test.mock_tk import Text as mockText
+
+def setUpModule():
+ # Replace s-e module tkinter imports other than non-gui TclError.
+ se.BooleanVar = Var
+ se.StringVar = Var
+ se.tkMessageBox = Mbox
+
+def tearDownModule():
+ # Restore 'just in case', though other tests should also replace.
+ se.BooleanVar = BooleanVar
+ se.StringVar = StringVar
+ se.tkMessageBox = tkMessageBox
+
+
+class Mock:
+ def __init__(self, *args, **kwargs): pass
+
+class GetTest(unittest.TestCase):
+ # SearchEngine.get returns singleton created & saved on first call.
+ def test_get(self):
+ saved_Engine = se.SearchEngine
+ se.SearchEngine = Mock # monkey-patch class
+ try:
+ root = Mock()
+ engine = se.get(root)
+ self.assertIsInstance(engine, se.SearchEngine)
+ self.assertIs(root._searchengine, engine)
+ self.assertIs(se.get(root), engine)
+ finally:
+ se.SearchEngine = saved_Engine # restore class to module
+
+class GetLineColTest(unittest.TestCase):
+ # Test simple text-independent helper function
+ def test_get_line_col(self):
+ self.assertEqual(se.get_line_col('1.0'), (1, 0))
+ self.assertEqual(se.get_line_col('1.11'), (1, 11))
+
+ self.assertRaises(ValueError, se.get_line_col, ('1.0 lineend'))
+ self.assertRaises(ValueError, se.get_line_col, ('end'))
+
+class GetSelectionTest(unittest.TestCase):
+ # Test text-dependent helper function.
+## # Need gui for text.index('sel.first/sel.last/insert').
+## @classmethod
+## def setUpClass(cls):
+## requires('gui')
+## cls.root = Tk()
+##
+## @classmethod
+## def tearDownClass(cls):
+## cls.root.destroy()
+## del cls.root
+
+ def test_get_selection(self):
+ # text = Text(master=self.root)
+ text = mockText()
+ text.insert('1.0', 'Hello World!')
+
+ # fix text.index result when called in get_selection
+ def sel(s):
+ # select entire text, cursor irrelevant
+ if s == 'sel.first': return '1.0'
+ if s == 'sel.last': return '1.12'
+ raise TclError
+ text.index = sel # replaces .tag_add('sel', '1.0, '1.12')
+ self.assertEqual(se.get_selection(text), ('1.0', '1.12'))
+
+ def mark(s):
+ # no selection, cursor after 'Hello'
+ if s == 'insert': return '1.5'
+ raise TclError
+ text.index = mark # replaces .mark_set('insert', '1.5')
+ self.assertEqual(se.get_selection(text), ('1.5', '1.5'))
+
+
+class ReverseSearchTest(unittest.TestCase):
+ # Test helper function that searches backwards within a line.
+ def test_search_reverse(self):
+ Equal = self.assertEqual
+ line = "Here is an 'is' test text."
+ prog = re.compile('is')
+ Equal(se.search_reverse(prog, line, len(line)).span(), (12, 14))
+ Equal(se.search_reverse(prog, line, 14).span(), (12, 14))
+ Equal(se.search_reverse(prog, line, 13).span(), (5, 7))
+ Equal(se.search_reverse(prog, line, 7).span(), (5, 7))
+ Equal(se.search_reverse(prog, line, 6), None)
+
+
+class SearchEngineTest(unittest.TestCase):
+ # Test class methods that do not use Text widget.
+
+ def setUp(self):
+ self.engine = se.SearchEngine(root=None)
+ # Engine.root is only used to create error message boxes.
+ # The mock replacement ignores the root argument.
+
+ def test_is_get(self):
+ engine = self.engine
+ Equal = self.assertEqual
+
+ Equal(engine.getpat(), '')
+ engine.setpat('hello')
+ Equal(engine.getpat(), 'hello')
+
+ Equal(engine.isre(), False)
+ engine.revar.set(1)
+ Equal(engine.isre(), True)
+
+ Equal(engine.iscase(), False)
+ engine.casevar.set(1)
+ Equal(engine.iscase(), True)
+
+ Equal(engine.isword(), False)
+ engine.wordvar.set(1)
+ Equal(engine.isword(), True)
+
+ Equal(engine.iswrap(), True)
+ engine.wrapvar.set(0)
+ Equal(engine.iswrap(), False)
+
+ Equal(engine.isback(), False)
+ engine.backvar.set(1)
+ Equal(engine.isback(), True)
+
+ def test_setcookedpat(self):
+ engine = self.engine
+ engine.setcookedpat('\s')
+ self.assertEqual(engine.getpat(), '\s')
+ engine.revar.set(1)
+ engine.setcookedpat('\s')
+ self.assertEqual(engine.getpat(), r'\\s')
+
+ def test_getcookedpat(self):
+ engine = self.engine
+ Equal = self.assertEqual
+
+ Equal(engine.getcookedpat(), '')
+ engine.setpat('hello')
+ Equal(engine.getcookedpat(), 'hello')
+ engine.wordvar.set(True)
+ Equal(engine.getcookedpat(), r'\bhello\b')
+ engine.wordvar.set(False)
+
+ engine.setpat('\s')
+ Equal(engine.getcookedpat(), r'\\s')
+ engine.revar.set(True)
+ Equal(engine.getcookedpat(), '\s')
+
+ def test_getprog(self):
+ engine = self.engine
+ Equal = self.assertEqual
+
+ engine.setpat('Hello')
+ temppat = engine.getprog()
+ Equal(temppat.pattern, re.compile('Hello', re.IGNORECASE).pattern)
+ engine.casevar.set(1)
+ temppat = engine.getprog()
+ Equal(temppat.pattern, re.compile('Hello').pattern, 0)
+
+ engine.setpat('')
+ Equal(engine.getprog(), None)
+ engine.setpat('+')
+ engine.revar.set(1)
+ Equal(engine.getprog(), None)
+ self.assertEqual(Mbox.showerror.message,
+ 'Error: nothing to repeat\nPattern: +')
+
+ def test_report_error(self):
+ showerror = Mbox.showerror
+ Equal = self.assertEqual
+ pat = '[a-z'
+ msg = 'unexpected end of regular expression'
+
+ Equal(self.engine.report_error(pat, msg), None)
+ Equal(showerror.title, 'Regular expression error')
+ expected_message = ("Error: " + msg + "\nPattern: [a-z")
+ Equal(showerror.message, expected_message)
+
+ Equal(self.engine.report_error(pat, msg, 5), None)
+ Equal(showerror.title, 'Regular expression error')
+ expected_message += "\nOffset: 5"
+ Equal(showerror.message, expected_message)
+
+
+class SearchTest(unittest.TestCase):
+ # Test that search_text makes right call to right method.
+
+ @classmethod
+ def setUpClass(cls):
+## requires('gui')
+## cls.root = Tk()
+## cls.text = Text(master=cls.root)
+ cls.text = mockText()
+ test_text = (
+ 'First line\n'
+ 'Line with target\n'
+ 'Last line\n')
+ cls.text.insert('1.0', test_text)
+ cls.pat = re.compile('target')
+
+ cls.engine = se.SearchEngine(None)
+ cls.engine.search_forward = lambda *args: ('f', args)
+ cls.engine.search_backward = lambda *args: ('b', args)
+
+## @classmethod
+## def tearDownClass(cls):
+## cls.root.destroy()
+## del cls.root
+
+ def test_search(self):
+ Equal = self.assertEqual
+ engine = self.engine
+ search = engine.search_text
+ text = self.text
+ pat = self.pat
+
+ engine.patvar.set(None)
+ #engine.revar.set(pat)
+ Equal(search(text), None)
+
+ def mark(s):
+ # no selection, cursor after 'Hello'
+ if s == 'insert': return '1.5'
+ raise TclError
+ text.index = mark
+ Equal(search(text, pat), ('f', (text, pat, 1, 5, True, False)))
+ engine.wrapvar.set(False)
+ Equal(search(text, pat), ('f', (text, pat, 1, 5, False, False)))
+ engine.wrapvar.set(True)
+ engine.backvar.set(True)
+ Equal(search(text, pat), ('b', (text, pat, 1, 5, True, False)))
+ engine.backvar.set(False)
+
+ def sel(s):
+ if s == 'sel.first': return '2.10'
+ if s == 'sel.last': return '2.16'
+ raise TclError
+ text.index = sel
+ Equal(search(text, pat), ('f', (text, pat, 2, 16, True, False)))
+ Equal(search(text, pat, True), ('f', (text, pat, 2, 10, True, True)))
+ engine.backvar.set(True)
+ Equal(search(text, pat), ('b', (text, pat, 2, 10, True, False)))
+ Equal(search(text, pat, True), ('b', (text, pat, 2, 16, True, True)))
+
+
+class ForwardBackwardTest(unittest.TestCase):
+ # Test that search_forward method finds the target.
+## @classmethod
+## def tearDownClass(cls):
+## cls.root.destroy()
+## del cls.root
+
+ @classmethod
+ def setUpClass(cls):
+ cls.engine = se.SearchEngine(None)
+## requires('gui')
+## cls.root = Tk()
+## cls.text = Text(master=cls.root)
+ cls.text = mockText()
+ # search_backward calls index('end-1c')
+ cls.text.index = lambda index: '4.0'
+ test_text = (
+ 'First line\n'
+ 'Line with target\n'
+ 'Last line\n')
+ cls.text.insert('1.0', test_text)
+ cls.pat = re.compile('target')
+ cls.res = (2, (10, 16)) # line, slice indexes of 'target'
+ cls.failpat = re.compile('xyz') # not in text
+ cls.emptypat = re.compile('\w*') # empty match possible
+
+ def make_search(self, func):
+ def search(pat, line, col, wrap, ok=0):
+ res = func(self.text, pat, line, col, wrap, ok)
+ # res is (line, matchobject) or None
+ return (res[0], res[1].span()) if res else res
+ return search
+
+ def test_search_forward(self):
+ # search for non-empty match
+ Equal = self.assertEqual
+ forward = self.make_search(self.engine.search_forward)
+ pat = self.pat
+ Equal(forward(pat, 1, 0, True), self.res)
+ Equal(forward(pat, 3, 0, True), self.res) # wrap
+ Equal(forward(pat, 3, 0, False), None) # no wrap
+ Equal(forward(pat, 2, 10, False), self.res)
+
+ Equal(forward(self.failpat, 1, 0, True), None)
+ Equal(forward(self.emptypat, 2, 9, True, ok=True), (2, (9, 9)))
+ #Equal(forward(self.emptypat, 2, 9, True), self.res)
+ # While the initial empty match is correctly ignored, skipping
+ # the rest of the line and returning (3, (0,4)) seems buggy - tjr.
+ Equal(forward(self.emptypat, 2, 10, True), self.res)
+
+ def test_search_backward(self):
+ # search for non-empty match
+ Equal = self.assertEqual
+ backward = self.make_search(self.engine.search_backward)
+ pat = self.pat
+ Equal(backward(pat, 3, 5, True), self.res)
+ Equal(backward(pat, 2, 0, True), self.res) # wrap
+ Equal(backward(pat, 2, 0, False), None) # no wrap
+ Equal(backward(pat, 2, 16, False), self.res)
+
+ Equal(backward(self.failpat, 3, 9, True), None)
+ Equal(backward(self.emptypat, 2, 10, True, ok=True), (2, (9,9)))
+ # Accepted because 9 < 10, not because ok=True.
+ # It is not clear that ok=True is useful going back - tjr
+ Equal(backward(self.emptypat, 2, 9, True), (2, (5, 9)))
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=2)
diff --git a/lib/python2.7/idlelib/idle_test/test_text.py b/lib/python2.7/idlelib/idle_test/test_text.py
new file mode 100644
index 0000000..50d3fac
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_text.py
@@ -0,0 +1,227 @@
+# Test mock_tk.Text class against tkinter.Text class by running same tests with both.
+import unittest
+from test.test_support import requires
+
+from _tkinter import TclError
+
+class TextTest(object):
+
+ hw = 'hello\nworld' # usual initial insert after initialization
+ hwn = hw+'\n' # \n present at initialization, before insert
+
+ Text = None
+ def setUp(self):
+ self.text = self.Text()
+
+ def test_init(self):
+ self.assertEqual(self.text.get('1.0'), '\n')
+ self.assertEqual(self.text.get('end'), '')
+
+ def test_index_empty(self):
+ index = self.text.index
+
+ for dex in (-1.0, 0.3, '1.-1', '1.0', '1.0 lineend', '1.end', '1.33',
+ 'insert'):
+ self.assertEqual(index(dex), '1.0')
+
+ for dex in 'end', 2.0, '2.1', '33.44':
+ self.assertEqual(index(dex), '2.0')
+
+ def test_index_data(self):
+ index = self.text.index
+ self.text.insert('1.0', self.hw)
+
+ for dex in -1.0, 0.3, '1.-1', '1.0':
+ self.assertEqual(index(dex), '1.0')
+
+ for dex in '1.0 lineend', '1.end', '1.33':
+ self.assertEqual(index(dex), '1.5')
+
+ for dex in 'end', '33.44':
+ self.assertEqual(index(dex), '3.0')
+
+ def test_get(self):
+ get = self.text.get
+ Equal = self.assertEqual
+ self.text.insert('1.0', self.hw)
+
+ Equal(get('end'), '')
+ Equal(get('end', 'end'), '')
+ Equal(get('1.0'), 'h')
+ Equal(get('1.0', '1.1'), 'h')
+ Equal(get('1.0', '1.3'), 'hel')
+ Equal(get('1.1', '1.3'), 'el')
+ Equal(get('1.0', '1.0 lineend'), 'hello')
+ Equal(get('1.0', '1.10'), 'hello')
+ Equal(get('1.0 lineend'), '\n')
+ Equal(get('1.1', '2.3'), 'ello\nwor')
+ Equal(get('1.0', '2.5'), self.hw)
+ Equal(get('1.0', 'end'), self.hwn)
+ Equal(get('0.0', '5.0'), self.hwn)
+
+ def test_insert(self):
+ insert = self.text.insert
+ get = self.text.get
+ Equal = self.assertEqual
+
+ insert('1.0', self.hw)
+ Equal(get('1.0', 'end'), self.hwn)
+
+ insert('1.0', '') # nothing
+ Equal(get('1.0', 'end'), self.hwn)
+
+ insert('1.0', '*')
+ Equal(get('1.0', 'end'), '*hello\nworld\n')
+
+ insert('1.0 lineend', '*')
+ Equal(get('1.0', 'end'), '*hello*\nworld\n')
+
+ insert('2.3', '*')
+ Equal(get('1.0', 'end'), '*hello*\nwor*ld\n')
+
+ insert('end', 'x')
+ Equal(get('1.0', 'end'), '*hello*\nwor*ldx\n')
+
+ insert('1.4', 'x\n')
+ Equal(get('1.0', 'end'), '*helx\nlo*\nwor*ldx\n')
+
+ def test_no_delete(self):
+ # if index1 == 'insert' or 'end' or >= end, there is no deletion
+ delete = self.text.delete
+ get = self.text.get
+ Equal = self.assertEqual
+ self.text.insert('1.0', self.hw)
+
+ delete('insert')
+ Equal(get('1.0', 'end'), self.hwn)
+
+ delete('end')
+ Equal(get('1.0', 'end'), self.hwn)
+
+ delete('insert', 'end')
+ Equal(get('1.0', 'end'), self.hwn)
+
+ delete('insert', '5.5')
+ Equal(get('1.0', 'end'), self.hwn)
+
+ delete('1.4', '1.0')
+ Equal(get('1.0', 'end'), self.hwn)
+
+ delete('1.4', '1.4')
+ Equal(get('1.0', 'end'), self.hwn)
+
+ def test_delete_char(self):
+ delete = self.text.delete
+ get = self.text.get
+ Equal = self.assertEqual
+ self.text.insert('1.0', self.hw)
+
+ delete('1.0')
+ Equal(get('1.0', '1.end'), 'ello')
+
+ delete('1.0', '1.1')
+ Equal(get('1.0', '1.end'), 'llo')
+
+ # delete \n and combine 2 lines into 1
+ delete('1.end')
+ Equal(get('1.0', '1.end'), 'lloworld')
+
+ self.text.insert('1.3', '\n')
+ delete('1.10')
+ Equal(get('1.0', '1.end'), 'lloworld')
+
+ self.text.insert('1.3', '\n')
+ delete('1.3', '2.0')
+ Equal(get('1.0', '1.end'), 'lloworld')
+
+ def test_delete_slice(self):
+ delete = self.text.delete
+ get = self.text.get
+ Equal = self.assertEqual
+ self.text.insert('1.0', self.hw)
+
+ delete('1.0', '1.0 lineend')
+ Equal(get('1.0', 'end'), '\nworld\n')
+
+ delete('1.0', 'end')
+ Equal(get('1.0', 'end'), '\n')
+
+ self.text.insert('1.0', self.hw)
+ delete('1.0', '2.0')
+ Equal(get('1.0', 'end'), 'world\n')
+
+ delete('1.0', 'end')
+ Equal(get('1.0', 'end'), '\n')
+
+ self.text.insert('1.0', self.hw)
+ delete('1.2', '2.3')
+ Equal(get('1.0', 'end'), 'held\n')
+
+ def test_multiple_lines(self): # insert and delete
+ self.text.insert('1.0', 'hello')
+
+ self.text.insert('1.3', '1\n2\n3\n4\n5')
+ self.assertEqual(self.text.get('1.0', 'end'), 'hel1\n2\n3\n4\n5lo\n')
+
+ self.text.delete('1.3', '5.1')
+ self.assertEqual(self.text.get('1.0', 'end'), 'hello\n')
+
+ def test_compare(self):
+ compare = self.text.compare
+ Equal = self.assertEqual
+ # need data so indexes not squished to 1,0
+ self.text.insert('1.0', 'First\nSecond\nThird\n')
+
+ self.assertRaises(TclError, compare, '2.2', 'op', '2.2')
+
+ for op, less1, less0, equal, greater0, greater1 in (
+ ('<', True, True, False, False, False),
+ ('<=', True, True, True, False, False),
+ ('>', False, False, False, True, True),
+ ('>=', False, False, True, True, True),
+ ('==', False, False, True, False, False),
+ ('!=', True, True, False, True, True),
+ ):
+ Equal(compare('1.1', op, '2.2'), less1, op)
+ Equal(compare('2.1', op, '2.2'), less0, op)
+ Equal(compare('2.2', op, '2.2'), equal, op)
+ Equal(compare('2.3', op, '2.2'), greater0, op)
+ Equal(compare('3.3', op, '2.2'), greater1, op)
+
+
+class MockTextTest(TextTest, unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ from idlelib.idle_test.mock_tk import Text
+ cls.Text = Text
+
+ def test_decode(self):
+ # test endflags (-1, 0) not tested by test_index (which uses +1)
+ decode = self.text._decode
+ Equal = self.assertEqual
+ self.text.insert('1.0', self.hw)
+
+ Equal(decode('end', -1), (2, 5))
+ Equal(decode('3.1', -1), (2, 5))
+ Equal(decode('end', 0), (2, 6))
+ Equal(decode('3.1', 0), (2, 6))
+
+
+class TkTextTest(TextTest, unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ from Tkinter import Tk, Text
+ cls.Text = Text
+ cls.root = Tk()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.root
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=False)
diff --git a/lib/python2.7/idlelib/idle_test/test_textview.py b/lib/python2.7/idlelib/idle_test/test_textview.py
new file mode 100644
index 0000000..fa437fc
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_textview.py
@@ -0,0 +1,96 @@
+'''Test the functions and main class method of textView.py.'''
+
+import unittest
+import os
+from test.test_support import requires
+from Tkinter import Tk
+from idlelib import textView as tv
+from idlelib.idle_test.mock_idle import Func
+from idlelib.idle_test.mock_tk import Mbox
+
+
+class TV(tv.TextViewer): # Use in TextViewTest
+ transient = Func()
+ grab_set = Func()
+ wait_window = Func()
+
+class textviewClassTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ cls.root.withdraw()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.root
+
+ def setUp(self):
+ TV.transient.__init__()
+ TV.grab_set.__init__()
+ TV.wait_window.__init__()
+
+ def test_init_modal(self):
+ view = TV(self.root, 'Title', 'test text')
+ self.assertTrue(TV.transient.called)
+ self.assertTrue(TV.grab_set.called)
+ self.assertTrue(TV.wait_window.called)
+ view.Ok()
+
+ def test_init_nonmodal(self):
+ view = TV(self.root, 'Title', 'test text', modal=False)
+ self.assertFalse(TV.transient.called)
+ self.assertFalse(TV.grab_set.called)
+ self.assertFalse(TV.wait_window.called)
+ view.Ok()
+
+ def test_ok(self):
+ view = TV(self.root, 'Title', 'test text', modal=False)
+ view.destroy = Func()
+ view.Ok()
+ self.assertTrue(view.destroy.called)
+ del view.destroy # Unmask the real function.
+ view.destroy()
+
+
+class ViewFunctionTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ cls.root.withdraw()
+ cls.orig_mbox = tv.tkMessageBox
+ tv.tkMessageBox = Mbox
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.root
+ tv.tkMessageBox = cls.orig_mbox
+ del cls.orig_mbox
+
+ def test_view_text(self):
+ # If modal True, get tkinter error 'can't invoke "event" command'.
+ view = tv.view_text(self.root, 'Title', 'test text', modal=False)
+ self.assertIsInstance(view, tv.TextViewer)
+ view.Ok()
+
+ def test_view_file(self):
+ test_dir = os.path.dirname(__file__)
+ testfile = os.path.join(test_dir, 'test_textview.py')
+ view = tv.view_file(self.root, 'Title', testfile, modal=False)
+ self.assertIsInstance(view, tv.TextViewer)
+ self.assertIn('Test', view.textView.get('1.0', '1.end'))
+ view.Ok()
+
+ # Mock messagebox will be used; view_file will return None.
+ testfile = os.path.join(test_dir, '../notthere.py')
+ view = tv.view_file(self.root, 'Title', testfile, modal=False)
+ self.assertIsNone(view)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/lib/python2.7/idlelib/idle_test/test_warning.py b/lib/python2.7/idlelib/idle_test/test_warning.py
new file mode 100644
index 0000000..da1d8a1
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_warning.py
@@ -0,0 +1,73 @@
+'''Test warnings replacement in PyShell.py and run.py.
+
+This file could be expanded to include traceback overrides
+(in same two modules). If so, change name.
+Revise if output destination changes (http://bugs.python.org/issue18318).
+Make sure warnings module is left unaltered (http://bugs.python.org/issue18081).
+'''
+
+import unittest
+from test.test_support import captured_stderr
+
+import warnings
+# Try to capture default showwarning before Idle modules are imported.
+showwarning = warnings.showwarning
+# But if we run this file within idle, we are in the middle of the run.main loop
+# and default showwarnings has already been replaced.
+running_in_idle = 'idle' in showwarning.__name__
+
+from idlelib import run
+from idlelib import PyShell as shell
+
+# The following was generated from PyShell.idle_formatwarning
+# and checked as matching expectation.
+idlemsg = '''
+Warning (from warnings module):
+ File "test_warning.py", line 99
+ Line of code
+UserWarning: Test
+'''
+shellmsg = idlemsg + ">>> "
+
+class RunWarnTest(unittest.TestCase):
+
+ @unittest.skipIf(running_in_idle, "Does not work when run within Idle.")
+ def test_showwarnings(self):
+ self.assertIs(warnings.showwarning, showwarning)
+ run.capture_warnings(True)
+ self.assertIs(warnings.showwarning, run.idle_showwarning_subproc)
+ run.capture_warnings(False)
+ self.assertIs(warnings.showwarning, showwarning)
+
+ def test_run_show(self):
+ with captured_stderr() as f:
+ run.idle_showwarning_subproc(
+ 'Test', UserWarning, 'test_warning.py', 99, f, 'Line of code')
+ # The following uses .splitlines to erase line-ending differences
+ self.assertEqual(idlemsg.splitlines(), f.getvalue().splitlines())
+
+class ShellWarnTest(unittest.TestCase):
+
+ @unittest.skipIf(running_in_idle, "Does not work when run within Idle.")
+ def test_showwarnings(self):
+ self.assertIs(warnings.showwarning, showwarning)
+ shell.capture_warnings(True)
+ self.assertIs(warnings.showwarning, shell.idle_showwarning)
+ shell.capture_warnings(False)
+ self.assertIs(warnings.showwarning, showwarning)
+
+ def test_idle_formatter(self):
+ # Will fail if format changed without regenerating idlemsg
+ s = shell.idle_formatwarning(
+ 'Test', UserWarning, 'test_warning.py', 99, 'Line of code')
+ self.assertEqual(idlemsg, s)
+
+ def test_shell_show(self):
+ with captured_stderr() as f:
+ shell.idle_showwarning(
+ 'Test', UserWarning, 'test_warning.py', 99, f, 'Line of code')
+ self.assertEqual(shellmsg.splitlines(), f.getvalue().splitlines())
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=False)
diff --git a/lib/python2.7/idlelib/idle_test/test_widgetredir.py b/lib/python2.7/idlelib/idle_test/test_widgetredir.py
new file mode 100644
index 0000000..e35ea41
--- /dev/null
+++ b/lib/python2.7/idlelib/idle_test/test_widgetredir.py
@@ -0,0 +1,124 @@
+"""Unittest for idlelib.WidgetRedirector
+
+100% coverage
+"""
+from test.test_support import requires
+import unittest
+from idlelib.idle_test.mock_idle import Func
+from Tkinter import Tk, Text, TclError
+from idlelib.WidgetRedirector import WidgetRedirector
+
+
+class InitCloseTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ cls.root.withdraw()
+ cls.text = Text(cls.root)
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.text
+ cls.root.destroy()
+ del cls.root
+
+ def test_init(self):
+ redir = WidgetRedirector(self.text)
+ self.assertEqual(redir.widget, self.text)
+ self.assertEqual(redir.tk, self.text.tk)
+ self.assertRaises(TclError, WidgetRedirector, self.text)
+ redir.close() # restore self.tk, self.text
+
+ def test_close(self):
+ redir = WidgetRedirector(self.text)
+ redir.register('insert', Func)
+ redir.close()
+ self.assertEqual(redir._operations, {})
+ self.assertFalse(hasattr(self.text, 'widget'))
+
+
+class WidgetRedirectorTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ cls.root.withdraw()
+ cls.text = Text(cls.root)
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.text
+ cls.root.destroy()
+ del cls.root
+
+ def setUp(self):
+ self.redir = WidgetRedirector(self.text)
+ self.func = Func()
+ self.orig_insert = self.redir.register('insert', self.func)
+ self.text.insert('insert', 'asdf') # leaves self.text empty
+
+ def tearDown(self):
+ self.text.delete('1.0', 'end')
+ self.redir.close()
+
+ def test_repr(self): # partly for 100% coverage
+ self.assertIn('Redirector', repr(self.redir))
+ self.assertIn('Original', repr(self.orig_insert))
+
+ def test_register(self):
+ self.assertEqual(self.text.get('1.0', 'end'), '\n')
+ self.assertEqual(self.func.args, ('insert', 'asdf'))
+ self.assertIn('insert', self.redir._operations)
+ self.assertIn('insert', self.text.__dict__)
+ self.assertEqual(self.text.insert, self.func)
+
+ def test_original_command(self):
+ self.assertEqual(self.orig_insert.operation, 'insert')
+ self.assertEqual(self.orig_insert.tk_call, self.text.tk.call)
+ self.orig_insert('insert', 'asdf')
+ self.assertEqual(self.text.get('1.0', 'end'), 'asdf\n')
+
+ def test_unregister(self):
+ self.assertIsNone(self.redir.unregister('invalid operation name'))
+ self.assertEqual(self.redir.unregister('insert'), self.func)
+ self.assertNotIn('insert', self.redir._operations)
+ self.assertNotIn('insert', self.text.__dict__)
+
+ def test_unregister_no_attribute(self):
+ del self.text.insert
+ self.assertEqual(self.redir.unregister('insert'), self.func)
+
+ def test_dispatch_intercept(self):
+ self.func.__init__(True)
+ self.assertTrue(self.redir.dispatch('insert', False))
+ self.assertFalse(self.func.args[0])
+
+ def test_dispatch_bypass(self):
+ self.orig_insert('insert', 'asdf')
+ # tk.call returns '' where Python would return None
+ self.assertEqual(self.redir.dispatch('delete', '1.0', 'end'), '')
+ self.assertEqual(self.text.get('1.0', 'end'), '\n')
+
+ def test_dispatch_error(self):
+ self.func.__init__(TclError())
+ self.assertEqual(self.redir.dispatch('insert', False), '')
+ self.assertEqual(self.redir.dispatch('invalid'), '')
+
+ def test_command_dispatch(self):
+ # Test that .__init__ causes redirection of tk calls
+ # through redir.dispatch
+ self.root.call(self.text._w, 'insert', 'hello')
+ self.assertEqual(self.func.args, ('hello',))
+ self.assertEqual(self.text.get('1.0', 'end'), '\n')
+ # Ensure that called through redir .dispatch and not through
+ # self.text.insert by having mock raise TclError.
+ self.func.__init__(TclError())
+ self.assertEqual(self.root.call(self.text._w, 'insert', 'boo'), '')
+
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/lib/python2.7/idlelib/idlever.py b/lib/python2.7/idlelib/idlever.py
new file mode 100644
index 0000000..3e9f69a
--- /dev/null
+++ b/lib/python2.7/idlelib/idlever.py
@@ -0,0 +1,12 @@
+"""
+The separate Idle version was eliminated years ago;
+idlelib.idlever is no longer used by Idle
+and will be removed in 3.6 or later. Use
+ from sys import version
+ IDLE_VERSION = version[:version.index(' ')]
+"""
+# Kept for now only for possible existing extension use
+import warnings as w
+w.warn(__doc__, DeprecationWarning, stacklevel=2)
+from sys import version
+IDLE_VERSION = version[:version.index(' ')]
diff --git a/lib/python2.7/idlelib/keybindingDialog.py b/lib/python2.7/idlelib/keybindingDialog.py
new file mode 100644
index 0000000..4d32ca9
--- /dev/null
+++ b/lib/python2.7/idlelib/keybindingDialog.py
@@ -0,0 +1,266 @@
+"""
+Dialog for building Tkinter accelerator key bindings
+"""
+from Tkinter import *
+import tkMessageBox
+import string
+import sys
+
+class GetKeysDialog(Toplevel):
+ def __init__(self,parent,title,action,currentKeySequences,_htest=False):
+ """
+ action - string, the name of the virtual event these keys will be
+ mapped to
+ currentKeys - list, a list of all key sequence lists currently mapped
+ to virtual events, for overlap checking
+ _htest - bool, change box location when running htest
+ """
+ Toplevel.__init__(self, parent)
+ self.configure(borderwidth=5)
+ self.resizable(height=FALSE,width=FALSE)
+ self.title(title)
+ self.transient(parent)
+ self.grab_set()
+ self.protocol("WM_DELETE_WINDOW", self.Cancel)
+ self.parent = parent
+ self.action=action
+ self.currentKeySequences=currentKeySequences
+ self.result=''
+ self.keyString=StringVar(self)
+ self.keyString.set('')
+ self.SetModifiersForPlatform() # set self.modifiers, self.modifier_label
+ self.modifier_vars = []
+ for modifier in self.modifiers:
+ variable = StringVar(self)
+ variable.set('')
+ self.modifier_vars.append(variable)
+ self.advanced = False
+ self.CreateWidgets()
+ self.LoadFinalKeyList()
+ self.withdraw() #hide while setting geometry
+ self.update_idletasks()
+ self.geometry(
+ "+%d+%d" % (
+ parent.winfo_rootx() +
+ (parent.winfo_width()/2 - self.winfo_reqwidth()/2),
+ parent.winfo_rooty() +
+ ((parent.winfo_height()/2 - self.winfo_reqheight()/2)
+ if not _htest else 150)
+ ) ) #centre dialog over parent (or below htest box)
+ self.deiconify() #geometry set, unhide
+ self.wait_window()
+
+ def CreateWidgets(self):
+ frameMain = Frame(self,borderwidth=2,relief=SUNKEN)
+ frameMain.pack(side=TOP,expand=TRUE,fill=BOTH)
+ frameButtons=Frame(self)
+ frameButtons.pack(side=BOTTOM,fill=X)
+ self.buttonOK = Button(frameButtons,text='OK',
+ width=8,command=self.OK)
+ self.buttonOK.grid(row=0,column=0,padx=5,pady=5)
+ self.buttonCancel = Button(frameButtons,text='Cancel',
+ width=8,command=self.Cancel)
+ self.buttonCancel.grid(row=0,column=1,padx=5,pady=5)
+ self.frameKeySeqBasic = Frame(frameMain)
+ self.frameKeySeqAdvanced = Frame(frameMain)
+ self.frameControlsBasic = Frame(frameMain)
+ self.frameHelpAdvanced = Frame(frameMain)
+ self.frameKeySeqAdvanced.grid(row=0,column=0,sticky=NSEW,padx=5,pady=5)
+ self.frameKeySeqBasic.grid(row=0,column=0,sticky=NSEW,padx=5,pady=5)
+ self.frameKeySeqBasic.lift()
+ self.frameHelpAdvanced.grid(row=1,column=0,sticky=NSEW,padx=5)
+ self.frameControlsBasic.grid(row=1,column=0,sticky=NSEW,padx=5)
+ self.frameControlsBasic.lift()
+ self.buttonLevel = Button(frameMain,command=self.ToggleLevel,
+ text='Advanced Key Binding Entry >>')
+ self.buttonLevel.grid(row=2,column=0,stick=EW,padx=5,pady=5)
+ labelTitleBasic = Label(self.frameKeySeqBasic,
+ text="New keys for '"+self.action+"' :")
+ labelTitleBasic.pack(anchor=W)
+ labelKeysBasic = Label(self.frameKeySeqBasic,justify=LEFT,
+ textvariable=self.keyString,relief=GROOVE,borderwidth=2)
+ labelKeysBasic.pack(ipadx=5,ipady=5,fill=X)
+ self.modifier_checkbuttons = {}
+ column = 0
+ for modifier, variable in zip(self.modifiers, self.modifier_vars):
+ label = self.modifier_label.get(modifier, modifier)
+ check=Checkbutton(self.frameControlsBasic,
+ command=self.BuildKeyString,
+ text=label,variable=variable,onvalue=modifier,offvalue='')
+ check.grid(row=0,column=column,padx=2,sticky=W)
+ self.modifier_checkbuttons[modifier] = check
+ column += 1
+ labelFnAdvice=Label(self.frameControlsBasic,justify=LEFT,
+ text=\
+ "Select the desired modifier keys\n"+
+ "above, and the final key from the\n"+
+ "list on the right.\n\n" +
+ "Use upper case Symbols when using\n" +
+ "the Shift modifier. (Letters will be\n" +
+ "converted automatically.)")
+ labelFnAdvice.grid(row=1,column=0,columnspan=4,padx=2,sticky=W)
+ self.listKeysFinal=Listbox(self.frameControlsBasic,width=15,height=10,
+ selectmode=SINGLE)
+ self.listKeysFinal.bind('<ButtonRelease-1>',self.FinalKeySelected)
+ self.listKeysFinal.grid(row=0,column=4,rowspan=4,sticky=NS)
+ scrollKeysFinal=Scrollbar(self.frameControlsBasic,orient=VERTICAL,
+ command=self.listKeysFinal.yview)
+ self.listKeysFinal.config(yscrollcommand=scrollKeysFinal.set)
+ scrollKeysFinal.grid(row=0,column=5,rowspan=4,sticky=NS)
+ self.buttonClear=Button(self.frameControlsBasic,
+ text='Clear Keys',command=self.ClearKeySeq)
+ self.buttonClear.grid(row=2,column=0,columnspan=4)
+ labelTitleAdvanced = Label(self.frameKeySeqAdvanced,justify=LEFT,
+ text="Enter new binding(s) for '"+self.action+"' :\n"+
+ "(These bindings will not be checked for validity!)")
+ labelTitleAdvanced.pack(anchor=W)
+ self.entryKeysAdvanced=Entry(self.frameKeySeqAdvanced,
+ textvariable=self.keyString)
+ self.entryKeysAdvanced.pack(fill=X)
+ labelHelpAdvanced=Label(self.frameHelpAdvanced,justify=LEFT,
+ text="Key bindings are specified using Tkinter keysyms as\n"+
+ "in these samples: <Control-f>, <Shift-F2>, <F12>,\n"
+ "<Control-space>, <Meta-less>, <Control-Alt-Shift-X>.\n"
+ "Upper case is used when the Shift modifier is present!\n\n" +
+ "'Emacs style' multi-keystroke bindings are specified as\n" +
+ "follows: <Control-x><Control-y>, where the first key\n" +
+ "is the 'do-nothing' keybinding.\n\n" +
+ "Multiple separate bindings for one action should be\n"+
+ "separated by a space, eg., <Alt-v> <Meta-v>." )
+ labelHelpAdvanced.grid(row=0,column=0,sticky=NSEW)
+
+ def SetModifiersForPlatform(self):
+ """Determine list of names of key modifiers for this platform.
+
+ The names are used to build Tk bindings -- it doesn't matter if the
+ keyboard has these keys, it matters if Tk understands them. The
+ order is also important: key binding equality depends on it, so
+ config-keys.def must use the same ordering.
+ """
+ if sys.platform == "darwin":
+ self.modifiers = ['Shift', 'Control', 'Option', 'Command']
+ else:
+ self.modifiers = ['Control', 'Alt', 'Shift']
+ self.modifier_label = {'Control': 'Ctrl'} # short name
+
+ def ToggleLevel(self):
+ if self.buttonLevel.cget('text')[:8]=='Advanced':
+ self.ClearKeySeq()
+ self.buttonLevel.config(text='<< Basic Key Binding Entry')
+ self.frameKeySeqAdvanced.lift()
+ self.frameHelpAdvanced.lift()
+ self.entryKeysAdvanced.focus_set()
+ self.advanced = True
+ else:
+ self.ClearKeySeq()
+ self.buttonLevel.config(text='Advanced Key Binding Entry >>')
+ self.frameKeySeqBasic.lift()
+ self.frameControlsBasic.lift()
+ self.advanced = False
+
+ def FinalKeySelected(self,event):
+ self.BuildKeyString()
+
+ def BuildKeyString(self):
+ keyList = modifiers = self.GetModifiers()
+ finalKey = self.listKeysFinal.get(ANCHOR)
+ if finalKey:
+ finalKey = self.TranslateKey(finalKey, modifiers)
+ keyList.append(finalKey)
+ self.keyString.set('<' + string.join(keyList,'-') + '>')
+
+ def GetModifiers(self):
+ modList = [variable.get() for variable in self.modifier_vars]
+ return [mod for mod in modList if mod]
+
+ def ClearKeySeq(self):
+ self.listKeysFinal.select_clear(0,END)
+ self.listKeysFinal.yview(MOVETO, '0.0')
+ for variable in self.modifier_vars:
+ variable.set('')
+ self.keyString.set('')
+
+ def LoadFinalKeyList(self):
+ #these tuples are also available for use in validity checks
+ self.functionKeys=('F1','F2','F2','F4','F5','F6','F7','F8','F9',
+ 'F10','F11','F12')
+ self.alphanumKeys=tuple(string.ascii_lowercase+string.digits)
+ self.punctuationKeys=tuple('~!@#%^&*()_-+={}[]|;:,.<>/?')
+ self.whitespaceKeys=('Tab','Space','Return')
+ self.editKeys=('BackSpace','Delete','Insert')
+ self.moveKeys=('Home','End','Page Up','Page Down','Left Arrow',
+ 'Right Arrow','Up Arrow','Down Arrow')
+ #make a tuple of most of the useful common 'final' keys
+ keys=(self.alphanumKeys+self.punctuationKeys+self.functionKeys+
+ self.whitespaceKeys+self.editKeys+self.moveKeys)
+ self.listKeysFinal.insert(END, *keys)
+
+ def TranslateKey(self, key, modifiers):
+ "Translate from keycap symbol to the Tkinter keysym"
+ translateDict = {'Space':'space',
+ '~':'asciitilde','!':'exclam','@':'at','#':'numbersign',
+ '%':'percent','^':'asciicircum','&':'ampersand','*':'asterisk',
+ '(':'parenleft',')':'parenright','_':'underscore','-':'minus',
+ '+':'plus','=':'equal','{':'braceleft','}':'braceright',
+ '[':'bracketleft',']':'bracketright','|':'bar',';':'semicolon',
+ ':':'colon',',':'comma','.':'period','<':'less','>':'greater',
+ '/':'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():
+ key = translateDict[key]
+ if 'Shift' in modifiers and key in string.ascii_lowercase:
+ key = key.upper()
+ key = 'Key-' + key
+ return key
+
+ def OK(self, event=None):
+ if self.advanced or self.KeysOK(): # doesn't check advanced string yet
+ self.result=self.keyString.get()
+ self.destroy()
+
+ def Cancel(self, event=None):
+ self.result=''
+ self.destroy()
+
+ def KeysOK(self):
+ '''Validity check on user's 'basic' keybinding selection.
+
+ Doesn't check the string produced by the advanced dialog because
+ 'modifiers' isn't set.
+
+ '''
+ keys = self.keyString.get()
+ keys.strip()
+ finalKey = self.listKeysFinal.get(ANCHOR)
+ modifiers = self.GetModifiers()
+ # create a key sequence list for overlap check:
+ keySequence = keys.split()
+ keysOK = False
+ title = 'Key Sequence Error'
+ if not keys:
+ tkMessageBox.showerror(title=title, parent=self,
+ message='No keys specified.')
+ elif not keys.endswith('>'):
+ tkMessageBox.showerror(title=title, parent=self,
+ message='Missing the final Key')
+ elif (not modifiers
+ and finalKey not in self.functionKeys + self.moveKeys):
+ tkMessageBox.showerror(title=title, parent=self,
+ message='No modifier key(s) specified.')
+ elif (modifiers == ['Shift']) \
+ and (finalKey not in
+ self.functionKeys + self.moveKeys + ('Tab', 'Space')):
+ msg = 'The shift modifier by itself may not be used with'\
+ ' this key symbol.'
+ tkMessageBox.showerror(title=title, parent=self, message=msg)
+ elif keySequence in self.currentKeySequences:
+ msg = 'This key combination is already in use.'
+ tkMessageBox.showerror(title=title, parent=self, message=msg)
+ else:
+ keysOK = True
+ return keysOK
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(GetKeysDialog)
diff --git a/lib/python2.7/idlelib/macosxSupport.py b/lib/python2.7/idlelib/macosxSupport.py
new file mode 100644
index 0000000..041d700
--- /dev/null
+++ b/lib/python2.7/idlelib/macosxSupport.py
@@ -0,0 +1,237 @@
+"""
+A number of functions that enhance IDLE on Mac OSX.
+"""
+import sys
+import Tkinter
+from os import path
+
+
+import warnings
+
+def runningAsOSXApp():
+ warnings.warn("runningAsOSXApp() is deprecated, use isAquaTk()",
+ DeprecationWarning, stacklevel=2)
+ return isAquaTk()
+
+def isCarbonAquaTk(root):
+ warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()",
+ DeprecationWarning, stacklevel=2)
+ return isCarbonTk()
+
+_tk_type = None
+
+def _initializeTkVariantTests(root):
+ """
+ Initializes OS X Tk variant values for
+ isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
+ """
+ global _tk_type
+ if sys.platform == 'darwin':
+ ws = root.tk.call('tk', 'windowingsystem')
+ if 'x11' in ws:
+ _tk_type = "xquartz"
+ elif 'aqua' not in ws:
+ _tk_type = "other"
+ elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
+ _tk_type = "cocoa"
+ else:
+ _tk_type = "carbon"
+ else:
+ _tk_type = "other"
+
+def isAquaTk():
+ """
+ Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
+ """
+ assert _tk_type is not None
+ return _tk_type == "cocoa" or _tk_type == "carbon"
+
+def isCarbonTk():
+ """
+ Returns True if IDLE is using a Carbon Aqua Tk (instead of the
+ newer Cocoa Aqua Tk).
+ """
+ assert _tk_type is not None
+ return _tk_type == "carbon"
+
+def isCocoaTk():
+ """
+ Returns True if IDLE is using a Cocoa Aqua Tk.
+ """
+ assert _tk_type is not None
+ return _tk_type == "cocoa"
+
+def isXQuartz():
+ """
+ Returns True if IDLE is using an OS X X11 Tk.
+ """
+ assert _tk_type is not None
+ return _tk_type == "xquartz"
+
+def tkVersionWarning(root):
+ """
+ Returns a string warning message if the Tk version in use appears to
+ be one known to cause problems with IDLE.
+ 1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable.
+ 2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but
+ can still crash unexpectedly.
+ """
+
+ if isCocoaTk():
+ patchlevel = root.tk.call('info', 'patchlevel')
+ if patchlevel not in ('8.5.7', '8.5.9'):
+ return False
+ return (r"WARNING: The version of Tcl/Tk ({0}) in use may"
+ r" be unstable.\n"
+ r"Visit http://www.python.org/download/mac/tcltk/"
+ r" for current information.".format(patchlevel))
+ else:
+ return False
+
+def addOpenEventSupport(root, flist):
+ """
+ This ensures that the application will respond to open AppleEvents, which
+ makes is feasible to use IDLE as the default application for python files.
+ """
+ def doOpenFile(*args):
+ for fn in args:
+ flist.open(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,
+ # one for every file that should be opened.
+ root.createcommand("::tk::mac::OpenDocument", doOpenFile)
+
+def hideTkConsole(root):
+ try:
+ root.tk.call('console', 'hide')
+ except Tkinter.TclError:
+ # Some versions of the Tk framework don't have a console object
+ pass
+
+def overrideRootMenu(root, flist):
+ """
+ Replace the Tk root menu by something that is more appropriate for
+ IDLE with an Aqua Tk.
+ """
+ # The menu that is attached to the Tk root (".") is also used by AquaTk for
+ # all windows that don't specify a menu of their own. The default menubar
+ # contains a number of menus, none of which are appropriate for IDLE. The
+ # Most annoying of those is an 'About Tck/Tk...' menu in the application
+ # menu.
+ #
+ # This function replaces the default menubar by a mostly empty one, it
+ # should only contain the correct application menu and the window menu.
+ #
+ # Due to a (mis-)feature of TkAqua the user will also see an empty Help
+ # menu.
+ from Tkinter import Menu
+ from idlelib import Bindings
+ from idlelib import WindowList
+
+ closeItem = Bindings.menudefs[0][1][-2]
+
+ # Remove the last 3 items of the file menu: a separator, close window and
+ # quit. Close window will be reinserted just above the save item, where
+ # it should be according to the HIG. Quit is in the application menu.
+ del Bindings.menudefs[0][1][-3:]
+ Bindings.menudefs[0][1].insert(6, closeItem)
+
+ # Remove the 'About' entry from the help menu, it is in the application
+ # menu
+ del Bindings.menudefs[-1][1][0:2]
+ # Remove the 'Configure Idle' entry from the options menu, it is in the
+ # application menu as 'Preferences'
+ del Bindings.menudefs[-2][1][0]
+ menubar = Menu(root)
+ root.configure(menu=menubar)
+ menudict = {}
+
+ menudict['windows'] = menu = Menu(menubar, name='windows', tearoff=0)
+ menubar.add_cascade(label='Window', menu=menu, underline=0)
+
+ def postwindowsmenu(menu=menu):
+ end = menu.index('end')
+ if end is None:
+ end = -1
+
+ if end > 0:
+ menu.delete(0, end)
+ WindowList.add_windows_to_menu(menu)
+ WindowList.register_callback(postwindowsmenu)
+
+ def about_dialog(event=None):
+ "Handle Help 'About IDLE' event."
+ # Synchronize with EditorWindow.EditorWindow.about_dialog.
+ from idlelib import aboutDialog
+ aboutDialog.AboutDialog(root, 'About IDLE')
+
+ def config_dialog(event=None):
+ "Handle Options 'Configure IDLE' event."
+ # Synchronize with EditorWindow.EditorWindow.config_dialog.
+ from idlelib import configDialog
+ root.instance_dict = flist.inversedict
+ configDialog.ConfigDialog(root, 'Settings')
+
+ def help_dialog(event=None):
+ "Handle Help 'IDLE Help' event."
+ # Synchronize with EditorWindow.EditorWindow.help_dialog.
+ from idlelib import help
+ help.show_idlehelp(root)
+
+ root.bind('<<about-idle>>', about_dialog)
+ root.bind('<<open-config-dialog>>', config_dialog)
+ root.createcommand('::tk::mac::ShowPreferences', config_dialog)
+ if flist:
+ root.bind('<<close-all-windows>>', flist.close_all_callback)
+
+ # The binding above doesn't reliably work on all versions of Tk
+ # on MacOSX. Adding command definition below does seem to do the
+ # right thing for now.
+ root.createcommand('exit', flist.close_all_callback)
+
+ if isCarbonTk():
+ # for Carbon AquaTk, replace the default Tk apple menu
+ menudict['application'] = menu = Menu(menubar, name='apple',
+ tearoff=0)
+ menubar.add_cascade(label='IDLE', menu=menu)
+ Bindings.menudefs.insert(0,
+ ('application', [
+ ('About IDLE', '<<about-idle>>'),
+ None,
+ ]))
+ tkversion = root.tk.eval('info patchlevel')
+ if tuple(map(int, tkversion.split('.'))) < (8, 4, 14):
+ # for earlier AquaTk versions, supply a Preferences menu item
+ Bindings.menudefs[0][1].append(
+ ('_Preferences....', '<<open-config-dialog>>'),
+ )
+ if isCocoaTk():
+ # replace default About dialog with About IDLE one
+ root.createcommand('tkAboutDialog', about_dialog)
+ # replace default "Help" item in Help menu
+ root.createcommand('::tk::mac::ShowHelp', help_dialog)
+ # remove redundant "IDLE Help" from menu
+ del Bindings.menudefs[-1][1][0]
+
+def setupApp(root, flist):
+ """
+ Perform initial OS X customizations if needed.
+ Called from PyShell.main() after initial calls to Tk()
+
+ There are currently three major versions of Tk in use on OS X:
+ 1. Aqua Cocoa Tk (native default since OS X 10.6)
+ 2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
+ 3. X11 (supported by some third-party distributors, deprecated)
+ There are various differences among the three that affect IDLE
+ behavior, primarily with menus, mouse key events, and accelerators.
+ Some one-time customizations are performed here.
+ Others are dynamically tested throughout idlelib by calls to the
+ isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
+ are initialized here as well.
+ """
+ _initializeTkVariantTests(root)
+ if isAquaTk():
+ hideTkConsole(root)
+ overrideRootMenu(root, flist)
+ addOpenEventSupport(root, flist)
diff --git a/lib/python2.7/idlelib/rpc.py b/lib/python2.7/idlelib/rpc.py
new file mode 100644
index 0000000..43328e7
--- /dev/null
+++ b/lib/python2.7/idlelib/rpc.py
@@ -0,0 +1,597 @@
+"""RPC Implementation, originally written for the Python Idle IDE
+
+For security reasons, GvR requested that Idle's Python execution server process
+connect to the Idle process, which listens for the connection. Since Idle has
+only one client per server, this was not a limitation.
+
+ +---------------------------------+ +-------------+
+ | SocketServer.BaseRequestHandler | | SocketIO |
+ +---------------------------------+ +-------------+
+ ^ | register() |
+ | | unregister()|
+ | +-------------+
+ | ^ ^
+ | | |
+ | + -------------------+ |
+ | | |
+ +-------------------------+ +-----------------+
+ | RPCHandler | | RPCClient |
+ | [attribute of RPCServer]| | |
+ +-------------------------+ +-----------------+
+
+The RPCServer handler class is expected to provide register/unregister methods.
+RPCHandler inherits the mix-in class SocketIO, which provides these methods.
+
+See the Idle run.main() docstring for further information on how this was
+accomplished in Idle.
+
+"""
+
+import sys
+import os
+import socket
+import select
+import SocketServer
+import struct
+import cPickle as pickle
+import threading
+import Queue
+import traceback
+import copy_reg
+import types
+import marshal
+
+
+def unpickle_code(ms):
+ co = marshal.loads(ms)
+ assert isinstance(co, types.CodeType)
+ return co
+
+def pickle_code(co):
+ assert isinstance(co, types.CodeType)
+ ms = marshal.dumps(co)
+ return unpickle_code, (ms,)
+
+# XXX KBK 24Aug02 function pickling capability not used in Idle
+# def unpickle_function(ms):
+# return ms
+
+# def pickle_function(fn):
+# assert isinstance(fn, type.FunctionType)
+# return repr(fn)
+
+copy_reg.pickle(types.CodeType, pickle_code, unpickle_code)
+# copy_reg.pickle(types.FunctionType, pickle_function, unpickle_function)
+
+BUFSIZE = 8*1024
+LOCALHOST = '127.0.0.1'
+
+class RPCServer(SocketServer.TCPServer):
+
+ def __init__(self, addr, handlerclass=None):
+ if handlerclass is None:
+ handlerclass = RPCHandler
+ SocketServer.TCPServer.__init__(self, addr, handlerclass)
+
+ def server_bind(self):
+ "Override TCPServer method, no bind() phase for connecting entity"
+ pass
+
+ def server_activate(self):
+ """Override TCPServer method, connect() instead of listen()
+
+ Due to the reversed connection, self.server_address is actually the
+ address of the Idle Client to which we are connecting.
+
+ """
+ self.socket.connect(self.server_address)
+
+ def get_request(self):
+ "Override TCPServer method, return already connected socket"
+ return self.socket, self.server_address
+
+ def handle_error(self, request, client_address):
+ """Override TCPServer method
+
+ Error message goes to __stderr__. No error message if exiting
+ normally or socket raised EOF. Other exceptions not handled in
+ server code will cause os._exit.
+
+ """
+ try:
+ raise
+ except SystemExit:
+ 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)
+ traceback.print_exc(file=erf)
+ print>>erf, '\n*** Unrecoverable, server exiting!'
+ print>>erf, '-'*40
+ os._exit(0)
+
+#----------------- end class RPCServer --------------------
+
+objecttable = {}
+request_queue = Queue.Queue(0)
+response_queue = Queue.Queue(0)
+
+
+class SocketIO(object):
+
+ nextseq = 0
+
+ def __init__(self, sock, objtable=None, debugging=None):
+ self.sockthread = threading.currentThread()
+ if debugging is not None:
+ self.debugging = debugging
+ self.sock = sock
+ if objtable is None:
+ objtable = objecttable
+ self.objtable = objtable
+ self.responses = {}
+ self.cvars = {}
+
+ def close(self):
+ sock = self.sock
+ self.sock = None
+ if sock is not None:
+ sock.close()
+
+ def exithook(self):
+ "override for specific exit action"
+ os._exit(0)
+
+ def debug(self, *args):
+ if not self.debugging:
+ return
+ s = self.location + " " + str(threading.currentThread().getName())
+ for a in args:
+ s = s + " " + str(a)
+ print>>sys.__stderr__, s
+
+ def register(self, oid, object):
+ self.objtable[oid] = object
+
+ def unregister(self, oid):
+ try:
+ del self.objtable[oid]
+ except KeyError:
+ pass
+
+ def localcall(self, seq, request):
+ self.debug("localcall:", request)
+ try:
+ how, (oid, methodname, args, kwargs) = request
+ except TypeError:
+ return ("ERROR", "Bad request format")
+ if oid not in self.objtable:
+ return ("ERROR", "Unknown object id: %r" % (oid,))
+ obj = self.objtable[oid]
+ if methodname == "__methods__":
+ methods = {}
+ _getmethods(obj, methods)
+ return ("OK", methods)
+ if methodname == "__attributes__":
+ attributes = {}
+ _getattributes(obj, attributes)
+ return ("OK", attributes)
+ if not hasattr(obj, methodname):
+ return ("ERROR", "Unsupported method name: %r" % (methodname,))
+ method = getattr(obj, methodname)
+ try:
+ if how == 'CALL':
+ ret = method(*args, **kwargs)
+ if isinstance(ret, RemoteObject):
+ ret = remoteref(ret)
+ return ("OK", ret)
+ elif how == 'QUEUE':
+ request_queue.put((seq, (method, args, kwargs)))
+ return("QUEUED", None)
+ else:
+ return ("ERROR", "Unsupported message type: %s" % how)
+ except SystemExit:
+ raise
+ except socket.error:
+ raise
+ 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)
+ traceback.print_exc(file=sys.__stderr__)
+ return ("EXCEPTION", None)
+
+ def remotecall(self, oid, methodname, args, kwargs):
+ self.debug("remotecall:asynccall: ", oid, methodname)
+ seq = self.asynccall(oid, methodname, args, kwargs)
+ return self.asyncreturn(seq)
+
+ def remotequeue(self, oid, methodname, args, kwargs):
+ self.debug("remotequeue:asyncqueue: ", oid, methodname)
+ seq = self.asyncqueue(oid, methodname, args, kwargs)
+ return self.asyncreturn(seq)
+
+ def asynccall(self, oid, methodname, args, kwargs):
+ request = ("CALL", (oid, methodname, args, kwargs))
+ seq = self.newseq()
+ if threading.currentThread() != self.sockthread:
+ cvar = threading.Condition()
+ self.cvars[seq] = cvar
+ self.debug(("asynccall:%d:" % seq), oid, methodname, args, kwargs)
+ self.putmessage((seq, request))
+ return seq
+
+ def asyncqueue(self, oid, methodname, args, kwargs):
+ request = ("QUEUE", (oid, methodname, args, kwargs))
+ seq = self.newseq()
+ if threading.currentThread() != self.sockthread:
+ cvar = threading.Condition()
+ self.cvars[seq] = cvar
+ self.debug(("asyncqueue:%d:" % seq), oid, methodname, args, kwargs)
+ self.putmessage((seq, request))
+ return seq
+
+ def asyncreturn(self, seq):
+ self.debug("asyncreturn:%d:call getresponse(): " % seq)
+ response = self.getresponse(seq, wait=0.05)
+ self.debug(("asyncreturn:%d:response: " % seq), response)
+ return self.decoderesponse(response)
+
+ def decoderesponse(self, response):
+ how, what = response
+ if how == "OK":
+ return what
+ if how == "QUEUED":
+ return None
+ if how == "EXCEPTION":
+ self.debug("decoderesponse: EXCEPTION")
+ return None
+ if how == "EOF":
+ self.debug("decoderesponse: EOF")
+ self.decode_interrupthook()
+ return None
+ if how == "ERROR":
+ self.debug("decoderesponse: Internal ERROR:", what)
+ raise RuntimeError, what
+ raise SystemError, (how, what)
+
+ def decode_interrupthook(self):
+ ""
+ raise EOFError
+
+ def mainloop(self):
+ """Listen on socket until I/O not ready or EOF
+
+ pollresponse() will loop looking for seq number None, which
+ never comes, and exit on EOFError.
+
+ """
+ try:
+ self.getresponse(myseq=None, wait=0.05)
+ except EOFError:
+ self.debug("mainloop:return")
+ return
+
+ def getresponse(self, myseq, wait):
+ response = self._getresponse(myseq, wait)
+ if response is not None:
+ how, what = response
+ if how == "OK":
+ response = how, self._proxify(what)
+ return response
+
+ def _proxify(self, obj):
+ if isinstance(obj, RemoteProxy):
+ return RPCProxy(self, obj.oid)
+ if isinstance(obj, types.ListType):
+ return 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:
+ # this thread does all reading of requests or responses
+ while 1:
+ response = self.pollresponse(myseq, wait)
+ if response is not None:
+ return response
+ else:
+ # wait for notification from socket handling thread
+ cvar = self.cvars[myseq]
+ cvar.acquire()
+ while myseq not in self.responses:
+ cvar.wait()
+ response = self.responses[myseq]
+ self.debug("_getresponse:%s: thread woke up: response: %s" %
+ (myseq, response))
+ del self.responses[myseq]
+ del self.cvars[myseq]
+ cvar.release()
+ return response
+
+ def newseq(self):
+ self.nextseq = seq = self.nextseq + 2
+ return seq
+
+ def putmessage(self, message):
+ self.debug("putmessage:%d:" % message[0])
+ try:
+ s = pickle.dumps(message)
+ except pickle.PicklingError:
+ print >>sys.__stderr__, "Cannot pickle:", repr(message)
+ raise
+ s = struct.pack("<i", len(s)) + s
+ while len(s) > 0:
+ try:
+ r, w, x = select.select([], [self.sock], [])
+ n = self.sock.send(s[:BUFSIZE])
+ except (AttributeError, TypeError):
+ raise IOError, "socket no longer exists"
+ s = s[n:]
+
+ buffer = ""
+ bufneed = 4
+ bufstate = 0 # meaning: 0 => reading count; 1 => reading data
+
+ def pollpacket(self, wait):
+ self._stage0()
+ if len(self.buffer) < self.bufneed:
+ r, w, x = select.select([self.sock.fileno()], [], [], wait)
+ if len(r) == 0:
+ return None
+ try:
+ s = self.sock.recv(BUFSIZE)
+ except socket.error:
+ raise EOFError
+ if len(s) == 0:
+ raise EOFError
+ self.buffer += 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:]
+ self.bufneed = struct.unpack("<i", s)[0]
+ self.bufstate = 1
+
+ def _stage1(self):
+ if self.bufstate == 1 and len(self.buffer) >= self.bufneed:
+ packet = self.buffer[:self.bufneed]
+ self.buffer = self.buffer[self.bufneed:]
+ self.bufneed = 4
+ self.bufstate = 0
+ return packet
+
+ def pollmessage(self, wait):
+ packet = self.pollpacket(wait)
+ if packet is None:
+ return None
+ try:
+ message = pickle.loads(packet)
+ except pickle.UnpicklingError:
+ print >>sys.__stderr__, "-----------------------"
+ print >>sys.__stderr__, "cannot unpickle packet:", repr(packet)
+ traceback.print_stack(file=sys.__stderr__)
+ print >>sys.__stderr__, "-----------------------"
+ raise
+ return message
+
+ def pollresponse(self, myseq, wait):
+ """Handle messages received on the socket.
+
+ Some messages received may be asynchronous 'call' or 'queue' requests,
+ and some may be responses for other threads.
+
+ 'call' requests are passed to self.localcall() with the expectation of
+ immediate execution, during which time the socket is not serviced.
+
+ 'queue' requests are used for tasks (which may block or hang) to be
+ processed in a different thread. These requests are fed into
+ request_queue by self.localcall(). Responses to queued requests are
+ taken from response_queue and sent across the link with the associated
+ sequence numbers. Messages in the queues are (sequence_number,
+ request/response) tuples and code using this module removing messages
+ from the request_queue is responsible for returning the correct
+ sequence number in the response_queue.
+
+ pollresponse() will loop until a response message with the myseq
+ sequence number is received, and will save other responses in
+ self.responses and notify the owning thread.
+
+ """
+ while 1:
+ # send queued response if there is one available
+ try:
+ qmsg = response_queue.get(0)
+ except Queue.Empty:
+ pass
+ else:
+ seq, response = qmsg
+ message = (seq, ('OK', response))
+ self.putmessage(message)
+ # poll for message on link
+ try:
+ message = self.pollmessage(wait)
+ if message is None: # socket not ready
+ return None
+ except EOFError:
+ self.handle_EOF()
+ return None
+ except AttributeError:
+ return None
+ seq, resq = message
+ how = resq[0]
+ self.debug("pollresponse:%d:myseq:%s" % (seq, myseq))
+ # process or queue a request
+ if how in ("CALL", "QUEUE"):
+ self.debug("pollresponse:%d:localcall:call:" % seq)
+ response = self.localcall(seq, resq)
+ self.debug("pollresponse:%d:localcall:response:%s"
+ % (seq, response))
+ if how == "CALL":
+ self.putmessage((seq, response))
+ elif how == "QUEUE":
+ # don't acknowledge the 'queue' request!
+ pass
+ continue
+ # return if completed message transaction
+ elif seq == myseq:
+ return resq
+ # must be a response for a different thread:
+ else:
+ cv = self.cvars.get(seq, None)
+ # response involving unknown sequence number is discarded,
+ # probably intended for prior incarnation of server
+ if cv is not None:
+ cv.acquire()
+ self.responses[seq] = resq
+ cv.notify()
+ cv.release()
+ continue
+
+ def handle_EOF(self):
+ "action taken upon link being closed by peer"
+ self.EOFhook()
+ self.debug("handle_EOF")
+ for key in self.cvars:
+ cv = self.cvars[key]
+ cv.acquire()
+ self.responses[key] = ('EOF', None)
+ cv.notify()
+ cv.release()
+ # call our (possibly overridden) exit function
+ self.exithook()
+
+ def EOFhook(self):
+ "Classes using rpc client/server can override to augment EOF action"
+ pass
+
+#----------------- end class SocketIO --------------------
+
+class RemoteObject(object):
+ # Token mix-in class
+ pass
+
+def remoteref(obj):
+ oid = id(obj)
+ objecttable[oid] = obj
+ return RemoteProxy(oid)
+
+class RemoteProxy(object):
+
+ def __init__(self, oid):
+ self.oid = oid
+
+class RPCHandler(SocketServer.BaseRequestHandler, SocketIO):
+
+ debugging = False
+ location = "#S" # Server
+
+ def __init__(self, sock, addr, svr):
+ svr.current_handler = self ## cgt xxx
+ SocketIO.__init__(self, sock)
+ SocketServer.BaseRequestHandler.__init__(self, sock, addr, svr)
+
+ def handle(self):
+ "handle() method required by SocketServer"
+ self.mainloop()
+
+ def get_remote_proxy(self, oid):
+ return RPCProxy(self, oid)
+
+class RPCClient(SocketIO):
+
+ debugging = False
+ location = "#C" # Client
+
+ nextseq = 1 # Requests coming from the client are odd numbered
+
+ def __init__(self, address, family=socket.AF_INET, type=socket.SOCK_STREAM):
+ self.listening_sock = socket.socket(family, type)
+ self.listening_sock.bind(address)
+ self.listening_sock.listen(1)
+
+ def accept(self):
+ working_sock, address = self.listening_sock.accept()
+ if self.debugging:
+ print>>sys.__stderr__, "****** Connection request from ", address
+ if address[0] == LOCALHOST:
+ SocketIO.__init__(self, working_sock)
+ else:
+ print>>sys.__stderr__, "** Invalid host: ", address
+ raise socket.error
+
+ def get_remote_proxy(self, oid):
+ return RPCProxy(self, oid)
+
+class RPCProxy(object):
+
+ __methods = None
+ __attributes = None
+
+ def __init__(self, sockio, oid):
+ self.sockio = sockio
+ self.oid = oid
+
+ def __getattr__(self, name):
+ if self.__methods is None:
+ self.__getmethods()
+ if self.__methods.get(name):
+ return MethodProxy(self.sockio, self.oid, name)
+ if self.__attributes is None:
+ self.__getattributes()
+ if name in self.__attributes:
+ value = self.sockio.remotecall(self.oid, '__getattribute__',
+ (name,), {})
+ return value
+ else:
+ raise AttributeError, name
+
+ def __getattributes(self):
+ self.__attributes = self.sockio.remotecall(self.oid,
+ "__attributes__", (), {})
+
+ def __getmethods(self):
+ self.__methods = self.sockio.remotecall(self.oid,
+ "__methods__", (), {})
+
+def _getmethods(obj, methods):
+ # Helper to get a list of methods from an object
+ # Adds names to dictionary argument 'methods'
+ for name in dir(obj):
+ 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:
+ for super in obj.__bases__:
+ _getmethods(super, methods)
+
+def _getattributes(obj, attributes):
+ for name in dir(obj):
+ attr = getattr(obj, name)
+ if not hasattr(attr, '__call__'):
+ attributes[name] = 1
+
+class MethodProxy(object):
+
+ def __init__(self, sockio, oid, name):
+ self.sockio = sockio
+ self.oid = oid
+ self.name = name
+
+ def __call__(self, *args, **kwargs):
+ value = self.sockio.remotecall(self.oid, self.name, args, kwargs)
+ return value
+
+
+# XXX KBK 09Sep03 We need a proper unit test for this module. Previously
+# existing test code was removed at Rev 1.27 (r34098).
diff --git a/lib/python2.7/idlelib/run.py b/lib/python2.7/idlelib/run.py
new file mode 100644
index 0000000..466c61e
--- /dev/null
+++ b/lib/python2.7/idlelib/run.py
@@ -0,0 +1,375 @@
+import sys
+import linecache
+import time
+import socket
+import traceback
+import thread
+import threading
+import Queue
+
+from idlelib import CallTips
+from idlelib import AutoComplete
+
+from idlelib import RemoteDebugger
+from idlelib import RemoteObjectBrowser
+from idlelib import StackViewer
+from idlelib import rpc
+from idlelib import PyShell
+from idlelib import IOBinding
+
+import __main__
+
+LOCALHOST = '127.0.0.1'
+
+import warnings
+
+def idle_showwarning_subproc(
+ message, category, filename, lineno, file=None, line=None):
+ """Show Idle-format warning after replacing warnings.showwarning.
+
+ The only difference is the formatter called.
+ """
+ if file is None:
+ file = sys.stderr
+ try:
+ file.write(PyShell.idle_formatwarning(
+ message, category, filename, lineno, line))
+ except IOError:
+ pass # the file (probably stderr) is invalid - this warning gets lost.
+
+_warnings_showwarning = None
+
+def capture_warnings(capture):
+ "Replace warning.showwarning with idle_showwarning_subproc, or reverse."
+
+ global _warnings_showwarning
+ if capture:
+ if _warnings_showwarning is None:
+ _warnings_showwarning = warnings.showwarning
+ warnings.showwarning = idle_showwarning_subproc
+ else:
+ if _warnings_showwarning is not None:
+ warnings.showwarning = _warnings_showwarning
+ _warnings_showwarning = None
+
+capture_warnings(True)
+
+# Thread shared globals: Establish a queue between a subthread (which handles
+# the socket) and the main thread (which runs user code), plus global
+# completion, exit and interruptable (the main thread) flags:
+
+exit_now = False
+quitting = False
+interruptable = False
+
+def main(del_exitfunc=False):
+ """Start the Python execution server in a subprocess
+
+ In the Python subprocess, RPCServer is instantiated with handlerclass
+ MyHandler, which inherits register/unregister methods from RPCHandler via
+ the mix-in class SocketIO.
+
+ When the RPCServer 'server' is instantiated, the TCPServer initialization
+ creates an instance of run.MyHandler and calls its handle() method.
+ handle() instantiates a run.Executive object, passing it a reference to the
+ MyHandler object. That reference is saved as attribute rpchandler of the
+ Executive instance. The Executive methods have access to the reference and
+ can pass it on to entities that they command
+ (e.g. RemoteDebugger.Debugger.start_debugger()). The latter, in turn, can
+ call MyHandler(SocketIO) register/unregister methods via the reference to
+ register and unregister themselves.
+
+ """
+ global exit_now
+ global quitting
+ global no_exitfunc
+ no_exitfunc = del_exitfunc
+ #time.sleep(15) # test subprocess not responding
+ try:
+ assert(len(sys.argv) > 1)
+ port = int(sys.argv[-1])
+ except:
+ print>>sys.stderr, "IDLE Subprocess: no IP port passed in sys.argv."
+ return
+
+ capture_warnings(True)
+ sys.argv[:] = [""]
+ sockthread = threading.Thread(target=manage_socket,
+ name='SockThread',
+ args=((LOCALHOST, port),))
+ sockthread.setDaemon(True)
+ sockthread.start()
+ while 1:
+ try:
+ if exit_now:
+ try:
+ exit()
+ except KeyboardInterrupt:
+ # exiting but got an extra KBI? Try again!
+ continue
+ try:
+ seq, request = rpc.request_queue.get(block=True, timeout=0.05)
+ except Queue.Empty:
+ continue
+ method, args, kwargs = request
+ ret = method(*args, **kwargs)
+ rpc.response_queue.put((seq, ret))
+ except KeyboardInterrupt:
+ if quitting:
+ exit_now = True
+ continue
+ except SystemExit:
+ capture_warnings(False)
+ raise
+ except:
+ type, value, tb = sys.exc_info()
+ try:
+ print_exception()
+ rpc.response_queue.put((seq, None))
+ except:
+ # Link didn't work, print same exception to __stderr__
+ traceback.print_exception(type, value, tb, file=sys.__stderr__)
+ exit()
+ else:
+ continue
+
+def manage_socket(address):
+ for i in range(3):
+ time.sleep(i)
+ try:
+ server = MyRPCServer(address, MyHandler)
+ break
+ except socket.error as err:
+ print>>sys.__stderr__,"IDLE Subprocess: socket error: "\
+ + err.args[1] + ", retrying...."
+ else:
+ print>>sys.__stderr__, "IDLE Subprocess: Connection to "\
+ "IDLE GUI failed, exiting."
+ show_socket_error(err, address)
+ global exit_now
+ exit_now = True
+ return
+ server.handle_request() # A single request only
+
+def show_socket_error(err, address):
+ import Tkinter
+ import tkMessageBox
+ root = Tkinter.Tk()
+ root.withdraw()
+ if err.args[0] == 61: # connection refused
+ msg = "IDLE's subprocess can't connect to %s:%d. This may be due "\
+ "to your personal firewall configuration. It is safe to "\
+ "allow this internal connection because no data is visible on "\
+ "external ports." % address
+ tkMessageBox.showerror("IDLE Subprocess Error", msg, parent=root)
+ else:
+ tkMessageBox.showerror("IDLE Subprocess Error",
+ "Socket Error: %s" % err.args[1], parent=root)
+ root.destroy()
+
+def print_exception():
+ import linecache
+ linecache.checkcache()
+ flush_stdout()
+ efile = sys.stderr
+ 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",
+ "RemoteDebugger.py", "bdb.py")
+ cleanup_traceback(tbe, exclude)
+ traceback.print_list(tbe, file=efile)
+ lines = traceback.format_exception_only(typ, val)
+ for line in lines:
+ print>>efile, line,
+
+def cleanup_traceback(tb, exclude):
+ "Remove excluded traces from beginning/end of tb; get cached lines"
+ orig_tb = tb[:]
+ while tb:
+ for rpcfile in exclude:
+ if tb[0][0].count(rpcfile):
+ break # found an exclude, break for: and delete tb[0]
+ else:
+ break # no excludes, have left RPC code, break while:
+ del tb[0]
+ while tb:
+ for rpcfile in exclude:
+ if tb[-1][0].count(rpcfile):
+ break
+ else:
+ break
+ del tb[-1]
+ if len(tb) == 0:
+ # exception was in IDLE internals, don't prune!
+ tb[:] = orig_tb[:]
+ print>>sys.stderr, "** IDLE Internal Exception: "
+ rpchandler = rpc.objecttable['exec'].rpchandler
+ for i in range(len(tb)):
+ fn, ln, nm, line = tb[i]
+ if nm == '?':
+ nm = "-toplevel-"
+ if fn.startswith("<pyshell#") and IOBinding.encoding != 'utf-8':
+ ln -= 1 # correction for coding cookie
+ if not line and fn.startswith("<pyshell#"):
+ line = rpchandler.remotecall('linecache', 'getline',
+ (fn, ln), {})
+ 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
+
+def exit():
+ """Exit subprocess, possibly after first deleting sys.exitfunc
+
+ If config-main.cfg/.def 'General' 'delete-exitfunc' is True, then any
+ sys.exitfunc will be removed before exiting. (VPython support)
+
+ """
+ if no_exitfunc:
+ try:
+ del sys.exitfunc
+ except AttributeError:
+ pass
+ capture_warnings(False)
+ sys.exit(0)
+
+class MyRPCServer(rpc.RPCServer):
+
+ def handle_error(self, request, client_address):
+ """Override RPCServer method for IDLE
+
+ Interrupt the MainThread and exit server if link is dropped.
+
+ """
+ global quitting
+ try:
+ raise
+ except SystemExit:
+ raise
+ except EOFError:
+ global exit_now
+ exit_now = True
+ 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)
+ traceback.print_exc(file=erf)
+ print>>erf, '\n*** Unrecoverable, server exiting!'
+ print>>erf, '-'*40
+ quitting = True
+ thread.interrupt_main()
+
+class MyHandler(rpc.RPCHandler):
+
+ def handle(self):
+ """Override base method"""
+ executive = Executive(self)
+ self.register("exec", executive)
+ self.console = self.get_remote_proxy("console")
+ sys.stdin = PyShell.PseudoInputFile(self.console, "stdin",
+ IOBinding.encoding)
+ sys.stdout = PyShell.PseudoOutputFile(self.console, "stdout",
+ IOBinding.encoding)
+ sys.stderr = PyShell.PseudoOutputFile(self.console, "stderr",
+ IOBinding.encoding)
+
+ # Keep a reference to stdin so that it won't try to exit IDLE if
+ # sys.stdin gets changed from within IDLE's shell. See issue17838.
+ self._keep_stdin = sys.stdin
+
+ self.interp = self.get_remote_proxy("interp")
+ rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
+
+ def exithook(self):
+ "override SocketIO method - wait for MainThread to shut us down"
+ time.sleep(10)
+
+ def EOFhook(self):
+ "Override SocketIO method - terminate wait on callback and exit thread"
+ global quitting
+ quitting = True
+ thread.interrupt_main()
+
+ def decode_interrupthook(self):
+ "interrupt awakened thread"
+ global quitting
+ quitting = True
+ thread.interrupt_main()
+
+
+class Executive(object):
+
+ def __init__(self, rpchandler):
+ self.rpchandler = rpchandler
+ self.locals = __main__.__dict__
+ self.calltip = CallTips.CallTips()
+ self.autocomplete = AutoComplete.AutoComplete()
+
+ def runcode(self, code):
+ global interruptable
+ try:
+ self.usr_exc_info = None
+ interruptable = True
+ try:
+ exec code in self.locals
+ finally:
+ interruptable = False
+ except SystemExit:
+ # Scripts that raise SystemExit should just
+ # return to the interactive prompt
+ pass
+ except:
+ self.usr_exc_info = sys.exc_info()
+ if quitting:
+ exit()
+ print_exception()
+ jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
+ if jit:
+ self.rpchandler.interp.open_remote_stack_viewer()
+ else:
+ flush_stdout()
+
+ def interrupt_the_server(self):
+ if interruptable:
+ thread.interrupt_main()
+
+ def start_the_debugger(self, gui_adap_oid):
+ return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
+
+ def stop_the_debugger(self, idb_adap_oid):
+ "Unregister the Idb Adapter. Link objects and Idb then subject to GC"
+ self.rpchandler.unregister(idb_adap_oid)
+
+ def get_the_calltip(self, name):
+ return self.calltip.fetch_tip(name)
+
+ def get_the_completion_list(self, what, mode):
+ return self.autocomplete.fetch_completions(what, mode)
+
+ def stackviewer(self, flist_oid=None):
+ if self.usr_exc_info:
+ typ, val, tb = self.usr_exc_info
+ else:
+ return None
+ flist = None
+ if flist_oid is not None:
+ flist = self.rpchandler.get_remote_proxy(flist_oid)
+ while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
+ tb = tb.tb_next
+ sys.last_type = typ
+ sys.last_value = val
+ item = StackViewer.StackTreeItem(flist, tb)
+ return RemoteObjectBrowser.remote_object_tree_item(item)
+
+capture_warnings(False) # Make sure turned off; see issue 18081
diff --git a/lib/python2.7/idlelib/tabbedpages.py b/lib/python2.7/idlelib/tabbedpages.py
new file mode 100644
index 0000000..0723d94
--- /dev/null
+++ b/lib/python2.7/idlelib/tabbedpages.py
@@ -0,0 +1,498 @@
+"""An implementation of tabbed pages using only standard Tkinter.
+
+Originally developed for use in IDLE. Based on tabpage.py.
+
+Classes exported:
+TabbedPageSet -- A Tkinter implementation of a tabbed-page widget.
+TabSet -- A widget containing tabs (buttons) in one or more rows.
+
+"""
+from Tkinter import *
+
+class InvalidNameError(Exception): pass
+class AlreadyExistsError(Exception): pass
+
+
+class TabSet(Frame):
+ """A widget containing tabs (buttons) in one or more rows.
+
+ Only one tab may be selected at a time.
+
+ """
+ def __init__(self, page_set, select_command,
+ tabs=None, n_rows=1, max_tabs_per_row=5,
+ expand_tabs=False, **kw):
+ """Constructor arguments:
+
+ select_command -- A callable which will be called when a tab is
+ selected. It is called with the name of the selected tab as an
+ argument.
+
+ tabs -- A list of strings, the names of the tabs. Should be specified in
+ the desired tab order. The first tab will be the default and first
+ active tab. If tabs is None or empty, the TabSet will be initialized
+ empty.
+
+ n_rows -- Number of rows of tabs to be shown. If n_rows <= 0 or is
+ None, then the number of rows will be decided by TabSet. See
+ _arrange_tabs() for details.
+
+ max_tabs_per_row -- Used for deciding how many rows of tabs are needed,
+ when the number of rows is not constant. See _arrange_tabs() for
+ details.
+
+ """
+ Frame.__init__(self, page_set, **kw)
+ self.select_command = select_command
+ self.n_rows = n_rows
+ self.max_tabs_per_row = max_tabs_per_row
+ self.expand_tabs = expand_tabs
+ self.page_set = page_set
+
+ self._tabs = {}
+ self._tab2row = {}
+ if tabs:
+ self._tab_names = list(tabs)
+ else:
+ self._tab_names = []
+ self._selected_tab = None
+ self._tab_rows = []
+
+ self.padding_frame = Frame(self, height=2,
+ borderwidth=0, relief=FLAT,
+ background=self.cget('background'))
+ self.padding_frame.pack(side=TOP, fill=X, expand=False)
+
+ self._arrange_tabs()
+
+ def add_tab(self, tab_name):
+ """Add a new tab with the name given in tab_name."""
+ if not tab_name:
+ raise InvalidNameError("Invalid Tab name: '%s'" % tab_name)
+ if tab_name in self._tab_names:
+ raise AlreadyExistsError("Tab named '%s' already exists" %tab_name)
+
+ self._tab_names.append(tab_name)
+ self._arrange_tabs()
+
+ def remove_tab(self, tab_name):
+ """Remove the tab named <tab_name>"""
+ if not tab_name in self._tab_names:
+ raise KeyError("No such Tab: '%s" % page_name)
+
+ self._tab_names.remove(tab_name)
+ self._arrange_tabs()
+
+ def set_selected_tab(self, tab_name):
+ """Show the tab named <tab_name> as the selected one"""
+ if tab_name == self._selected_tab:
+ return
+ if tab_name is not None and tab_name not in self._tabs:
+ raise KeyError("No such Tab: '%s" % page_name)
+
+ # deselect the current selected tab
+ if self._selected_tab is not None:
+ self._tabs[self._selected_tab].set_normal()
+ self._selected_tab = None
+
+ if tab_name is not None:
+ # activate the tab named tab_name
+ self._selected_tab = tab_name
+ tab = self._tabs[tab_name]
+ tab.set_selected()
+ # move the tab row with the selected tab to the bottom
+ tab_row = self._tab2row[tab]
+ tab_row.pack_forget()
+ tab_row.pack(side=TOP, fill=X, expand=0)
+
+ def _add_tab_row(self, tab_names, expand_tabs):
+ if not tab_names:
+ return
+
+ tab_row = Frame(self)
+ tab_row.pack(side=TOP, fill=X, expand=0)
+ self._tab_rows.append(tab_row)
+
+ for tab_name in tab_names:
+ tab = TabSet.TabButton(tab_name, self.select_command,
+ tab_row, self)
+ if expand_tabs:
+ tab.pack(side=LEFT, fill=X, expand=True)
+ else:
+ tab.pack(side=LEFT)
+ self._tabs[tab_name] = tab
+ self._tab2row[tab] = tab_row
+
+ # tab is the last one created in the above loop
+ tab.is_last_in_row = True
+
+ def _reset_tab_rows(self):
+ while self._tab_rows:
+ tab_row = self._tab_rows.pop()
+ tab_row.destroy()
+ self._tab2row = {}
+
+ def _arrange_tabs(self):
+ """
+ Arrange the tabs in rows, in the order in which they were added.
+
+ If n_rows >= 1, this will be the number of rows used. Otherwise the
+ number of rows will be calculated according to the number of tabs and
+ max_tabs_per_row. In this case, the number of rows may change when
+ adding/removing tabs.
+
+ """
+ # remove all tabs and rows
+ for tab_name in self._tabs.keys():
+ self._tabs.pop(tab_name).destroy()
+ self._reset_tab_rows()
+
+ if not self._tab_names:
+ return
+
+ if self.n_rows is not None and self.n_rows > 0:
+ n_rows = self.n_rows
+ else:
+ # calculate the required number of rows
+ n_rows = (len(self._tab_names) - 1) // self.max_tabs_per_row + 1
+
+ # 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):
+ # 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]
+ i += n_tabs
+ self._add_tab_row(tab_names, expand_tabs)
+
+ # re-select selected tab so it is properly displayed
+ selected = self._selected_tab
+ self.set_selected_tab(None)
+ if selected in self._tab_names:
+ self.set_selected_tab(selected)
+
+ class TabButton(Frame):
+ """A simple tab-like widget."""
+
+ bw = 2 # borderwidth
+
+ def __init__(self, name, select_command, tab_row, tab_set):
+ """Constructor arguments:
+
+ name -- The tab's name, which will appear in its button.
+
+ select_command -- The command to be called upon selection of the
+ tab. It is called with the tab's name as an argument.
+
+ """
+ Frame.__init__(self, tab_row, borderwidth=self.bw, relief=RAISED)
+
+ self.name = name
+ self.select_command = select_command
+ self.tab_set = tab_set
+ self.is_last_in_row = False
+
+ self.button = Radiobutton(
+ self, text=name, command=self._select_event,
+ padx=5, pady=1, takefocus=FALSE, indicatoron=FALSE,
+ highlightthickness=0, selectcolor='', borderwidth=0)
+ self.button.pack(side=LEFT, fill=X, expand=True)
+
+ self._init_masks()
+ self.set_normal()
+
+ def _select_event(self, *args):
+ """Event handler for tab selection.
+
+ With TabbedPageSet, this calls TabbedPageSet.change_page, so that
+ selecting a tab changes the page.
+
+ Note that this does -not- call set_selected -- it will be called by
+ TabSet.set_selected_tab, which should be called when whatever the
+ tabs are related to changes.
+
+ """
+ self.select_command(self.name)
+ return
+
+ def set_selected(self):
+ """Assume selected look"""
+ self._place_masks(selected=True)
+
+ def set_normal(self):
+ """Assume normal look"""
+ self._place_masks(selected=False)
+
+ def _init_masks(self):
+ page_set = self.tab_set.page_set
+ background = page_set.pages_frame.cget('background')
+ # mask replaces the middle of the border with the background color
+ self.mask = Frame(page_set, borderwidth=0, relief=FLAT,
+ background=background)
+ # mskl replaces the bottom-left corner of the border with a normal
+ # left border
+ self.mskl = Frame(page_set, borderwidth=0, relief=FLAT,
+ background=background)
+ self.mskl.ml = Frame(self.mskl, borderwidth=self.bw,
+ relief=RAISED)
+ self.mskl.ml.place(x=0, y=-self.bw,
+ width=2*self.bw, height=self.bw*4)
+ # mskr replaces the bottom-right corner of the border with a normal
+ # right border
+ self.mskr = Frame(page_set, borderwidth=0, relief=FLAT,
+ background=background)
+ self.mskr.mr = Frame(self.mskr, borderwidth=self.bw,
+ relief=RAISED)
+
+ def _place_masks(self, selected=False):
+ height = self.bw
+ if selected:
+ height += self.bw
+
+ self.mask.place(in_=self,
+ relx=0.0, x=0,
+ rely=1.0, y=0,
+ relwidth=1.0, width=0,
+ relheight=0.0, height=height)
+
+ self.mskl.place(in_=self,
+ relx=0.0, x=-self.bw,
+ rely=1.0, y=0,
+ relwidth=0.0, width=self.bw,
+ relheight=0.0, height=height)
+
+ page_set = self.tab_set.page_set
+ if selected and ((not self.is_last_in_row) or
+ (self.winfo_rootx() + self.winfo_width() <
+ page_set.winfo_rootx() + page_set.winfo_width())
+ ):
+ # for a selected tab, if its rightmost edge isn't on the
+ # rightmost edge of the page set, the right mask should be one
+ # borderwidth shorter (vertically)
+ height -= self.bw
+
+ self.mskr.place(in_=self,
+ relx=1.0, x=0,
+ rely=1.0, y=0,
+ relwidth=0.0, width=self.bw,
+ relheight=0.0, height=height)
+
+ self.mskr.mr.place(x=-self.bw, y=-self.bw,
+ width=2*self.bw, height=height + self.bw*2)
+
+ # finally, lower the tab set so that all of the frames we just
+ # placed hide it
+ self.tab_set.lower()
+
+class TabbedPageSet(Frame):
+ """A Tkinter tabbed-pane widget.
+
+ Constains set of 'pages' (or 'panes') with tabs above for selecting which
+ page is displayed. Only one page will be displayed at a time.
+
+ Pages may be accessed through the 'pages' attribute, which is a dictionary
+ of pages, using the name given as the key. A page is an instance of a
+ subclass of Tk's Frame widget.
+
+ The page widgets will be created (and destroyed when required) by the
+ TabbedPageSet. Do not call the page's pack/place/grid/destroy methods.
+
+ Pages may be added or removed at any time using the add_page() and
+ remove_page() methods.
+
+ """
+ class Page(object):
+ """Abstract base class for TabbedPageSet's pages.
+
+ Subclasses must override the _show() and _hide() methods.
+
+ """
+ uses_grid = False
+
+ def __init__(self, page_set):
+ self.frame = Frame(page_set, borderwidth=2, relief=RAISED)
+
+ def _show(self):
+ raise NotImplementedError
+
+ def _hide(self):
+ raise NotImplementedError
+
+ class PageRemove(Page):
+ """Page class using the grid placement manager's "remove" mechanism."""
+ uses_grid = True
+
+ def _show(self):
+ self.frame.grid(row=0, column=0, sticky=NSEW)
+
+ def _hide(self):
+ self.frame.grid_remove()
+
+ class PageLift(Page):
+ """Page class using the grid placement manager's "lift" mechanism."""
+ uses_grid = True
+
+ def __init__(self, page_set):
+ super(TabbedPageSet.PageLift, self).__init__(page_set)
+ self.frame.grid(row=0, column=0, sticky=NSEW)
+ self.frame.lower()
+
+ def _show(self):
+ self.frame.lift()
+
+ def _hide(self):
+ self.frame.lower()
+
+ class PagePackForget(Page):
+ """Page class using the pack placement manager's "forget" mechanism."""
+ def _show(self):
+ self.frame.pack(fill=BOTH, expand=True)
+
+ def _hide(self):
+ self.frame.pack_forget()
+
+ def __init__(self, parent, page_names=None, page_class=PageLift,
+ n_rows=1, max_tabs_per_row=5, expand_tabs=False,
+ **kw):
+ """Constructor arguments:
+
+ page_names -- A list of strings, each will be the dictionary key to a
+ page's widget, and the name displayed on the page's tab. Should be
+ specified in the desired page order. The first page will be the default
+ and first active page. If page_names is None or empty, the
+ TabbedPageSet will be initialized empty.
+
+ n_rows, max_tabs_per_row -- Parameters for the TabSet which will
+ manage the tabs. See TabSet's docs for details.
+
+ page_class -- Pages can be shown/hidden using three mechanisms:
+
+ * PageLift - All pages will be rendered one on top of the other. When
+ a page is selected, it will be brought to the top, thus hiding all
+ other pages. Using this method, the TabbedPageSet will not be resized
+ when pages are switched. (It may still be resized when pages are
+ added/removed.)
+
+ * PageRemove - When a page is selected, the currently showing page is
+ hidden, and the new page shown in its place. Using this method, the
+ TabbedPageSet may resize when pages are changed.
+
+ * PagePackForget - This mechanism uses the pack placement manager.
+ When a page is shown it is packed, and when it is hidden it is
+ unpacked (i.e. pack_forget). This mechanism may also cause the
+ TabbedPageSet to resize when the page is changed.
+
+ """
+ Frame.__init__(self, parent, **kw)
+
+ self.page_class = page_class
+ self.pages = {}
+ self._pages_order = []
+ self._current_page = None
+ self._default_page = None
+
+ self.columnconfigure(0, weight=1)
+ self.rowconfigure(1, weight=1)
+
+ self.pages_frame = Frame(self)
+ self.pages_frame.grid(row=1, column=0, sticky=NSEW)
+ if self.page_class.uses_grid:
+ self.pages_frame.columnconfigure(0, weight=1)
+ self.pages_frame.rowconfigure(0, weight=1)
+
+ # the order of the following commands is important
+ self._tab_set = TabSet(self, self.change_page, n_rows=n_rows,
+ max_tabs_per_row=max_tabs_per_row,
+ expand_tabs=expand_tabs)
+ if page_names:
+ for name in page_names:
+ self.add_page(name)
+ self._tab_set.grid(row=0, column=0, sticky=NSEW)
+
+ self.change_page(self._default_page)
+
+ def add_page(self, page_name):
+ """Add a new page with the name given in page_name."""
+ if not page_name:
+ raise InvalidNameError("Invalid TabPage name: '%s'" % page_name)
+ if page_name in self.pages:
+ raise AlreadyExistsError(
+ "TabPage named '%s' already exists" % page_name)
+
+ self.pages[page_name] = self.page_class(self.pages_frame)
+ self._pages_order.append(page_name)
+ self._tab_set.add_tab(page_name)
+
+ if len(self.pages) == 1: # adding first page
+ self._default_page = page_name
+ self.change_page(page_name)
+
+ def remove_page(self, page_name):
+ """Destroy the page whose name is given in page_name."""
+ if not page_name in self.pages:
+ raise KeyError("No such TabPage: '%s" % page_name)
+
+ self._pages_order.remove(page_name)
+
+ # handle removing last remaining, default, or currently shown page
+ if len(self._pages_order) > 0:
+ if page_name == self._default_page:
+ # set a new default page
+ self._default_page = self._pages_order[0]
+ else:
+ self._default_page = None
+
+ if page_name == self._current_page:
+ self.change_page(self._default_page)
+
+ self._tab_set.remove_tab(page_name)
+ page = self.pages.pop(page_name)
+ page.frame.destroy()
+
+ def change_page(self, page_name):
+ """Show the page whose name is given in page_name."""
+ if self._current_page == page_name:
+ return
+ if page_name is not None and page_name not in self.pages:
+ raise KeyError("No such TabPage: '%s'" % page_name)
+
+ if self._current_page is not None:
+ self.pages[self._current_page]._hide()
+ self._current_page = None
+
+ if page_name is not None:
+ self._current_page = page_name
+ self.pages[page_name]._show()
+
+ self._tab_set.set_selected_tab(page_name)
+
+def _tabbed_pages(parent):
+ # test dialog
+ root=Tk()
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 175))
+ root.title("Test tabbed pages")
+ tabPage=TabbedPageSet(root, page_names=['Foobar','Baz'], n_rows=0,
+ expand_tabs=False,
+ )
+ tabPage.pack(side=TOP, expand=TRUE, fill=BOTH)
+ Label(tabPage.pages['Foobar'].frame, text='Foo', pady=20).pack()
+ Label(tabPage.pages['Foobar'].frame, text='Bar', pady=20).pack()
+ Label(tabPage.pages['Baz'].frame, text='Baz').pack()
+ entryPgName=Entry(root)
+ buttonAdd=Button(root, text='Add Page',
+ command=lambda:tabPage.add_page(entryPgName.get()))
+ buttonRemove=Button(root, text='Remove Page',
+ command=lambda:tabPage.remove_page(entryPgName.get()))
+ labelPgName=Label(root, text='name of page to add/remove:')
+ buttonAdd.pack(padx=5, pady=5)
+ buttonRemove.pack(padx=5, pady=5)
+ labelPgName.pack(padx=5)
+ entryPgName.pack(padx=5)
+ root.mainloop()
+
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_tabbed_pages)
diff --git a/lib/python2.7/idlelib/textView.py b/lib/python2.7/idlelib/textView.py
new file mode 100644
index 0000000..b8c4ac1
--- /dev/null
+++ b/lib/python2.7/idlelib/textView.py
@@ -0,0 +1,94 @@
+"""Simple text browser for IDLE
+
+"""
+
+from Tkinter import *
+import tkMessageBox
+
+class TextViewer(Toplevel):
+ """A simple text viewer dialog for IDLE
+
+ """
+ def __init__(self, parent, title, text, modal=True, _htest=False):
+ """Show the given text in a scrollable window with a 'close' button
+
+ If modal option set to False, user can interact with other windows,
+ otherwise they will be unable to interact with other windows until
+ the textview window is closed.
+
+ _htest - bool; change box location when running htest.
+ """
+ Toplevel.__init__(self, parent)
+ self.configure(borderwidth=5)
+ # place dialog below parent if running htest
+ self.geometry("=%dx%d+%d+%d" % (750, 500,
+ parent.winfo_rootx() + 10,
+ parent.winfo_rooty() + (10 if not _htest else 100)))
+ #elguavas - config placeholders til config stuff completed
+ self.bg = '#ffffff'
+ self.fg = '#000000'
+
+ self.CreateWidgets()
+ self.title(title)
+ self.protocol("WM_DELETE_WINDOW", self.Ok)
+ self.parent = parent
+ self.textView.focus_set()
+ #key bindings for this dialog
+ self.bind('<Return>',self.Ok) #dismiss dialog
+ self.bind('<Escape>',self.Ok) #dismiss dialog
+ self.textView.insert(0.0, text)
+ self.textView.config(state=DISABLED)
+
+ if modal:
+ self.transient(parent)
+ self.grab_set()
+ self.wait_window()
+
+ def CreateWidgets(self):
+ frameText = Frame(self, relief=SUNKEN, height=700)
+ frameButtons = Frame(self)
+ self.buttonOk = Button(frameButtons, text='Close',
+ command=self.Ok, takefocus=FALSE)
+ self.scrollbarView = Scrollbar(frameText, orient=VERTICAL,
+ takefocus=FALSE, highlightthickness=0)
+ self.textView = Text(frameText, wrap=WORD, highlightthickness=0,
+ fg=self.fg, bg=self.bg)
+ self.scrollbarView.config(command=self.textView.yview)
+ self.textView.config(yscrollcommand=self.scrollbarView.set)
+ self.buttonOk.pack()
+ self.scrollbarView.pack(side=RIGHT,fill=Y)
+ self.textView.pack(side=LEFT,expand=TRUE,fill=BOTH)
+ frameButtons.pack(side=BOTTOM,fill=X)
+ frameText.pack(side=TOP,expand=TRUE,fill=BOTH)
+
+ def Ok(self, event=None):
+ self.destroy()
+
+
+def view_text(parent, title, text, modal=True):
+ return TextViewer(parent, title, text, modal)
+
+def view_file(parent, title, filename, encoding=None, modal=True):
+ try:
+ if encoding:
+ import codecs
+ textFile = codecs.open(filename, 'r')
+ else:
+ textFile = open(filename, 'r')
+ except IOError:
+ tkMessageBox.showerror(title='File Load Error',
+ message='Unable to load file %r .' % filename,
+ parent=parent)
+ except UnicodeDecodeError as err:
+ showerror(title='Unicode Decode Error',
+ message=str(err),
+ parent=parent)
+ else:
+ return view_text(parent, title, textFile.read(), modal)
+
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main('idlelib.idle_test.test_textview', verbosity=2, exit=False)
+ from idlelib.idle_test.htest import run
+ run(TextViewer)