summaryrefslogtreecommitdiff
path: root/Lib/idlelib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/idlelib')
-rw-r--r--Lib/idlelib/AutoComplete.py5
-rw-r--r--Lib/idlelib/AutoExpand.py21
-rw-r--r--Lib/idlelib/Bindings.py32
-rw-r--r--Lib/idlelib/CallTipWindow.py61
-rw-r--r--Lib/idlelib/ClassBrowser.py20
-rw-r--r--Lib/idlelib/ColorDelegator.py14
-rw-r--r--Lib/idlelib/Debugger.py2
-rw-r--r--Lib/idlelib/EditorWindow.py44
-rw-r--r--Lib/idlelib/FileList.py2
-rw-r--r--Lib/idlelib/FormatParagraph.py14
-rw-r--r--Lib/idlelib/GrepDialog.py69
-rw-r--r--Lib/idlelib/HyperParser.py174
-rw-r--r--Lib/idlelib/IOBinding.py18
-rw-r--r--Lib/idlelib/IdleHistory.py2
-rw-r--r--Lib/idlelib/MultiCall.py48
-rw-r--r--Lib/idlelib/MultiStatusBar.py35
-rw-r--r--Lib/idlelib/NEWS.txt68
-rw-r--r--Lib/idlelib/ObjectBrowser.py11
-rw-r--r--Lib/idlelib/PathBrowser.py22
-rw-r--r--Lib/idlelib/Percolator.py50
-rwxr-xr-xLib/idlelib/PyShell.py16
-rw-r--r--Lib/idlelib/ReplaceDialog.py31
-rw-r--r--Lib/idlelib/ScriptBinding.py4
-rw-r--r--Lib/idlelib/ScrolledList.py19
-rw-r--r--Lib/idlelib/SearchDialog.py22
-rw-r--r--Lib/idlelib/SearchEngine.py1
-rw-r--r--Lib/idlelib/StackViewer.py30
-rw-r--r--Lib/idlelib/ToolTip.py22
-rw-r--r--Lib/idlelib/TreeWidget.py35
-rw-r--r--Lib/idlelib/UndoDelegator.py21
-rw-r--r--Lib/idlelib/WidgetRedirector.py15
-rw-r--r--Lib/idlelib/ZoomHeight.py2
-rw-r--r--Lib/idlelib/aboutDialog.py20
-rw-r--r--Lib/idlelib/config-keys.def28
-rw-r--r--Lib/idlelib/config-main.def2
-rw-r--r--Lib/idlelib/configDialog.py19
-rw-r--r--Lib/idlelib/configHandler.py16
-rw-r--r--Lib/idlelib/configHelpSourceEdit.py27
-rw-r--r--Lib/idlelib/configSectionNameDialog.py28
-rw-r--r--Lib/idlelib/dynOptionMenuWidget.py26
-rw-r--r--Lib/idlelib/help.txt556
-rw-r--r--Lib/idlelib/idle_test/README.txt37
-rw-r--r--Lib/idlelib/idle_test/htest.py369
-rw-r--r--Lib/idlelib/idle_test/mock_idle.py25
-rw-r--r--Lib/idlelib/idle_test/mock_tk.py27
-rw-r--r--Lib/idlelib/idle_test/test_autocomplete.py143
-rw-r--r--Lib/idlelib/idle_test/test_autoexpand.py141
-rw-r--r--Lib/idlelib/idle_test/test_calltips.py7
-rw-r--r--Lib/idlelib/idle_test/test_formatparagraph.py10
-rw-r--r--Lib/idlelib/idle_test/test_hyperparser.py191
-rw-r--r--Lib/idlelib/idle_test/test_textview.py97
-rw-r--r--Lib/idlelib/idlever.py2
-rw-r--r--Lib/idlelib/keybindingDialog.py31
-rw-r--r--Lib/idlelib/macosxSupport.py136
-rw-r--r--Lib/idlelib/rpc.py8
-rw-r--r--Lib/idlelib/run.py4
-rw-r--r--Lib/idlelib/tabbedpages.py10
-rw-r--r--Lib/idlelib/textView.py39
58 files changed, 2109 insertions, 820 deletions
diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py
index f3660307d3..b20512dfa0 100644
--- a/Lib/idlelib/AutoComplete.py
+++ b/Lib/idlelib/AutoComplete.py
@@ -226,3 +226,8 @@ class AutoComplete:
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/idlelib/AutoExpand.py b/Lib/idlelib/AutoExpand.py
index 9e93d57d65..7059054281 100644
--- a/Lib/idlelib/AutoExpand.py
+++ b/Lib/idlelib/AutoExpand.py
@@ -1,3 +1,17 @@
+'''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
@@ -20,6 +34,7 @@ class AutoExpand:
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:
@@ -46,6 +61,7 @@ class AutoExpand:
return "break"
def getwords(self):
+ "Return a list of words that match the prefix before the cursor."
word = self.getprevword()
if not word:
return []
@@ -76,8 +92,13 @@ class AutoExpand:
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/idlelib/Bindings.py b/Lib/idlelib/Bindings.py
index 65c0317e60..df2b251426 100644
--- a/Lib/idlelib/Bindings.py
+++ b/Lib/idlelib/Bindings.py
@@ -8,9 +8,14 @@ the PythonShell window, and a Format menu which is only present in the Editor
windows.
"""
-import sys
from idlelib.configHandler import idleConf
-from idlelib import macosxSupport
+
+# 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
@@ -81,27 +86,4 @@ menudefs = [
]),
]
-if macosxSupport.runningAsOSXApp():
- # Running as a proper MacOS application bundle. This block restructures
- # the menus a little to make them conform better to the HIG.
-
- quitItem = menudefs[0][1][-1]
- closeItem = 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 menudefs[0][1][-3:]
- menudefs[0][1].insert(6, closeItem)
-
- # Remove the 'About' entry from the help menu, it is in the application
- # menu
- del menudefs[-1][1][0:2]
-
- # Remove the 'Configure' entry from the options menu, it is in the
- # application menu as 'Preferences'
- del menudefs[-2][1][0:2]
-
default_keydefs = idleConf.GetCurrentKeySet()
-
-del sys
diff --git a/Lib/idlelib/CallTipWindow.py b/Lib/idlelib/CallTipWindow.py
index 8e29dabaea..aacfa00851 100644
--- a/Lib/idlelib/CallTipWindow.py
+++ b/Lib/idlelib/CallTipWindow.py
@@ -133,37 +133,36 @@ class CallTip:
return bool(self.tipwindow)
+def _calltip_window(parent):
+ root = Tk()
+ root.title("Test calltips")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
-###############################
-#
-# Test Code
-#
-class container: # Conceptually an editor_window
- def __init__(self):
- root = Tk()
- text = self.text = Text(root)
- text.pack(side=LEFT, fill=BOTH, expand=1)
- text.insert("insert", "string.split")
- root.update()
- self.calltip = CallTip(text)
-
- text.event_add("<<calltip-show>>", "(")
- text.event_add("<<calltip-hide>>", ")")
- text.bind("<<calltip-show>>", self.calltip_show)
- text.bind("<<calltip-hide>>", self.calltip_hide)
-
- text.focus_set()
- root.mainloop()
-
- def calltip_show(self, event):
- self.calltip.showtip("Hello world")
-
- def calltip_hide(self, event):
- self.calltip.hidetip()
-
-def main():
- # Test code
- c=container()
+ class MyEditWin: # comparenceptually an editor_window
+ def __init__(self):
+ text = self.text = Text(root)
+ text.pack(side=LEFT, fill=BOTH, expand=1)
+ text.insert("insert", "string.split")
+ root.update()
+ self.calltip = CallTip(text)
+
+ text.event_add("<<calltip-show>>", "(")
+ text.event_add("<<calltip-hide>>", ")")
+ text.bind("<<calltip-show>>", self.calltip_show)
+ text.bind("<<calltip-hide>>", self.calltip_hide)
+
+ text.focus_set()
+ root.mainloop()
+
+ def calltip_show(self, event):
+ self.calltip.showtip("Hello world", "insert", "end")
+
+ def calltip_hide(self, event):
+ self.calltip.hidetip()
+
+ editwin = MyEditWin()
if __name__=='__main__':
- main()
+ from idlelib.idle_test.htest import run
+ run(_calltip_window)
diff --git a/Lib/idlelib/ClassBrowser.py b/Lib/idlelib/ClassBrowser.py
index 71176cd701..ee1a70f9dc 100644
--- a/Lib/idlelib/ClassBrowser.py
+++ b/Lib/idlelib/ClassBrowser.py
@@ -21,11 +21,15 @@ from idlelib.configHandler import idleConf
class ClassBrowser:
- def __init__(self, flist, name, path):
+ 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.
+ """
self.name = name
self.file = os.path.join(path[0], self.name + ".py")
+ self._htest = _htest
self.init(flist)
def close(self, event=None):
@@ -40,6 +44,9 @@ class ClassBrowser:
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
@@ -202,7 +209,7 @@ class MethodBrowserTreeItem(TreeItem):
edit = PyShell.flist.open(self.file)
edit.gotoline(self.cl.methods[self.name])
-def main():
+def _class_browser(parent): #Wrapper for htest
try:
file = __file__
except NameError:
@@ -213,9 +220,10 @@ def main():
file = sys.argv[0]
dir, file = os.path.split(file)
name = os.path.splitext(file)[0]
- ClassBrowser(PyShell.flist, name, [dir])
- if sys.stdin is sys.__stdin__:
- mainloop()
+ flist = PyShell.PyShellFileList(parent)
+ ClassBrowser(flist, name, [dir], _htest=True)
+ parent.mainloop()
if __name__ == "__main__":
- main()
+ from idlelib.idle_test.htest import run
+ run(_class_browser)
diff --git a/Lib/idlelib/ColorDelegator.py b/Lib/idlelib/ColorDelegator.py
index 61e2be47c7..424755f028 100644
--- a/Lib/idlelib/ColorDelegator.py
+++ b/Lib/idlelib/ColorDelegator.py
@@ -253,17 +253,21 @@ class ColorDelegator(Delegator):
for tag in self.tagdefs:
self.tag_remove(tag, "1.0", "end")
-def main():
+def _color_delegator(parent):
from idlelib.Percolator import Percolator
root = Tk()
- root.wm_protocol("WM_DELETE_WINDOW", root.quit)
- text = Text(background="white")
+ root.title("Test ColorDelegator")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ source = "if somename: x = 'abc' # comment\nprint"
+ text = Text(root, background="white")
+ text.insert("insert", source)
text.pack(expand=1, fill="both")
- text.focus_set()
p = Percolator(text)
d = ColorDelegator()
p.insertfilter(d)
root.mainloop()
if __name__ == "__main__":
- main()
+ from idlelib.idle_test.htest import run
+ run(_color_delegator)
diff --git a/Lib/idlelib/Debugger.py b/Lib/idlelib/Debugger.py
index d4872ed42a..ca98b107c4 100644
--- a/Lib/idlelib/Debugger.py
+++ b/Lib/idlelib/Debugger.py
@@ -322,7 +322,7 @@ class Debugger:
class StackViewer(ScrolledList):
def __init__(self, master, flist, gui):
- if macosxSupport.runningAsOSXApp():
+ if macosxSupport.isAquaTk():
# At least on with the stock AquaTk version on OSX 10.4 you'll
# get an shaking GUI that eventually kills IDLE if the width
# argument is specified.
diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py
index 4bf1111482..f3df8eacc9 100644
--- a/Lib/idlelib/EditorWindow.py
+++ b/Lib/idlelib/EditorWindow.py
@@ -1,5 +1,6 @@
import importlib
import importlib.abc
+import importlib.util
import os
from platform import python_version
import re
@@ -78,6 +79,8 @@ class HelpDialog(object):
self.parent = None
helpDialog = HelpDialog() # singleton instance
+def _help_dialog(parent): # wrapper for htest
+ helpDialog.show_dialog(parent)
class EditorWindow(object):
@@ -108,8 +111,8 @@ class EditorWindow(object):
'Python%s.chm' % _sphinx_version())
if os.path.isfile(chmfile):
dochome = chmfile
- elif macosxSupport.runningAsOSXApp():
- # documentation is stored inside the python framework
+ elif sys.platform == 'darwin':
+ # documentation may be stored inside a python framework
dochome = os.path.join(sys.base_prefix,
'Resources/English.lproj/Documentation/index.html')
dochome = os.path.normpath(dochome)
@@ -165,7 +168,7 @@ class EditorWindow(object):
self.top.protocol("WM_DELETE_WINDOW", self.close)
self.top.bind("<<close-window>>", self.close_event)
- if macosxSupport.runningAsOSXApp():
+ 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,
@@ -408,7 +411,7 @@ class EditorWindow(object):
def set_status_bar(self):
self.status_bar = self.MultiStatusBar(self.top)
- if macosxSupport.runningAsOSXApp():
+ 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)
@@ -435,7 +438,7 @@ class EditorWindow(object):
("help", "_Help"),
]
- if macosxSupport.runningAsOSXApp():
+ if sys.platform == "darwin":
menu_specs[-2] = ("windows", "_Window")
@@ -446,7 +449,7 @@ class EditorWindow(object):
underline, label = prepstr(label)
menudict[name] = menu = Menu(mbar, name=name)
mbar.add_cascade(label=label, menu=menu, underline=underline)
- if macosxSupport.isCarbonAquaTk(self.root):
+ if macosxSupport.isCarbonTk():
# Insert the application menu
menudict['application'] = menu = Menu(mbar, name='apple')
mbar.add_cascade(label='IDLE', menu=menu)
@@ -549,7 +552,7 @@ class EditorWindow(object):
if sys.platform[:3] == 'win':
try:
os.startfile(self.help_url)
- except WindowsError as why:
+ except OSError as why:
tkMessageBox.showerror(title='Document Start Failure',
message=str(why), parent=self.text)
else:
@@ -660,20 +663,20 @@ class EditorWindow(object):
return
# XXX Ought to insert current file's directory in front of path
try:
- loader = importlib.find_loader(name)
+ spec = importlib.util.find_spec(name)
except (ValueError, ImportError) as msg:
tkMessageBox.showerror("Import error", str(msg), parent=self.text)
return
- if loader is None:
+ if spec is None:
tkMessageBox.showerror("Import error", "module not found",
parent=self.text)
return
- if not isinstance(loader, importlib.abc.SourceLoader):
+ if not isinstance(spec.loader, importlib.abc.SourceLoader):
tkMessageBox.showerror("Import error", "not a source-based module",
parent=self.text)
return
try:
- file_path = loader.get_filename(name)
+ file_path = spec.loader.get_filename(name)
except AttributeError:
tkMessageBox.showerror("Import error",
"loader does not support get_filename",
@@ -872,7 +875,7 @@ class EditorWindow(object):
if sys.platform[:3] == 'win':
try:
os.startfile(helpfile)
- except WindowsError as why:
+ except OSError as why:
tkMessageBox.showerror(title='Document Start Failure',
message=str(why), parent=self.text)
else:
@@ -1063,7 +1066,7 @@ class EditorWindow(object):
try:
try:
mod = importlib.import_module('.' + name, package=__package__)
- except ImportError:
+ except (ImportError, TypeError):
mod = importlib.import_module(name)
except ImportError:
print("\nFailed to import extension: ", name)
@@ -1672,7 +1675,7 @@ 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.runningAsOSXApp() and eventname in {
+ if (not keylist) or (macosxSupport.isCocoaTk() and eventname in {
"<<open-module>>",
"<<goto-line>>",
"<<change-indentwidth>>"}):
@@ -1699,19 +1702,18 @@ def fixwordbreaks(root):
tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
-def test():
- root = Tk()
+def _editor_window(parent):
+ root = parent
fixwordbreaks(root)
- root.withdraw()
if sys.argv[1:]:
filename = sys.argv[1]
else:
filename = None
+ macosxSupport.setupApp(root, None)
edit = EditorWindow(root=root, filename=filename)
- edit.set_close_hook(root.quit)
edit.text.bind("<<close-all-windows>>", edit.close_event)
- root.mainloop()
- root.destroy()
+ parent.mainloop()
if __name__ == '__main__':
- test()
+ from idlelib.idle_test.htest import run
+ run(_help_dialog, _editor_window)
diff --git a/Lib/idlelib/FileList.py b/Lib/idlelib/FileList.py
index 37a337ed9a..a9989a8624 100644
--- a/Lib/idlelib/FileList.py
+++ b/Lib/idlelib/FileList.py
@@ -103,7 +103,7 @@ class FileList:
if not os.path.isabs(filename):
try:
pwd = os.getcwd()
- except os.error:
+ except OSError:
pass
else:
filename = os.path.join(pwd, filename)
diff --git a/Lib/idlelib/FormatParagraph.py b/Lib/idlelib/FormatParagraph.py
index ae4e6e7b91..9b10c0a760 100644
--- a/Lib/idlelib/FormatParagraph.py
+++ b/Lib/idlelib/FormatParagraph.py
@@ -32,7 +32,7 @@ class FormatParagraph:
def close(self):
self.editwin = None
- def format_paragraph_event(self, event):
+ 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
@@ -41,9 +41,12 @@ class FormatParagraph:
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.
"""
- maxformatwidth = idleConf.GetOption(
- 'main', 'FormatParagraph', 'paragraph', type='int')
+ if limit == None:
+ limit = idleConf.GetOption(
+ 'main', 'FormatParagraph', 'paragraph', type='int')
text = self.editwin.text
first, last = self.editwin.get_selection_indices()
if first and last:
@@ -53,9 +56,9 @@ class FormatParagraph:
first, last, comment_header, data = \
find_paragraph(text, text.index("insert"))
if comment_header:
- newdata = reformat_comment(data, maxformatwidth, comment_header)
+ newdata = reformat_comment(data, limit, comment_header)
else:
- newdata = reformat_paragraph(data, maxformatwidth)
+ newdata = reformat_paragraph(data, limit)
text.tag_remove("sel", "1.0", "end")
if newdata != data:
@@ -185,7 +188,6 @@ def get_comment_header(line):
return m.group(1)
if __name__ == "__main__":
- from test import support; support.use_resources = ['gui']
import unittest
unittest.main('idlelib.idle_test.test_formatparagraph',
verbosity=2, exit=False)
diff --git a/Lib/idlelib/GrepDialog.py b/Lib/idlelib/GrepDialog.py
index c3590742eb..38b6480fc6 100644
--- a/Lib/idlelib/GrepDialog.py
+++ b/Lib/idlelib/GrepDialog.py
@@ -1,9 +1,14 @@
import os
import fnmatch
+import re # for htest
import sys
-from tkinter import *
+from tkinter import StringVar, BooleanVar, Checkbutton # for GrepDialog
+from tkinter import Tk, Text, Button, SEL, END # for htest
from idlelib import SearchEngine
+import itertools
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()
@@ -63,7 +68,7 @@ class GrepDialog(SearchDialogBase):
if not path:
self.top.bell()
return
- from idlelib.OutputWindow import OutputWindow
+ from idlelib.OutputWindow import OutputWindow # leave here!
save = sys.stdout
try:
sys.stdout = OutputWindow(self.flist)
@@ -79,21 +84,26 @@ class GrepDialog(SearchDialogBase):
pat = self.engine.getpat()
print("Searching %r in %s ..." % (pat, path))
hits = 0
- for fn in list:
- try:
- with open(fn, errors='replace') 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 OSError as msg:
- print(msg)
- print(("Hits found: %s\n"
- "(Hint: right-click to open locations.)"
- % hits) if hits else "No hits.")
+ try:
+ for fn in list:
+ try:
+ with open(fn, errors='replace') 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 OSError 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:
@@ -120,9 +130,30 @@ class GrepDialog(SearchDialogBase):
self.top.grab_release()
self.top.withdraw()
+
+def _grep_dialog(parent): # for 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__":
- # A human test is a bit tricky since EditorWindow() imports this module.
- # Hence Idle must be restarted after editing this file for a live test.
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/idlelib/HyperParser.py b/Lib/idlelib/HyperParser.py
index 4af4b085c7..d376568fc9 100644
--- a/Lib/idlelib/HyperParser.py
+++ b/Lib/idlelib/HyperParser.py
@@ -1,11 +1,8 @@
-"""
-HyperParser
-===========
-This module defines the HyperParser class, which provides advanced parsing
-abilities for the ParenMatch and other extensions.
-The HyperParser uses PyParser. PyParser is intended mostly to give information
-on the proper indentation of code. HyperParser gives some information on the
-structure of code, used by extensions to help the user.
+"""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
@@ -15,9 +12,7 @@ from idlelib import PyParse
class HyperParser:
def __init__(self, editwin, index):
- """Initialize the HyperParser to analyze the surroundings of the given
- index.
- """
+ "To initialize, analyze the surroundings of the given index."
self.editwin = editwin
self.text = text = editwin.text
@@ -33,9 +28,10 @@ class HyperParser:
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.
+ # 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))
@@ -49,122 +45,131 @@ class HyperParser:
else:
startatindex = "1.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.
+ # 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, except for the last newline and space.
+ # We want what the parser has, minus the last newline and space.
self.rawtext = parser.str[:-2]
- # As far as I can see, parser.str preserves the statement we are in,
- # so that stopatindex can be used to synchronize the string with the
- # text box indices.
+ # 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]
+ # 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. Note that it must be
- in the same statement.
+ """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))
+ indexinrawtext = (len(self.rawtext) -
+ len(self.text.get(index, self.stopatindex)))
if indexinrawtext < 0:
- raise ValueError("The index given is before the analyzed statement")
+ 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:
+ 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]:
+ 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 is in a string?"""
+ """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 ('"', "'")
+ 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 is in a normal code?"""
- return not self.isopener[self.indexbracket] or \
- self.rawtext[self.bracketing[self.indexbracket][0]] not in \
- ('#', '"', "'")
+ """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):
- """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.
+ """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:
+ 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:
+ 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 (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,
+ # 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
- # This string includes all chars that may be in a white space
+ # Ascii chars that may be in a white space
_whitespace_chars = " \t\n\\"
- # This string includes all chars that may be in an identifier
+ # Ascii chars that may be in an identifier
_id_chars = string.ascii_letters + string.digits + "_"
- # This string includes all chars that may be the first char of an identifier
+ # 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.
+ # 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])):
+ if (i < pos and (str[i] not in self._id_first_chars or
+ (keyword.iskeyword(str[i:pos]) and
+ str[i:pos] not in {'None', 'False', 'True'}))):
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.
+ """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.")
+ raise ValueError("get_expression should only be called"
+ "if index is inside a code.")
rawtext = self.rawtext
bracketing = self.bracketing
@@ -177,20 +182,20 @@ class HyperParser:
postdot_phase = True
while 1:
- # Eat whitespaces, comments, and if postdot_phase is False - one dot
+ # 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] == '.':
+ 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]] == '#':
+ # 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]
@@ -200,8 +205,8 @@ class HyperParser:
break
if not postdot_phase:
- # We didn't find a dot, so the expression end at the last
- # identifier pos.
+ # We didn't find a dot, so the expression end at the
+ # last identifier pos.
break
ret = self._eat_identifier(rawtext, brck_limit, pos)
@@ -209,13 +214,13 @@ class HyperParser:
# There is an identifier to eat
pos = pos - ret
last_identifier_pos = pos
- # Now, in order to continue the search, we must find a dot.
+ # 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.
+ # 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
@@ -244,3 +249,8 @@ class HyperParser:
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/idlelib/IOBinding.py b/Lib/idlelib/IOBinding.py
index f008b46799..841a30810a 100644
--- a/Lib/idlelib/IOBinding.py
+++ b/Lib/idlelib/IOBinding.py
@@ -506,7 +506,7 @@ class IOBinding:
else:
try:
pwd = os.getcwd()
- except os.error:
+ except OSError:
pwd = ""
return pwd, ""
@@ -525,16 +525,17 @@ class IOBinding:
if self.editwin.flist:
self.editwin.update_recent_files_list(filename)
-def test():
+def _io_binding(parent):
root = Tk()
+ 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-s>", self.save)
- self.text.bind("<Alt-s>", self.save_as)
- self.text.bind("<Alt-z>", self.save_a_copy)
def get_saved(self): return 0
def set_saved(self, flag): pass
def reset_undo(self): pass
@@ -542,16 +543,13 @@ def test():
self.text.event_generate("<<open-window-from-file>>")
def save(self, event):
self.text.event_generate("<<save-window>>")
- def save_as(self, event):
- self.text.event_generate("<<save-window-as-file>>")
- def save_a_copy(self, event):
- self.text.event_generate("<<save-copy-of-window-as-file>>")
+
text = Text(root)
text.pack()
text.focus_set()
editwin = MyEditWin(text)
io = IOBinding(editwin)
- root.mainloop()
if __name__ == "__main__":
- test()
+ from idlelib.idle_test.htest import run
+ run(_io_binding)
diff --git a/Lib/idlelib/IdleHistory.py b/Lib/idlelib/IdleHistory.py
index d6cb16272b..078af29053 100644
--- a/Lib/idlelib/IdleHistory.py
+++ b/Lib/idlelib/IdleHistory.py
@@ -100,7 +100,5 @@ class History:
self.prefix = None
if __name__ == "__main__":
- from test import support
- support.use_resources = ['gui']
from unittest import main
main('idlelib.idle_test.test_idlehistory', verbosity=2, exit=False)
diff --git a/Lib/idlelib/MultiCall.py b/Lib/idlelib/MultiCall.py
index 64729eab8c..ee31f3073b 100644
--- a/Lib/idlelib/MultiCall.py
+++ b/Lib/idlelib/MultiCall.py
@@ -32,7 +32,6 @@ Each function will be called at most once for each event.
import sys
import re
import tkinter
-from idlelib import macosxSupport
# the event type constants, which define the meaning of mc_type
MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3;
@@ -45,7 +44,7 @@ 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 macosxSupport.runningAsOSXApp():
+if sys.platform == "darwin":
_modifiers = (("Shift",), ("Control",), ("Option",), ("Command",))
_modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND)
else:
@@ -57,6 +56,13 @@ _modifier_names = dict([(name, number)
for number in range(len(_modifiers))
for name in _modifiers[number]])
+# In 3.4, if no shell window is ever open, the underlying Tk widget is
+# destroyed before .__del__ methods here are called. The following
+# is used to selectively ignore shutdown exceptions to avoid
+# 'Exception ignored' messages. See http://bugs.python.org/issue20167
+APPLICATION_GONE = '''\
+can't invoke "bind" command: application has been destroyed'''
+
# 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:
@@ -98,7 +104,14 @@ class _SimpleBinder:
def __del__(self):
if self.handlerid:
- self.widget.unbind(self.widgetinst, self.sequence, self.handlerid)
+ try:
+ self.widget.unbind(self.widgetinst, self.sequence,
+ self.handlerid)
+ except tkinter.TclError as e:
+ if e.args[0] == APPLICATION_GONE:
+ pass
+ else:
+ raise
# An int in range(1 << len(_modifiers)) represents a combination of modifiers
# (if the least significent bit is on, _modifiers[0] is on, and so on).
@@ -227,7 +240,13 @@ class _ComplexBinder:
def __del__(self):
for seq, id in self.handlerids:
- self.widget.unbind(self.widgetinst, seq, id)
+ try:
+ self.widget.unbind(self.widgetinst, seq, id)
+ except tkinter.TclError as e:
+ if e.args[0] == APPLICATION_GONE:
+ break
+ else:
+ raise
# define the list of event types to be handled by MultiEvent. the order is
# compatible with the definition of event type constants.
@@ -390,15 +409,23 @@ def MultiCallCreator(widget):
func, triplets = self.__eventinfo[virtual]
if func:
for triplet in triplets:
- self.__binders[triplet[1]].unbind(triplet, func)
-
+ try:
+ self.__binders[triplet[1]].unbind(triplet, func)
+ except tkinter.TclError as e:
+ if e.args[0] == APPLICATION_GONE:
+ break
+ else:
+ raise
_multicall_dict[widget] = MultiCall
return MultiCall
-if __name__ == "__main__":
- # Test
+
+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]):
@@ -414,8 +441,13 @@ if __name__ == "__main__":
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/idlelib/MultiStatusBar.py b/Lib/idlelib/MultiStatusBar.py
index 4fc8dcf94b..f44b6a8602 100644
--- a/Lib/idlelib/MultiStatusBar.py
+++ b/Lib/idlelib/MultiStatusBar.py
@@ -17,16 +17,29 @@ class MultiStatusBar(Frame):
label = self.labels[name]
label.config(text=text)
-def _test():
- b = Frame()
- c = Text(b)
- c.pack(side=TOP)
- a = MultiStatusBar(b)
- a.set_label("one", "hello")
- a.set_label("two", "world")
- a.pack(side=BOTTOM, fill=X)
- b.pack()
- b.mainloop()
+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__':
- _test()
+ from idlelib.idle_test.htest import run
+ run(_multistatus_bar)
diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt
index 6388d0dc31..953a38dcda 100644
--- a/Lib/idlelib/NEWS.txt
+++ b/Lib/idlelib/NEWS.txt
@@ -1,74 +1,10 @@
-What's New in IDLE 3.3.4?
-=========================
-
-- Issue #17390: Add Python version to Idle editor window title bar.
- Original patches by Edmond Burnett and Kent Johnson.
-
-- Issue #18960: IDLE now ignores the source encoding declaration on the second
- line if the first line contains anything except a comment.
-
-- Issue #20058: sys.stdin.readline() in IDLE now always returns only one line.
-
-- Issue #19481: print() of string subclass instance in IDLE no longer hangs.
-
-- Issue #18270: Prevent possible IDLE AttributeError on OS X when no initial
- shell window is present.
-
-
-What's New in IDLE 3.3.3?
-=========================
-
-- 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 #7136: In the Idle File menu, "New Window" is renamed "New File".
- Patch by Tal Einat, Roget Serwy, and Todd Rovito.
-
-- Remove dead imports of imp.
-
-- Issue #18196: Avoid displaying spurious SystemExit tracebacks.
-
-- 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 #18055: Move IDLE off of imp and on to importlib.
-
-- Issue #15392: Create a unittest framework for IDLE.
- Initial 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 3.3.2?
+What's New in IDLE 3.4.0?
=========================
- Issue #17390: Display Python version on Idle title bar.
Initial patch by Edmond Burnett.
-
-What's New in IDLE 3.3.1?
-=========================
+- Issue #5066: Update IDLE docs. Patch by Todd Rovito.
- Issue #17625: Close the replace dialog after it is used.
diff --git a/Lib/idlelib/ObjectBrowser.py b/Lib/idlelib/ObjectBrowser.py
index b359efc1b4..7b57aa4c68 100644
--- a/Lib/idlelib/ObjectBrowser.py
+++ b/Lib/idlelib/ObjectBrowser.py
@@ -9,6 +9,8 @@
# XXX TO DO:
# - for classes/modules, add "open source" to object browser
+import re
+
from idlelib.TreeWidget import TreeItem, TreeNode, ScrolledCanvas
from reprlib import Repr
@@ -119,12 +121,14 @@ def make_objecttreeitem(labeltext, object, setfunction=None):
c = ObjectTreeItem
return c(labeltext, object, setfunction)
-# Test script
-def _test():
+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)
@@ -135,4 +139,5 @@ def _test():
root.mainloop()
if __name__ == '__main__':
- _test()
+ from idlelib.idle_test.htest import run
+ run(_object_browser)
diff --git a/Lib/idlelib/PathBrowser.py b/Lib/idlelib/PathBrowser.py
index ba40719084..58ff830b43 100644
--- a/Lib/idlelib/PathBrowser.py
+++ b/Lib/idlelib/PathBrowser.py
@@ -4,10 +4,16 @@ import importlib.machinery
from idlelib.TreeWidget import TreeItem
from idlelib.ClassBrowser import ClassBrowser, ModuleBrowserTreeItem
+from idlelib.PyShell import PyShellFileList
+
class PathBrowser(ClassBrowser):
- def __init__(self, flist):
+ def __init__(self, flist, _htest=False):
+ """
+ _htest - bool, change box location when running htest
+ """
+ self._htest = _htest
self.init(flist)
def settitle(self):
@@ -44,7 +50,7 @@ class DirBrowserTreeItem(TreeItem):
def GetSubList(self):
try:
names = os.listdir(self.dir or os.curdir)
- except os.error:
+ except OSError:
return []
packages = []
for name in names:
@@ -87,12 +93,14 @@ class DirBrowserTreeItem(TreeItem):
sorted.sort()
return sorted
-def main():
- from idlelib import PyShell
- PathBrowser(PyShell.flist)
- if sys.stdin is sys.__stdin__:
- mainloop()
+def _path_browser(parent):
+ 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/idlelib/Percolator.py b/Lib/idlelib/Percolator.py
index c91de38129..9e9331940f 100644
--- a/Lib/idlelib/Percolator.py
+++ b/Lib/idlelib/Percolator.py
@@ -51,8 +51,9 @@ class Percolator:
f.setdelegate(filter.delegate)
filter.setdelegate(None)
-def main():
- import tkinter as Tk
+def _percolator(parent):
+ import tkinter as tk
+ import re
class Tracer(Delegator):
def __init__(self, name):
self.name = name
@@ -63,22 +64,41 @@ def main():
def delete(self, *args):
print(self.name, ": delete", args)
self.delegate.delete(*args)
- root = Tk.Tk()
- root.wm_protocol("WM_DELETE_WINDOW", root.quit)
- text = Tk.Text()
- text.pack()
- text.focus_set()
+ 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")
- p.insertfilter(t1)
- p.insertfilter(t2)
- root.mainloop() # click close widget to continue...
- p.removefilter(t2)
- root.mainloop()
- p.insertfilter(t2)
- p.removefilter(t1)
+
+ 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()
+
root.mainloop()
if __name__ == "__main__":
- main()
+ from idlelib.idle_test.htest import run
+ run(_percolator)
diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py
index 2e5ebb233b..c23b62abd9 100755
--- a/Lib/idlelib/PyShell.py
+++ b/Lib/idlelib/PyShell.py
@@ -419,7 +419,7 @@ class ModifiedInterpreter(InteractiveInterpreter):
try:
self.rpcclt = MyRPCClient(addr)
break
- except socket.error as err:
+ except OSError as err:
pass
else:
self.display_port_binding_error()
@@ -844,7 +844,7 @@ class PyShell(OutputWindow):
("help", "_Help"),
]
- if macosxSupport.runningAsOSXApp():
+ if sys.platform == "darwin":
menu_specs[-2] = ("windows", "_Window")
@@ -1034,7 +1034,10 @@ class PyShell(OutputWindow):
self.close()
return False
else:
- nosub = "==== No Subprocess ===="
+ nosub = ("==== No Subprocess ====\n\n" +
+ "WARNING: Running IDLE without a Subprocess is deprecated\n" +
+ "and will be removed in a later version. See Help/IDLE Help\n" +
+ "for details.\n\n")
sys.displayhook = rpc.displayhook
self.write("Python %s on %s\n%s\n%s" %
@@ -1398,7 +1401,8 @@ USAGE: idle [-deins] [-t title] [file]*
idle [-dns] [-t title] - [arg]*
-h print this help message and exit
- -n run IDLE without a subprocess (see Help/IDLE Help for details)
+ -n run IDLE without a subprocess (DEPRECATED,
+ see Help/IDLE Help for details)
The following options will override the IDLE 'settings' configuration:
@@ -1476,6 +1480,8 @@ def main():
if o == '-i':
enable_shell = True
if o == '-n':
+ print(" Warning: running IDLE without a subprocess is deprecated.",
+ file=sys.stderr)
use_subprocess = False
if o == '-r':
script = a
@@ -1554,7 +1560,7 @@ def main():
shell = flist.open_shell()
if not shell:
return # couldn't open shell
- if macosxSupport.runningAsOSXApp() and flist.dict:
+ 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
diff --git a/Lib/idlelib/ReplaceDialog.py b/Lib/idlelib/ReplaceDialog.py
index e73f2c5039..ffa97a2ca7 100644
--- a/Lib/idlelib/ReplaceDialog.py
+++ b/Lib/idlelib/ReplaceDialog.py
@@ -188,3 +188,34 @@ class ReplaceDialog(SearchDialogBase):
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/idlelib/ScriptBinding.py b/Lib/idlelib/ScriptBinding.py
index 6bfe128e3c..b78363708f 100644
--- a/Lib/idlelib/ScriptBinding.py
+++ b/Lib/idlelib/ScriptBinding.py
@@ -53,7 +53,7 @@ class ScriptBinding:
self.flist = self.editwin.flist
self.root = self.editwin.root
- if macosxSupport.runningAsOSXApp():
+ if macosxSupport.isCocoaTk():
self.editwin.text_frame.bind('<<run-module-event-2>>', self._run_module_event)
def check_module_event(self, event):
@@ -114,7 +114,7 @@ class ScriptBinding:
shell.set_warning_stream(saved_stream)
def run_module_event(self, event):
- if macosxSupport.runningAsOSXApp():
+ 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
diff --git a/Lib/idlelib/ScrolledList.py b/Lib/idlelib/ScrolledList.py
index 0255a0a23f..71ec5470b4 100644
--- a/Lib/idlelib/ScrolledList.py
+++ b/Lib/idlelib/ScrolledList.py
@@ -119,21 +119,22 @@ class ScrolledList:
pass
-def test():
+def _scrolled_list(parent):
root = Tk()
- root.protocol("WM_DELETE_WINDOW", root.destroy)
+ 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="pass")
+ 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))
- s = MyScrolledList(root)
+
+ scrolled_list = MyScrolledList(root)
for i in range(30):
- s.append("item %02d" % i)
- return root
+ scrolled_list.append("Item %02d" % i)
-def main():
- root = test()
root.mainloop()
if __name__ == '__main__':
- main()
+ from idlelib.idle_test.htest import run
+ run(_scrolled_list)
diff --git a/Lib/idlelib/SearchDialog.py b/Lib/idlelib/SearchDialog.py
index bf76c419ac..38408b8dea 100644
--- a/Lib/idlelib/SearchDialog.py
+++ b/Lib/idlelib/SearchDialog.py
@@ -65,3 +65,25 @@ class SearchDialog(SearchDialogBase):
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/idlelib/SearchEngine.py b/Lib/idlelib/SearchEngine.py
index 9d3c4cb78a..da42bd9ea1 100644
--- a/Lib/idlelib/SearchEngine.py
+++ b/Lib/idlelib/SearchEngine.py
@@ -229,6 +229,5 @@ def get_line_col(index):
return line, col
if __name__ == "__main__":
- from test import support; support.use_resources = ['gui']
import unittest
unittest.main('idlelib.idle_test.test_searchengine', verbosity=2, exit=False)
diff --git a/Lib/idlelib/StackViewer.py b/Lib/idlelib/StackViewer.py
index 4ef2d31699..ed2c9e75c5 100644
--- a/Lib/idlelib/StackViewer.py
+++ b/Lib/idlelib/StackViewer.py
@@ -1,9 +1,12 @@
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:
@@ -120,3 +123,30 @@ class VariablesTreeItem(ObjectTreeItem):
item = make_objecttreeitem(key + " =", value, setfunction)
sublist.append(item)
return sublist
+
+def _stack_viewer(parent):
+ 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
+ a
+ except:
+ 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/idlelib/ToolTip.py b/Lib/idlelib/ToolTip.py
index b178803b02..964107e117 100644
--- a/Lib/idlelib/ToolTip.py
+++ b/Lib/idlelib/ToolTip.py
@@ -76,14 +76,22 @@ class ListboxToolTip(ToolTipBase):
for item in self.items:
listbox.insert(END, item)
-def main():
- # Test code
+def _tooltip(parent):
root = Tk()
- b = Button(root, text="Hello", command=root.destroy)
- b.pack()
- root.update()
- tip = ListboxToolTip(b, ["Hello", "world"])
+ 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__':
- main()
+ from idlelib.idle_test.htest import run
+ run(_tooltip)
diff --git a/Lib/idlelib/TreeWidget.py b/Lib/idlelib/TreeWidget.py
index 25bae48047..f05011d470 100644
--- a/Lib/idlelib/TreeWidget.py
+++ b/Lib/idlelib/TreeWidget.py
@@ -381,7 +381,7 @@ class FileTreeItem(TreeItem):
try:
os.rename(self.path, newpath)
self.path = newpath
- except os.error:
+ except OSError:
pass
def GetIconName(self):
@@ -394,7 +394,7 @@ class FileTreeItem(TreeItem):
def GetSubList(self):
try:
names = os.listdir(self.path)
- except os.error:
+ except OSError:
return []
names.sort(key = os.path.normcase)
sublist = []
@@ -448,29 +448,18 @@ class ScrolledCanvas:
return "break"
-# Testing functions
-
-def test():
- from idlelib import PyShell
- root = Toplevel(PyShell.root)
- root.configure(bd=0, bg="yellow")
- root.focus_set()
+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")
- item = FileTreeItem("C:/windows/desktop")
+ sc.frame.pack(expand=1, fill="both", side=LEFT)
+ item = FileTreeItem(os.getcwd())
node = TreeNode(sc.canvas, None, item)
node.expand()
-
-def test2():
- # test w/o scrolling canvas
- root = Tk()
- root.configure(bd=0)
- canvas = Canvas(root, bg="white", highlightthickness=0)
- canvas.pack(expand=1, fill="both")
- item = FileTreeItem(os.curdir)
- node = TreeNode(canvas, None, item)
- node.update()
- canvas.focus_set()
+ root.mainloop()
if __name__ == '__main__':
- test()
+ from idlelib.idle_test.htest import run
+ run(_tree_widget)
diff --git a/Lib/idlelib/UndoDelegator.py b/Lib/idlelib/UndoDelegator.py
index d2ef638ad2..04c1cf5a27 100644
--- a/Lib/idlelib/UndoDelegator.py
+++ b/Lib/idlelib/UndoDelegator.py
@@ -336,17 +336,30 @@ class CommandSequence(Command):
self.depth = self.depth + incr
return self.depth
-def main():
+def _undo_delegator(parent):
from idlelib.Percolator import Percolator
root = Tk()
- root.wm_protocol("WM_DELETE_WINDOW", root.quit)
- text = Text()
+ 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__":
- main()
+ from idlelib.idle_test.htest import run
+ run(_undo_delegator)
diff --git a/Lib/idlelib/WidgetRedirector.py b/Lib/idlelib/WidgetRedirector.py
index ba5251ff71..5ee4298900 100644
--- a/Lib/idlelib/WidgetRedirector.py
+++ b/Lib/idlelib/WidgetRedirector.py
@@ -104,10 +104,12 @@ class OriginalCommand:
return self.tk_call(self.orig_and_operation + args)
-def main():
+def _widget_redirector(parent):
root = Tk()
- root.wm_protocol("WM_DELETE_WINDOW", root.quit)
- text = Text()
+ 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)
@@ -117,10 +119,7 @@ def main():
previous_tcl_fcn(*args)
previous_tcl_fcn = redir.register("insert", my_insert)
root.mainloop()
- redir.unregister("insert") # runs after first 'close window'
- redir.close()
- root.mainloop()
- root.destroy()
if __name__ == "__main__":
- main()
+ from idlelib.idle_test.htest import run
+ run(_widget_redirector)
diff --git a/Lib/idlelib/ZoomHeight.py b/Lib/idlelib/ZoomHeight.py
index e8d1710751..a5d679e499 100644
--- a/Lib/idlelib/ZoomHeight.py
+++ b/Lib/idlelib/ZoomHeight.py
@@ -32,7 +32,7 @@ def zoom_height(top):
newy = 0
newheight = newheight - 72
- elif macosxSupport.runningAsOSXApp():
+ 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.
diff --git a/Lib/idlelib/aboutDialog.py b/Lib/idlelib/aboutDialog.py
index 7fe1ab81ee..e264295535 100644
--- a/Lib/idlelib/aboutDialog.py
+++ b/Lib/idlelib/aboutDialog.py
@@ -12,11 +12,16 @@ class AboutDialog(Toplevel):
"""Modal about dialog for idle
"""
- def __init__(self,parent,title):
+ def __init__(self, parent, title, _htest=False):
+ """
+ _htest - bool, change box location when running htest
+ """
Toplevel.__init__(self, parent)
self.configure(borderwidth=5)
- self.geometry("+%d+%d" % (parent.winfo_rootx()+30,
- parent.winfo_rooty()+30))
+ # 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()
@@ -136,10 +141,5 @@ class AboutDialog(Toplevel):
self.destroy()
if __name__ == '__main__':
- # test the dialog
- root = Tk()
- def run():
- from idlelib import aboutDialog
- aboutDialog.AboutDialog(root, 'About')
- Button(root, text='Dialog', command=run).pack()
- root.mainloop()
+ from idlelib.idle_test.htest import run
+ run(AboutDialog)
diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def
index fdc35ba7b5..3bfcb69015 100644
--- a/Lib/idlelib/config-keys.def
+++ b/Lib/idlelib/config-keys.def
@@ -13,37 +13,37 @@ 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>
+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>
-history-previous=<Alt-Key-p> <Meta-Key-p>
+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>
-open-module=<Alt-Key-m> <Meta-Key-m> <Alt-Key-M>
+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>
+redo=<Control-Shift-Key-Z> <Control-Shift-Key-z>
remove-selection=<Key-Escape>
-save-copy-of-window-as-file=<Alt-Shift-Key-S>
-save-window-as-file=<Control-Shift-Key-S>
-save-window=<Control-Key-s>
-select-all=<Control-Key-a>
+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>
+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>
+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>
@@ -53,8 +53,8 @@ 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>
-change-indentwidth=<Alt-Key-u> <Meta-Key-u> <Alt-Key-U>
+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>
diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def
index 9546e2bf12..8f0fe76bbb 100644
--- a/Lib/idlelib/config-main.def
+++ b/Lib/idlelib/config-main.def
@@ -59,7 +59,7 @@ font-bold= 0
encoding= none
[FormatParagraph]
-paragraph=70
+paragraph=72
[Indent]
use-spaces= 1
diff --git a/Lib/idlelib/configDialog.py b/Lib/idlelib/configDialog.py
index efe5c4326c..30238af768 100644
--- a/Lib/idlelib/configDialog.py
+++ b/Lib/idlelib/configDialog.py
@@ -25,14 +25,19 @@ from idlelib import macosxSupport
class ConfigDialog(Toplevel):
- def __init__(self,parent,title):
+ def __init__(self,parent,title,_htest=False):
+ """
+ _htest - bool, change box location when running htest
+ """
Toplevel.__init__(self, parent)
self.wm_withdraw()
self.configure(borderwidth=5)
self.title('IDLE Preferences')
+ if _htest:
+ parent.instance_dict = {}
self.geometry("+%d+%d" % (parent.winfo_rootx()+20,
- parent.winfo_rooty()+30))
+ 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.
@@ -74,7 +79,7 @@ class ConfigDialog(Toplevel):
frameActionButtons = Frame(self,pady=2)
#action buttons
- if macosxSupport.runningAsOSXApp():
+ if macosxSupport.isAquaTk():
# Surpress the padx and pady arguments when
# running as IDLE.app, otherwise the text
# on these buttons will not be readable.
@@ -1140,9 +1145,5 @@ class ConfigDialog(Toplevel):
pass
if __name__ == '__main__':
- #test the dialog
- root=Tk()
- Button(root,text='Dialog',
- command=lambda:ConfigDialog(root,'Settings')).pack()
- root.instance_dict={}
- root.mainloop()
+ from idlelib.idle_test.htest import run
+ run(ConfigDialog)
diff --git a/Lib/idlelib/configHandler.py b/Lib/idlelib/configHandler.py
index a974d54bce..8608f7c869 100644
--- a/Lib/idlelib/configHandler.py
+++ b/Lib/idlelib/configHandler.py
@@ -20,7 +20,6 @@ configuration problem notification and resolution.
import os
import sys
-from idlelib import macosxSupport
from configparser import ConfigParser, NoOptionError, NoSectionError
class InvalidConfigType(Exception): pass
@@ -271,8 +270,10 @@ class IdleConf:
except OSError:
pass
return default
+
def SetOption(self, configType, section, option, value):
"""In user's config file, set section's option to value.
+
"""
self.userCfg[configType].SetOption(section, option, value)
@@ -525,10 +526,13 @@ class IdleConf:
def GetCurrentKeySet(self):
result = self.GetKeySet(self.CurrentKeys())
- if macosxSupport.runningAsOSXApp():
- # We're using AquaTk, replace all keybingings that use the
- # Alt key by ones that use the Option key because the former
- # don't work reliably.
+ 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.
+ # TO DO: 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:
@@ -638,8 +642,10 @@ class IdleConf:
except OSError:
pass
return keyBindings
+
def GetExtraHelpSourceList(self,configSet):
"""Fetch 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'
diff --git a/Lib/idlelib/configHelpSourceEdit.py b/Lib/idlelib/configHelpSourceEdit.py
index 2ccb400575..242b08db56 100644
--- a/Lib/idlelib/configHelpSourceEdit.py
+++ b/Lib/idlelib/configHelpSourceEdit.py
@@ -8,13 +8,14 @@ import tkinter.messagebox as tkMessageBox
import tkinter.filedialog as tkFileDialog
class GetHelpSourceDialog(Toplevel):
- def __init__(self, parent, title, menuItem='', filePath=''):
+ 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)
@@ -31,12 +32,14 @@ class GetHelpSourceDialog(Toplevel):
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:
- 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)))))
+ #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()
@@ -159,11 +162,5 @@ class GetHelpSourceDialog(Toplevel):
self.destroy()
if __name__ == '__main__':
- #test the dialog
- root = Tk()
- def run():
- keySeq = ''
- dlg = GetHelpSourceDialog(root, 'Get Help Source')
- print(dlg.result)
- Button(root,text='Dialog', command=run).pack()
- root.mainloop()
+ from idlelib.idle_test.htest import run
+ run(GetHelpSourceDialog)
diff --git a/Lib/idlelib/configSectionNameDialog.py b/Lib/idlelib/configSectionNameDialog.py
index b05e38e9d3..5137836981 100644
--- a/Lib/idlelib/configSectionNameDialog.py
+++ b/Lib/idlelib/configSectionNameDialog.py
@@ -8,10 +8,11 @@ from tkinter import *
import tkinter.messagebox as tkMessageBox
class GetCfgSectionNameDialog(Toplevel):
- def __init__(self, parent, title, message, used_names):
+ 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)
@@ -30,11 +31,12 @@ class GetCfgSectionNameDialog(Toplevel):
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)
- ) ) #centre dialog over parent
+ 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()
@@ -92,15 +94,5 @@ if __name__ == '__main__':
import unittest
unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False)
- # also human test the dialog
- root = Tk()
- def run():
- dlg=GetCfgSectionNameDialog(root,'Get Name',
- "After the text entered with [Ok] is stripped, <nothing>, "
- "'abc', or more that 30 chars are errors. "
- "Close with a valid entry (printed), [Cancel], or [X]",
- {'abc'})
- print(dlg.result)
- Message(root, text='').pack() # will be needed for oher dialog tests
- Button(root, text='Click to begin dialog test', command=run).pack()
- root.mainloop()
+ from idlelib.idle_test.htest import run
+ run(GetCfgSectionNameDialog)
diff --git a/Lib/idlelib/dynOptionMenuWidget.py b/Lib/idlelib/dynOptionMenuWidget.py
index 922de96cea..877ca1b14e 100644
--- a/Lib/idlelib/dynOptionMenuWidget.py
+++ b/Lib/idlelib/dynOptionMenuWidget.py
@@ -2,9 +2,10 @@
OptionMenu widget modified to allow dynamic menu reconfiguration
and setting of highlightthickness
"""
-from tkinter import OptionMenu
-from tkinter import _setit
+from tkinter import OptionMenu, _setit, Tk, StringVar, Button
+
import copy
+import re
class DynOptionMenu(OptionMenu):
"""
@@ -33,3 +34,24 @@ class DynOptionMenu(OptionMenu):
command=_setit(self.variable,item,self.command))
if value:
self.variable.set(value)
+
+def _dyn_option_menu(parent):
+ root = Tk()
+ root.title("Tets dynamic option menu")
+ var = StringVar(root)
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ var.set("Old option set") #Set the default value
+ dyn = DynOptionMenu(root,var, "old1","old2","old3","old4")
+ dyn.pack()
+
+ def update():
+ dyn.SetMenu(["new1","new2","new3","new4"],value="new option set")
+
+ button = Button(root, text="Change option set", command=update)
+ button.pack()
+ root.mainloop()
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_dyn_option_menu)
diff --git a/Lib/idlelib/help.txt b/Lib/idlelib/help.txt
index ff786c53f3..6378a2e722 100644
--- a/Lib/idlelib/help.txt
+++ b/Lib/idlelib/help.txt
@@ -1,142 +1,185 @@
[See the end of this file for ** TIPS ** on using IDLE !!]
-Click on the dotted line at the top of a menu to "tear it off": a
-separate window containing the menu is created.
-
-File Menu:
-
- New File -- Create a new file 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
+IDLE is the Python IDE built with the tkinter GUI toolkit.
+
+IDLE has the following features:
+-coded in 100% pure Python, using the tkinter GUI toolkit
+-cross-platform: works on Windows, Unix, and OS X
+-multi-window text editor with multiple undo, Python colorizing, smart indent,
+call tips, and many other features
+-Python shell window (a.k.a interactive interpreter)
+-debugger (not complete, but you can set breakpoints, view and step)
+
+Menus:
+
+IDLE has two window types the Shell window and the Editor window. It is
+possible to have multiple editor windows simultaneously. IDLE's
+menus dynamically change based on which window is currently selected. Each menu
+documented below indicates which window type it is associated with. Click on
+the dotted line at the top of a menu to "tear it off": a separate window
+containing the menu is created (for Unix and Windows only).
+
+File Menu (Shell and Editor):
+
+ New File -- Create a new file editing window
+ Open... -- Open an existing file
+ Open Module... -- Open an existing module (searches sys.path)
+ Recent Files... -- Open a list of recent files
+ 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,
+ ---
+ 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 (Shell and Editor):
+
+ 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
+ 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
+ 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,
+ Show Calltip -- After an unclosed parenthesis for a function, open
+ a small window with function parameter hints
+ Show Parens -- Highlight the surrounding parenthesis
+ Show Completions -- Open a scroll window allowing selection keywords
+ and attributes. (see '*TIPS*', below)
+
+Format Menu (Editor window only):
+
+ Indent Region -- Shift selected lines right by the indent width
+ (default 4 spaces)
+ Dedent Region -- Shift selected lines left by the indent width
+ (default 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 corrent number of spaces
+ Toggle tabs -- Open a dialog to switch between indenting with
+ spaces and tabs.
+ New Indent Width... -- Open a dialog to change indent width. The
+ accepted default by the Python community is 4
+ spaces.
+ Format Paragraph -- Reformat the current blank-line-separated
+ paragraph. All lines in the paragraph will be
+ formatted to less than 80 columns.
+ ---
+ Strip trailing whitespace -- Removed any space characters after the end
+ of the last non-space character
+
+Run Menu (Editor window only):
+
+ Python Shell -- Open or wake up the Python shell window
+ ---
+ Check Module -- Check the syntax of the module currently open in the
+ Editor window. If the module has not been saved IDLE
+ will prompt the user to save the code.
+ Run Module -- Restart the shell to clean the environment, then
+ execute the currently open module. If the module has
+ not been saved IDLE will prompt the user to save the
+ code.
+
+Shell Menu (Shell window only):
+
+ View Last Restart -- Scroll the shell window to the last Shell restart
+ Restart Shell -- Restart the shell to clean the environment
+
+Debug Menu (Shell window only):
+
+ Go to File/Line -- Look around the insert point for a filename
+ and line number, open the file, and show the line.
+ Useful to view the source lines referenced in an
+ exception traceback. Available in the context
+ menu of the Shell window.
+ Debugger (toggle) -- This feature is not complete and considered
+ experimental. Run commands in the shell under the
+ debugger.
+ Stack Viewer -- Show the stack traceback of the last exception
+ Auto-open Stack Viewer (toggle) -- Toggle automatically opening the
+ stack viewer on unhandled
+ exception
+
+Options Menu (Shell and Editor):
+
+ 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 this menu is not present, use
- menu 'IDLE -> Preferences...' instead.
- ---
- 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.)
-
-Windows 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).
+ Startup Preferences may be set, and additional Help
+ sources can be specified.
+
+ ---
+ Code Context (toggle) -- 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. This is not present in the Shell
+ window only the Editor window.
+
+Windows Menu (Shell and Editor):
+
+ Zoom Height -- Toggles the window between normal size (40x80 initial
+ setting) and maximum height. The initial size is in the Configure
+ IDLE dialog under the general tab.
+ ---
+ 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,
+ About IDLE -- Version, copyright, license, credits
+ ---
+ IDLE Help -- Display this file which is a help file for IDLE
+ detailing the menu options, basic editing and navigation,
+ and other tips.
+ Python Docs -- Access local Python documentation, if
+ installed. Or will start a web browser and open
+ docs.python.org showing the latest Python documentation.
+ ---
+ Additional help sources may be added here with the Configure IDLE
+ dialog under the General tab.
+
+Editor 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
+ Copy -- Copy selection into system-wide clipboard
+ Paste -- Insert system-wide clipboard into window
+ Set Breakpoint -- Sets a breakpoint. Breakpoints are only enabled
+ when the debugger is 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,
+ 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
+ Copy -- Copy selection into system-wide clipboard
+ Paste -- Insert system-wide clipboard into window
+ ---
+ Go to file/line -- Same as in Debug menu
** TIPS **
@@ -144,159 +187,182 @@ Shell context menu (Right-click / Control-click on OS X in Shell window):
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.
+ 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.
+ All users can access 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 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.
+ 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 keybindings (like Control-c to copy and Control-v to
+ paste) may work. Keybindings are selected in the Configure IDLE
+ dialog.
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.)
+ 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. Currently tabs are restricted to four spaces due
+ to Tcl/Tk limitations.
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.)
+ 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) a tab is typed 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, by default the
+ Control-space keys 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.
+
+ 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 scroll wheel 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 Editor 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. Or another option is the delay could
+ be set to zero. Another alternative to preventing ACW popups is to
+ disable the call tips extension.
Python Shell window:
- Control-c interrupts executing command.
- Control-d sends end-of-file; closes window if typed at >>> prompt.
+ Control-c interrupts executing command.
+ Control-d sends end-of-file; closes window if typed at >>> prompt.
+ Alt-/ expand word is also useful to reduce typing.
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.
+ Alt-p retrieves previous command matching what you have typed. On OS X
+ use Control-p.
+ Alt-n retrieves next. On OS X use Control-n.
+ Return while cursor is on a previous command retrieves that command.
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.
+ 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
+ Keywords orange
+ Builtins royal purple
+ Strings green
+ Comments red
+ Definitions blue
Shell default colors:
- Console output brown
- stdout blue
- stderr red
- stdin black
+ 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.
+ The font preferences, highlighting, keys, and general preferences can
+ be changed via the Configure IDLE menu option. Be sure to note that
+ 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.
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.
+ Enter idle -h at the command prompt to get a usage message.
+
+ idle.py [-c command] [-d] [-e] [-s] [-t title] [arg] ...
+
+ -c command run this command
+ -d enable debugger
+ -e edit mode; arguments are files to be edited
+ -s run $IDLESTARTUP or $PYTHONSTARTUP first
+ -t title set title of shell window
+
+ If there are arguments:
+ 1. If -e is used, arguments are files opened for editing and sys.argv
+ reflects the arguments passed to IDLE itself.
+ 2. Otherwise, if -c is used, all arguments are placed in
+ sys.argv[1:...], with sys.argv[0] set to -c.
+ 3. Otherwise, if neither -e nor -c is used, the first argument is a
+ script which is executed with the remaining arguments in
+ sys.argv[1:...] and sys.argv[0] set to the script name. If the
+ script name is -, no script is executed but an interactive Python
+ session is started; the arguments are still available in sys.argv.
+
+Running without a subprocess: (DEPRECATED in Python 3.4 see Issue 16123)
+
+ 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
+ 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/idlelib/idle_test/README.txt b/Lib/idlelib/idle_test/README.txt
index 6b92483193..f6b6a21ad5 100644
--- a/Lib/idlelib/idle_test/README.txt
+++ b/Lib/idlelib/idle_test/README.txt
@@ -26,7 +26,6 @@ Once test_xyy is written, the following should go at the end of xyy.py,
with xyz (lowercased) added after 'test_'.
---
if __name__ == "__main__":
- from test import support; support.use_resources = ['gui']
import unittest
unittest.main('idlelib.idle_test.test_', verbosity=2, exit=False)
---
@@ -34,12 +33,12 @@ if __name__ == "__main__":
2. Gui Tests
-Gui tests need 'requires' and 'use_resources' from test.support
-(test.test_support in 2.7). 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 buildbot machines that do not have a graphics
-screen, gui tests must be 'guarded' by "requires('gui')" in a setUp
-function or method. This will typically be setUpClass.
+Gui tests need 'requires' from test.support (test.test_support in 2.7). 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. If a widget, such as a Tk root, is created
@@ -57,11 +56,17 @@ and class attributes, also delete the widget.
del cls.root
---
-Support.requires('gui') returns true if it is either called in a main module
-(which never happens on buildbots) or if use_resources contains 'gui'.
-Use_resources is set by test.regrtest but not by unittest. So when running
-tests in another module with unittest, we set it ourselves, as in the xyz.py
-template above.
+Support.requires('gui') causes the test(s) it guards to be skipped if any of
+a few 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 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".
Since non-gui tests always run, but gui tests only sometimes, tests of non-gui
operations should best avoid needing a gui. Methods that make incidental use of
@@ -88,8 +93,8 @@ python -m idlelib.idle_test.test_xyz
To run all idle_test/test_*.py tests, either interactively
('>>>', with unittest imported) or from a command line, use one of the
-following. (Notes: unittest does not run gui tests; in 2.7, 'test ' (with the
-space) is 'test.regrtest '; where present, -v and -ugui can be omitted.)
+following. (Notes: in 2.7, 'test ' (with the space) is 'test.regrtest ';
+where present, -v and -ugui can be omitted.)
>>> unittest.main('idlelib.idle_test', verbosity=2, exit=False)
python -m unittest -v idlelib.idle_test
@@ -98,13 +103,13 @@ 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 runs
+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. (But gui tests will not this way.)
+unittest on the command line.
python -m unittest -v idlelib.idle_test.test_xyz.Test_case.test_meth
diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py
new file mode 100644
index 0000000000..d0faf44d8a
--- /dev/null
+++ b/Lib/idlelib/idle_test/htest.py
@@ -0,0 +1,369 @@
+'''Run human tests of Idle's window, dialog, and popup widgets.
+
+run(*tests)
+Run each callable in tests after finding the matching test spec in this file.
+If there are none, run an htest for each spec dict in this file after finding
+the matching callable in the module named in the spec.
+
+In a tested module, let X be a global name bound to a widget callable.
+End the module with
+
+if __name__ == '__main__':
+ <unittest, if there is one>
+ from idlelib.idle_test.htest import run
+ run(X)
+
+The X object must have a .__name__ attribute and a 'parent' parameter.
+X will often be a widget class, but a callable instance with .__name__
+or a wrapper function also work. The name of wrapper functions, like
+'_editor_window', should start with '_'.
+
+This file must contain a matching instance of the following template,
+with X.__name__ prepended, as in '_editor_window_spec ...'.
+
+_spec = {
+ 'file': '',
+ 'kwds': {'title': ''},
+ 'msg': ""
+ }
+
+file (no .py): used in run() to import the file and get X.
+kwds: passed to X (**kwds), after 'parent' is added, to initialize X.
+title: an example; used for some widgets, delete if not.
+msg: displayed in a master window. Hints as to how the user might
+ test the widget. Close the window to skip or end the test.
+
+Modules 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': 'Settings',
+ '_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' and 'General' tab, 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."
+ }
+
+_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'."
+ }
+
+_editor_window_spec = {
+ 'file': 'EditorWindow',
+ 'kwds': {},
+ 'msg': "Test editor functions of interest."
+ }
+
+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."
+ }
+
+_help_dialog_spec = {
+ 'file': 'EditorWindow',
+ 'kwds': {},
+ 'msg': "If the help text displays, this works.\n"
+ "Text is selectable. Window is scrollable."
+ }
+
+_io_binding_spec = {
+ 'file': 'IOBinding',
+ 'kwds': {},
+ 'msg': "Test the following bindings\n"
+ "<Control-o> to display open window from file dialog.\n"
+ "<Control-s> to save the file\n"
+ }
+
+_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."
+ }
+
+_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():
+
+ nonlocal test_name, callable_object, test_kwds
+ if len(test_list) == 1:
+ next_button.pack_forget()
+ test_spec, callable_object = test_list.pop()
+ test_kwds = test_spec['kwds']
+ test_kwds['parent'] = root
+ test_name.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(**test_kwds)
+ try:
+ print(widget.result)
+ except AttributeError:
+ pass
+
+ button = tk.Button(root, textvariable=test_name, 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/idlelib/idle_test/mock_idle.py b/Lib/idlelib/idle_test/mock_idle.py
index c364a24dac..9c7e567570 100644
--- a/Lib/idlelib/idle_test/mock_idle.py
+++ b/Lib/idlelib/idle_test/mock_idle.py
@@ -5,6 +5,30 @@ Attributes and methods will be added as needed for tests.
from idlelib.idle_test.mock_tk import Text
+class Func:
+ '''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
+ return self.result
+
+
class Editor:
'''Minimally imitate EditorWindow.EditorWindow class.
'''
@@ -17,6 +41,7 @@ class Editor:
last = self.text.index('end')
return first, last
+
class UndoDelegator:
'''Minimally imitate UndoDelegator,UndoDelegator class.
'''
diff --git a/Lib/idlelib/idle_test/mock_tk.py b/Lib/idlelib/idle_test/mock_tk.py
index 762bbc905d..b9fd5216b9 100644
--- a/Lib/idlelib/idle_test/mock_tk.py
+++ b/Lib/idlelib/idle_test/mock_tk.py
@@ -1,9 +1,27 @@
"""Classes that replace tkinter gui objects used by an object being tested.
-A gui object is anything with a master or parent paramenter, which is typically
-required in spite of what the doc strings say.
+A gui object is anything with a master or parent paramenter, which is
+typically required in spite of what the doc strings say.
"""
+class Event:
+ '''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:
"Use for String/Int/BooleanVar: incomplete"
def __init__(self, master=None, value=None, name=None):
@@ -20,9 +38,10 @@ class Mbox_func:
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):
- self.result = None # The return for all show funcs
+ 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
diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py
new file mode 100644
index 0000000000..bcc853c9f2
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_autocomplete.py
@@ -0,0 +1,143 @@
+import unittest
+from test.support import requires
+from tkinter import Tk, Text, TclError
+
+import idlelib.AutoComplete as ac
+import idlelib.AutoCompleteWindow as acw
+import idlelib.macosxSupport as mac
+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()
+ mac.setupApp(cls.root, None)
+ cls.text = Text(cls.root)
+ cls.editor = DummyEditwin(cls.root, cls.text)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.text
+ del cls.editor
+ 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
+ testwin = self.autocomplete._make_autocomplete_window()
+ self.text.insert('1.0', 're.')
+ 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/idlelib/idle_test/test_autoexpand.py b/Lib/idlelib/idle_test/test_autoexpand.py
new file mode 100644
index 0000000000..7f112a0025
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_autoexpand.py
@@ -0,0 +1,141 @@
+"""Unit tests for idlelib.AutoExpand"""
+import unittest
+from 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):
+ if hasattr(cls, 'tk'):
+ cls.tk.destroy()
+ del cls.tk
+ del cls.text, cls.auto_expand
+
+ 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/idlelib/idle_test/test_calltips.py b/Lib/idlelib/idle_test/test_calltips.py
index f3637646c4..4ee15aef76 100644
--- a/Lib/idlelib/idle_test/test_calltips.py
+++ b/Lib/idlelib/idle_test/test_calltips.py
@@ -54,9 +54,9 @@ class Get_signatureTest(unittest.TestCase):
gtest(List, List.__doc__)
gtest(list.__new__,
- 'T.__new__(S, ...) -> a new object with type S, a subtype of T')
+ 'Create and return a new object. See help(type) for accurate signature.')
gtest(list.__init__,
- 'x.__init__(...) initializes x; see help(type(x)) for signature')
+ 'Initialize self. See help(type(self)) for accurate signature.')
append_doc = "L.append(object) -> None -- append object to end"
gtest(list.append, append_doc)
gtest([].append, append_doc)
@@ -69,7 +69,8 @@ class Get_signatureTest(unittest.TestCase):
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, tabsize=8)''')
+ drop_whitespace=True, break_on_hyphens=True, tabsize=8, *, max_lines=None,
+ placeholder=' [...]')''')
def test_docline_truncation(self):
def f(): pass
diff --git a/Lib/idlelib/idle_test/test_formatparagraph.py b/Lib/idlelib/idle_test/test_formatparagraph.py
index f4a7c2d680..690c936145 100644
--- a/Lib/idlelib/idle_test/test_formatparagraph.py
+++ b/Lib/idlelib/idle_test/test_formatparagraph.py
@@ -293,7 +293,7 @@ class FormatEventTest(unittest.TestCase):
# 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')
+ self.formatter('ParameterDoesNothing', limit=70)
result = text.get('1.0', 'insert')
# find function includes \n
expected = (
@@ -305,7 +305,7 @@ class FormatEventTest(unittest.TestCase):
# 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')
+ self.formatter('ParameterDoesNothing', limit=70)
result = text.get('1.0', 'insert')
# selection excludes \n
expected = (
@@ -319,7 +319,7 @@ class FormatEventTest(unittest.TestCase):
# Select 2 long lines.
text.insert('1.0', self.multiline_test_string)
text.tag_add('sel', '2.0', '4.0')
- self.formatter('ParameterDoesNothing')
+ 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"
@@ -334,7 +334,7 @@ class FormatEventTest(unittest.TestCase):
# Set cursor ('insert') to '1.0', within block.
text.insert('1.0', self.multiline_test_comment)
- self.formatter('ParameterDoesNothing')
+ 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"
@@ -348,7 +348,7 @@ class FormatEventTest(unittest.TestCase):
# 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')
+ self.formatter('ParameterDoesNothing', limit=70)
result = text.get('1.0', 'insert')
expected = (
"# The first line is under the max width.\n"
diff --git a/Lib/idlelib/idle_test/test_hyperparser.py b/Lib/idlelib/idle_test/test_hyperparser.py
new file mode 100644
index 0000000000..68e97fcf81
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_hyperparser.py
@@ -0,0 +1,191 @@
+"""Unittest for idlelib.HyperParser"""
+import unittest
+from 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
+ is_char_in_string = EditorWindow.is_char_in_string
+
+
+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.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/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py
new file mode 100644
index 0000000000..a9b60ca832
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_textview.py
@@ -0,0 +1,97 @@
+'''Test the functions and main class method of textView.py.
+
+Since all methods and functions create (or destroy) a TextViewer, which
+is a widget containing multiple widgets, all tests must be gui tests.
+Using mock Text would not change this. Other mocks are used to retrieve
+information about calls.
+
+The coverage is essentially 100%.
+'''
+from test.support import requires
+requires('gui')
+
+import unittest
+import os
+from tkinter import Tk, Text, TclError
+from idlelib import textView as tv
+from idlelib.idle_test.mock_idle import Func
+from idlelib.idle_test.mock_tk import Mbox
+
+def setUpModule():
+ global root
+ root = Tk()
+
+def tearDownModule():
+ global root
+ root.destroy()
+ del root
+
+
+class TV(tv.TextViewer): # used by TextViewTest
+ transient = Func()
+ grab_set = Func()
+ wait_window = Func()
+
+class TextViewTest(unittest.TestCase):
+
+ def setUp(self):
+ TV.transient.__init__()
+ TV.grab_set.__init__()
+ TV.wait_window.__init__()
+
+ def test_init_modal(self):
+ view = TV(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(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(root, 'Title', 'test text', modal=False)
+ view.destroy = Func()
+ view.Ok()
+ self.assertTrue(view.destroy.called)
+ del view.destroy # unmask real function
+ view.destroy
+
+
+class textviewTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.orig_mbox = tv.tkMessageBox
+ tv.tkMessageBox = Mbox
+
+ @classmethod
+ def tearDownClass(cls):
+ tv.tkMessageBox = cls.orig_mbox
+ del cls.orig_mbox
+
+ def test_view_text(self):
+ # If modal True, tkinter will error with 'can't invoke "event" command'
+ view = tv.view_text(root, 'Title', 'test text', modal=False)
+ self.assertIsInstance(view, tv.TextViewer)
+
+ def test_view_file(self):
+ test_dir = os.path.dirname(__file__)
+ testfile = os.path.join(test_dir, 'test_textview.py')
+ view = tv.view_file(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 and view_file will not return anything
+ testfile = os.path.join(test_dir, '../notthere.py')
+ view = tv.view_file(root, 'Title', testfile, modal=False)
+ self.assertIsNone(view)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py
index 044bef929f..22acb413d9 100644
--- a/Lib/idlelib/idlever.py
+++ b/Lib/idlelib/idlever.py
@@ -1 +1 @@
-IDLE_VERSION = "3.3.5"
+IDLE_VERSION = "3.4.1"
diff --git a/Lib/idlelib/keybindingDialog.py b/Lib/idlelib/keybindingDialog.py
index 0f0da8c7e9..e6438bfc39 100644
--- a/Lib/idlelib/keybindingDialog.py
+++ b/Lib/idlelib/keybindingDialog.py
@@ -4,15 +4,16 @@ Dialog for building Tkinter accelerator key bindings
from tkinter import *
import tkinter.messagebox as tkMessageBox
import string
-from idlelib import macosxSupport
+import sys
class GetKeysDialog(Toplevel):
- def __init__(self,parent,title,action,currentKeySequences):
+ 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)
@@ -38,11 +39,14 @@ class GetKeysDialog(Toplevel):
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)) )) ) #centre dialog over parent
+ 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()
@@ -133,8 +137,7 @@ class GetKeysDialog(Toplevel):
order is also important: key binding equality depends on it, so
config-keys.def must use the same ordering.
"""
- import sys
- if macosxSupport.runningAsOSXApp():
+ if sys.platform == "darwin":
self.modifiers = ['Shift', 'Control', 'Option', 'Command']
else:
self.modifiers = ['Control', 'Alt', 'Shift']
@@ -259,11 +262,5 @@ class GetKeysDialog(Toplevel):
return keysOK
if __name__ == '__main__':
- #test the dialog
- root=Tk()
- def run():
- keySeq=''
- dlg=GetKeysDialog(root,'Get Keys','find-again',[])
- print(dlg.result)
- Button(root,text='Dialog',command=run).pack()
- root.mainloop()
+ from idlelib.idle_test.htest import run
+ run(GetKeysDialog)
diff --git a/Lib/idlelib/macosxSupport.py b/Lib/idlelib/macosxSupport.py
index 67069fa0f3..b6488f84ca 100644
--- a/Lib/idlelib/macosxSupport.py
+++ b/Lib/idlelib/macosxSupport.py
@@ -1,48 +1,70 @@
"""
-A number of function that enhance IDLE on MacOSX when it used as a normal
-GUI application (as opposed to an X11 application).
+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()
-_appbundle = None
+def isCarbonAquaTk(root):
+ warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()",
+ DeprecationWarning, stacklevel=2)
+ return isCarbonTk()
-def runningAsOSXApp():
+_tk_type = None
+
+def _initializeTkVariantTests(root):
+ """
+ Initializes OS X Tk variant values for
+ isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
"""
- Returns True if Python is running from within an app on OSX.
- If so, the various OS X customizations will be triggered later (menu
- fixup, et al). (Originally, this test was supposed to condition
- behavior on whether IDLE was running under Aqua Tk rather than
- under X11 Tk but that does not work since a framework build
- could be linked with X11. For several releases, this test actually
- differentiates between whether IDLE is running from a framework or
- not. As a future enhancement, it should be considered whether there
- should be a difference based on framework and any needed X11 adaptions
- should be made dependent on a new function that actually tests for X11.)
- """
- global _appbundle
- if _appbundle is None:
- _appbundle = sys.platform == 'darwin'
- if _appbundle:
- import sysconfig
- _appbundle = bool(sysconfig.get_config_var('PYTHONFRAMEWORK'))
- return _appbundle
-
-_carbonaquatk = None
+ 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 isCarbonAquaTk(root):
+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).
"""
- global _carbonaquatk
- if _carbonaquatk is None:
- _carbonaquatk = (runningAsOSXApp() and
- 'aqua' in root.tk.call('tk', 'windowingsystem') and
- 'AppKit' not in root.tk.call('winfo', 'server', '.'))
- return _carbonaquatk
+ 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):
"""
@@ -53,8 +75,7 @@ def tkVersionWarning(root):
can still crash unexpectedly.
"""
- if (runningAsOSXApp() and
- ('AppKit' in root.tk.call('winfo', 'server', '.')) ):
+ if isCocoaTk():
patchlevel = root.tk.call('info', 'patchlevel')
if patchlevel not in ('8.5.7', '8.5.9'):
return False
@@ -88,8 +109,8 @@ def hideTkConsole(root):
def overrideRootMenu(root, flist):
"""
- Replace the Tk root menu by something that's more appropriate for
- IDLE.
+ 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
@@ -108,6 +129,22 @@ def overrideRootMenu(root, flist):
from idlelib import WindowList
from idlelib.MultiCall import MultiCallCreator
+ 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' entry from the options menu, it is in the
+ # application menu as 'Preferences'
+ del Bindings.menudefs[-2][1][0:2]
+
menubar = Menu(root)
root.configure(menu=menubar)
menudict = {}
@@ -156,7 +193,7 @@ def overrideRootMenu(root, flist):
# right thing for now.
root.createcommand('exit', flist.close_all_callback)
- if isCarbonAquaTk(root):
+ if isCarbonTk():
# for Carbon AquaTk, replace the default Tk apple menu
menudict['application'] = menu = Menu(menubar, name='apple')
menubar.add_cascade(label='IDLE', menu=menu)
@@ -171,8 +208,7 @@ def overrideRootMenu(root, flist):
Bindings.menudefs[0][1].append(
('_Preferences....', '<<open-config-dialog>>'),
)
- else:
- # assume Cocoa AquaTk
+ if isCocoaTk():
# replace default About dialog with About IDLE one
root.createcommand('tkAboutDialog', about_dialog)
# replace default "Help" item in Help menu
@@ -182,10 +218,22 @@ def overrideRootMenu(root, flist):
def setupApp(root, flist):
"""
- Perform setup for the OSX application bundle.
+ 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.
"""
- if not runningAsOSXApp(): return
-
- hideTkConsole(root)
- overrideRootMenu(root, flist)
- addOpenEventSupport(root, flist)
+ _initializeTkVariantTests(root)
+ if isAquaTk():
+ hideTkConsole(root)
+ overrideRootMenu(root, flist)
+ addOpenEventSupport(root, flist)
diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py
index ddce6e9389..9c51b8f6b5 100644
--- a/Lib/idlelib/rpc.py
+++ b/Lib/idlelib/rpc.py
@@ -199,7 +199,7 @@ class SocketIO(object):
raise
except KeyboardInterrupt:
raise
- except socket.error:
+ except OSError:
raise
except Exception as ex:
return ("CALLEXC", ex)
@@ -340,7 +340,7 @@ class SocketIO(object):
n = self.sock.send(s[:BUFSIZE])
except (AttributeError, TypeError):
raise OSError("socket no longer exists")
- except socket.error:
+ except OSError:
raise
else:
s = s[n:]
@@ -357,7 +357,7 @@ class SocketIO(object):
return None
try:
s = self.sock.recv(BUFSIZE)
- except socket.error:
+ except OSError:
raise EOFError
if len(s) == 0:
raise EOFError
@@ -537,7 +537,7 @@ class RPCClient(SocketIO):
SocketIO.__init__(self, working_sock)
else:
print("** Invalid host: ", address, file=sys.__stderr__)
- raise socket.error
+ raise OSError
def get_remote_proxy(self, oid):
return RPCProxy(self, oid)
diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py
index c1859b66b2..13cec62976 100644
--- a/Lib/idlelib/run.py
+++ b/Lib/idlelib/run.py
@@ -150,8 +150,8 @@ def manage_socket(address):
try:
server = MyRPCServer(address, MyHandler)
break
- except socket.error as err:
- print("IDLE Subprocess: socket error: " + err.args[1] +
+ except OSError as err:
+ print("IDLE Subprocess: OSError: " + err.args[1] +
", retrying....", file=sys.__stderr__)
socket_error = err
else:
diff --git a/Lib/idlelib/tabbedpages.py b/Lib/idlelib/tabbedpages.py
index 2557732755..965f9f8593 100644
--- a/Lib/idlelib/tabbedpages.py
+++ b/Lib/idlelib/tabbedpages.py
@@ -467,9 +467,12 @@ class TabbedPageSet(Frame):
self._tab_set.set_selected_tab(page_name)
-if __name__ == '__main__':
+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,
)
@@ -488,3 +491,8 @@ if __name__ == '__main__':
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/idlelib/textView.py b/Lib/idlelib/textView.py
index dd50544c41..4257eeabae 100644
--- a/Lib/idlelib/textView.py
+++ b/Lib/idlelib/textView.py
@@ -9,15 +9,21 @@ class TextViewer(Toplevel):
"""A simple text viewer dialog for IDLE
"""
- def __init__(self, parent, title, text, modal=True):
+ 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" % (625, 500,
- parent.winfo_rootx() + 10,
- parent.winfo_rooty() + 10))
+ 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'
@@ -66,32 +72,15 @@ def view_file(parent, title, filename, encoding=None, modal=True):
try:
with open(filename, 'r', encoding=encoding) as file:
contents = file.read()
- except OSError:
- import tkinter.messagebox as tkMessageBox
+ except IOError:
tkMessageBox.showerror(title='File Load Error',
message='Unable to load file %r .' % filename,
parent=parent)
else:
return view_text(parent, title, contents, modal)
-
if __name__ == '__main__':
- #test the dialog
- root=Tk()
- root.title('textView test')
- filename = './textView.py'
- with open(filename, 'r') as f:
- text = f.read()
- btn1 = Button(root, text='view_text',
- command=lambda:view_text(root, 'view_text', text))
- btn1.pack(side=LEFT)
- btn2 = Button(root, text='view_file',
- command=lambda:view_file(root, 'view_file', filename))
- btn2.pack(side=LEFT)
- btn3 = Button(root, text='nonmodal view_text',
- command=lambda:view_text(root, 'nonmodal view_text', text,
- modal=False))
- btn3.pack(side=LEFT)
- close = Button(root, text='Close', command=root.destroy)
- close.pack(side=RIGHT)
- root.mainloop()
+ import unittest
+ unittest.main('idlelib.idle_test.test_textview', verbosity=2, exit=False)
+ from idlelib.idle_test.htest import run
+ run(TextViewer)