summaryrefslogtreecommitdiff
path: root/Lib/idlelib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/idlelib')
-rw-r--r--Lib/idlelib/NEWS.txt96
-rw-r--r--Lib/idlelib/README.txt287
-rw-r--r--Lib/idlelib/__init__.py2
-rw-r--r--Lib/idlelib/__main__.py4
-rw-r--r--Lib/idlelib/autocomplete.py (renamed from Lib/idlelib/AutoComplete.py)67
-rw-r--r--Lib/idlelib/autocomplete_w.py (renamed from Lib/idlelib/AutoCompleteWindow.py)41
-rw-r--r--Lib/idlelib/autoexpand.py (renamed from Lib/idlelib/AutoExpand.py)7
-rw-r--r--Lib/idlelib/browser.py (renamed from Lib/idlelib/ClassBrowser.py)18
-rw-r--r--Lib/idlelib/calltip_w.py (renamed from Lib/idlelib/CallTipWindow.py)8
-rw-r--r--Lib/idlelib/calltips.py (renamed from Lib/idlelib/CallTips.py)12
-rw-r--r--Lib/idlelib/codecontext.py (renamed from Lib/idlelib/CodeContext.py)12
-rw-r--r--Lib/idlelib/colorizer.py (renamed from Lib/idlelib/ColorDelegator.py)40
-rw-r--r--Lib/idlelib/config-keys.def51
-rw-r--r--Lib/idlelib/config-main.def74
-rw-r--r--Lib/idlelib/config.py (renamed from Lib/idlelib/configHandler.py)185
-rw-r--r--Lib/idlelib/configHelpSourceEdit.py170
-rw-r--r--Lib/idlelib/configSectionNameDialog.py98
-rw-r--r--Lib/idlelib/config_key.py (renamed from Lib/idlelib/keybindingDialog.py)21
-rw-r--r--Lib/idlelib/configdialog.py (renamed from Lib/idlelib/configDialog.py)163
-rw-r--r--Lib/idlelib/debugger.py (renamed from Lib/idlelib/Debugger.py)21
-rw-r--r--Lib/idlelib/debugger_r.py (renamed from Lib/idlelib/RemoteDebugger.py)8
-rw-r--r--Lib/idlelib/debugobj.py (renamed from Lib/idlelib/ObjectBrowser.py)26
-rw-r--r--Lib/idlelib/debugobj_r.py (renamed from Lib/idlelib/RemoteObjectBrowser.py)0
-rw-r--r--Lib/idlelib/delegator.py (renamed from Lib/idlelib/Delegator.py)0
-rw-r--r--Lib/idlelib/dynoption.py (renamed from Lib/idlelib/dynOptionMenuWidget.py)9
-rw-r--r--Lib/idlelib/editor.py (renamed from Lib/idlelib/EditorWindow.py)276
-rw-r--r--Lib/idlelib/filelist.py (renamed from Lib/idlelib/FileList.py)5
-rw-r--r--Lib/idlelib/grep.py (renamed from Lib/idlelib/GrepDialog.py)48
-rw-r--r--Lib/idlelib/help.py26
-rw-r--r--Lib/idlelib/help.txt372
-rw-r--r--Lib/idlelib/help_about.py (renamed from Lib/idlelib/aboutDialog.py)13
-rw-r--r--Lib/idlelib/history.py (renamed from Lib/idlelib/IdleHistory.py)8
-rw-r--r--Lib/idlelib/hyperparser.py (renamed from Lib/idlelib/HyperParser.py)7
-rw-r--r--Lib/idlelib/idle.py3
-rw-r--r--Lib/idlelib/idle.pyw12
-rw-r--r--Lib/idlelib/idle_test/__init__.py2
-rw-r--r--Lib/idlelib/idle_test/htest.py145
-rw-r--r--Lib/idlelib/idle_test/mock_idle.py4
-rw-r--r--Lib/idlelib/idle_test/test_autocomplete.py13
-rw-r--r--Lib/idlelib/idle_test/test_autoexpand.py6
-rw-r--r--Lib/idlelib/idle_test/test_calltips.py2
-rw-r--r--Lib/idlelib/idle_test/test_colorizer.py56
-rw-r--r--Lib/idlelib/idle_test/test_config.py160
-rw-r--r--Lib/idlelib/idle_test/test_config_help.py106
-rw-r--r--Lib/idlelib/idle_test/test_config_key.py33
-rw-r--r--Lib/idlelib/idle_test/test_config_name.py75
-rw-r--r--Lib/idlelib/idle_test/test_configdialog.py135
-rw-r--r--Lib/idlelib/idle_test/test_debugger.py29
-rw-r--r--Lib/idlelib/idle_test/test_delegator.py2
-rw-r--r--Lib/idlelib/idle_test/test_editmenu.py30
-rw-r--r--Lib/idlelib/idle_test/test_editor.py2
-rw-r--r--Lib/idlelib/idle_test/test_grep.py6
-rw-r--r--Lib/idlelib/idle_test/test_help.py34
-rw-r--r--Lib/idlelib/idle_test/test_help_about.py24
-rw-r--r--Lib/idlelib/idle_test/test_history.py (renamed from Lib/idlelib/idle_test/test_idlehistory.py)4
-rw-r--r--Lib/idlelib/idle_test/test_hyperparser.py6
-rw-r--r--Lib/idlelib/idle_test/test_iomenu.py (renamed from Lib/idlelib/idle_test/test_io.py)5
-rw-r--r--Lib/idlelib/idle_test/test_macosx.py103
-rw-r--r--Lib/idlelib/idle_test/test_paragraph.py (renamed from Lib/idlelib/idle_test/test_formatparagraph.py)8
-rw-r--r--Lib/idlelib/idle_test/test_parenmatch.py17
-rw-r--r--Lib/idlelib/idle_test/test_pathbrowser.py8
-rw-r--r--Lib/idlelib/idle_test/test_percolator.py4
-rw-r--r--Lib/idlelib/idle_test/test_query.py353
-rw-r--r--Lib/idlelib/idle_test/test_redirector.py (renamed from Lib/idlelib/idle_test/test_widgetredir.py)5
-rw-r--r--Lib/idlelib/idle_test/test_replace.py (renamed from Lib/idlelib/idle_test/test_replacedialog.py)16
-rw-r--r--Lib/idlelib/idle_test/test_rstrip.py4
-rw-r--r--Lib/idlelib/idle_test/test_scrolledlist.py29
-rw-r--r--Lib/idlelib/idle_test/test_search.py (renamed from Lib/idlelib/idle_test/test_searchdialog.py)8
-rw-r--r--Lib/idlelib/idle_test/test_searchbase.py (renamed from Lib/idlelib/idle_test/test_searchdialogbase.py)27
-rw-r--r--Lib/idlelib/idle_test/test_searchengine.py16
-rw-r--r--Lib/idlelib/idle_test/test_text.py21
-rw-r--r--Lib/idlelib/idle_test/test_textview.py20
-rw-r--r--Lib/idlelib/idle_test/test_tree.py36
-rw-r--r--Lib/idlelib/idle_test/test_undo.py (renamed from Lib/idlelib/idle_test/test_undodelegator.py)8
-rw-r--r--Lib/idlelib/idle_test/test_warning.py6
-rw-r--r--Lib/idlelib/idlever.py12
-rw-r--r--Lib/idlelib/iomenu.py (renamed from Lib/idlelib/IOBinding.py)95
-rw-r--r--Lib/idlelib/macosx.py (renamed from Lib/idlelib/macosxSupport.py)99
-rw-r--r--Lib/idlelib/mainmenu.py (renamed from Lib/idlelib/Bindings.py)4
-rw-r--r--Lib/idlelib/multicall.py (renamed from Lib/idlelib/MultiCall.py)17
-rw-r--r--Lib/idlelib/outwin.py (renamed from Lib/idlelib/OutputWindow.py)11
-rw-r--r--Lib/idlelib/paragraph.py (renamed from Lib/idlelib/FormatParagraph.py)10
-rw-r--r--Lib/idlelib/parenmatch.py (renamed from Lib/idlelib/ParenMatch.py)14
-rw-r--r--Lib/idlelib/pathbrowser.py (renamed from Lib/idlelib/PathBrowser.py)11
-rw-r--r--Lib/idlelib/percolator.py (renamed from Lib/idlelib/Percolator.py)13
-rw-r--r--Lib/idlelib/pyparse.py (renamed from Lib/idlelib/PyParse.py)2
-rwxr-xr-xLib/idlelib/pyshell.py (renamed from Lib/idlelib/PyShell.py)220
-rw-r--r--Lib/idlelib/query.py308
-rw-r--r--Lib/idlelib/redirector.py (renamed from Lib/idlelib/WidgetRedirector.py)21
-rw-r--r--Lib/idlelib/replace.py (renamed from Lib/idlelib/ReplaceDialog.py)26
-rw-r--r--Lib/idlelib/rpc.py26
-rw-r--r--Lib/idlelib/rstrip.py (renamed from Lib/idlelib/RstripExtension.py)0
-rw-r--r--Lib/idlelib/run.py159
-rw-r--r--Lib/idlelib/runscript.py (renamed from Lib/idlelib/ScriptBinding.py)17
-rw-r--r--Lib/idlelib/scrolledlist.py (renamed from Lib/idlelib/ScrolledList.py)21
-rw-r--r--Lib/idlelib/search.py (renamed from Lib/idlelib/SearchDialog.py)29
-rw-r--r--Lib/idlelib/searchbase.py (renamed from Lib/idlelib/SearchDialogBase.py)40
-rw-r--r--Lib/idlelib/searchengine.py (renamed from Lib/idlelib/SearchEngine.py)5
-rw-r--r--Lib/idlelib/stackviewer.py (renamed from Lib/idlelib/StackViewer.py)30
-rw-r--r--Lib/idlelib/statusbar.py (renamed from Lib/idlelib/MultiStatusBar.py)35
-rw-r--r--Lib/idlelib/tabbedpages.py26
-rw-r--r--Lib/idlelib/textview.py (renamed from Lib/idlelib/textView.py)34
-rw-r--r--Lib/idlelib/tooltip.py (renamed from Lib/idlelib/ToolTip.py)19
-rw-r--r--Lib/idlelib/tree.py (renamed from Lib/idlelib/TreeWidget.py)23
-rw-r--r--Lib/idlelib/undo.py (renamed from Lib/idlelib/UndoDelegator.py)26
-rw-r--r--Lib/idlelib/windows.py (renamed from Lib/idlelib/WindowList.py)2
-rw-r--r--Lib/idlelib/zoomheight.py (renamed from Lib/idlelib/ZoomHeight.py)6
107 files changed, 2913 insertions, 2206 deletions
diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt
index 72905c18dd..a3fc5012fb 100644
--- a/Lib/idlelib/NEWS.txt
+++ b/Lib/idlelib/NEWS.txt
@@ -1,45 +1,101 @@
-What's New in IDLE 3.5.3?
-=========================
-*Release date: 2017-01-01?*
+What's New in IDLE 3.6.0?
+===========================
+*Release date: 2016-12-16?*
- Issue #15308: Add 'interrupt execution' (^C) to Shell menu.
Patch by Roger Serwy, updated by Bayard Randel.
- Issue #27922: Stop IDLE tests from 'flashing' gui widgets on the screen.
+- Issue #27891: Consistently group and sort imports within idlelib modules.
+
+- Issue #17642: add larger font sizes for classroom projection.
+
- Add version to title of IDLE help window.
- Issue #25564: In section on IDLE -- console differences, mention that
using exec means that __builtins__ is defined for each statement.
+- Issue #27821: Fix 3.6.0a3 regression that prevented custom key sets
+ from being selected when no custom theme was defined.
+
- Issue #27714: text_textview and test_autocomplete now pass when re-run
in the same process. This occurs when test_idle fails when run with the
-w option but without -jn. Fix warning from test_config.
+- Issue #27621: Put query response validation error messages in the query
+ box itself instead of in a separate massagebox. Redo tests to match.
+ Add Mac OSX refinements. Original patch by Mark Roseman.
+
+- Issue #27620: Escape key now closes Query box as cancelled.
+
+- Issue #27609: IDLE: tab after initial whitespace should tab, not
+ autocomplete. This fixes problem with writing docstrings at least
+ twice indented.
+
+- Issue #27609: Explicitly return None when there are also non-None
+ returns. In a few cases, reverse a condition and eliminate a return.
+
- Issue #25507: IDLE no longer runs buggy code because of its tkinter imports.
Users must include the same imports required to run directly in Python.
+- Issue #27173: Add 'IDLE Modern Unix' to the built-in key sets.
+ Make the default key set depend on the platform.
+ Add tests for the changes to the config module.
+
+- Issue #27452: add line counter and crc to IDLE configHandler test dump.
+
+- Issue #27477: IDLE search dialogs now use ttk widgets.
+
+- Issue #27173: Add 'IDLE Modern Unix' to the built-in key sets.
+ Make the default key set depend on the platform.
+ Add tests for the changes to the config module.
+
+- Issue #27452: make command line "idle-test> python test_help.py" work.
+ __file__ is relative when python is started in the file's directory.
+
- Issue #27452: add line counter and crc to IDLE configHandler test dump.
+- Issue #27380: IDLE: add query.py with base Query dialog and ttk widgets.
+ Module had subclasses SectionName, ModuleName, and HelpSource, which are
+ used to get information from users by configdialog and file =>Load Module.
+ Each subclass has itw own validity checks. Using ModuleName allows users
+ to edit bad module names instead of starting over.
+ Add tests and delete the two files combined into the new one.
+
+- Issue #27372: Test_idle no longer changes the locale.
+
- Issue #27365: Allow non-ascii chars in IDLE NEWS.txt, for contributor names.
- Issue #27245: IDLE: Cleanly delete custom themes and key bindings.
Previously, when IDLE was started from a console or by import, a cascade
of warnings was emitted. Patch by Serhiy Storchaka.
+- Issue #24137: Run IDLE, test_idle, and htest with tkinter default root disabled.
+ Fix code and tests that fail with this restriction.
+ Fix htests to not create a second and redundant root and mainloop.
-What's New in IDLE 3.5.2?
-=========================
-*Release date: 2016-06-26*
+- Issue #27310: Fix IDLE.app failure to launch on OS X due to vestigial import.
- Issue #5124: Paste with text selected now replaces the selection on X11.
This matches how paste works on Windows, Mac, most modern Linux apps,
and ttk widgets. Original patch by Serhiy Storchaka.
+- Issue #24750: Switch all scrollbars in IDLE to ttk versions.
+ Where needed, minimal tests are added to cover changes.
+
+- Issue #24759: IDLE requires tk 8.5 and availability ttk widgets.
+ Delete now unneeded tk version tests and code for older versions.
+ Add test for IDLE syntax colorizer.
+
+- Issue #27239: idlelib.macosx.isXyzTk functions initialize as needed.
+
+- Issue #27262: move Aqua unbinding code, which enable context menus, to maxosx.
+
- Issue #24759: Make clear in idlelib.idle_test.__init__ that the directory
is a private implementation of test.test_idle and tool for maintainers.
-- Issue #27196: Stop 'ThemeChangef' warnings when running IDLE tests.
+- Issue #27196: Stop 'ThemeChanged' warnings when running IDLE tests.
These persisted after other warnings were suppressed in #20567.
Apply Serhiy Storchaka's update_idletasks solution to four test files.
Record this additional advice in idle_test/README.txt
@@ -47,9 +103,26 @@ What's New in IDLE 3.5.2?
- Issue #20567: Revise idle_test/README.txt with advice about avoiding
tk warning messages from tests. Apply advice to several IDLE tests.
+- Issue # 24225: Update idlelib/README.txt with new file names
+ and event handlers.
+
+- Issue #27156: Remove obsolete code not used by IDLE. Replacements:
+ 1. help.txt, replaced by help.html, is out-of-date and should not be used.
+ Its dedicated viewer has be replaced by the html viewer in help.py.
+ 2. 'import idlever; I = idlever.IDLE_VERSION' is the same as
+ 'import sys; I = version[:version.index(' ')]'
+ 3. After 'ob = stackviewer.VariablesTreeItem(*args)',
+ 'ob.keys()' == 'list(ob.object.keys).
+ 4. In macosc, runningAsOSXAPP == isAquaTk; idCarbonAquaTk == isCarbonTk
+
- Issue #27117: Make colorizer htest and turtledemo work with dark themes.
Move code for configuring text widget colors to a new function.
+- Issue #24225: Rename many idlelib/*.py and idle_test/test_*.py files.
+ Edit files to replace old names with new names when the old name
+ referred to the module rather than the class it contained.
+ See the issue and IDLE section in What's New in 3.6 for more.
+
- Issue #26673: When tk reports font size as 0, change to size 10.
Such fonts on Linux prevented the configuration dialog from opening.
@@ -62,8 +135,8 @@ What's New in IDLE 3.5.2?
- Issue #18410: Add test for IDLE's search dialog.
Original patch by Westley Martínez.
-- Issue #21703: Add test for undo delegator.
- Original patch by Saimadhav Heblikar .
+- Issue #21703: Add test for undo delegator. Patch mostly by
+ Saimadhav Heblikar .
- Issue #27044: Add ConfigDialog.remove_var_callbacks to stop memory leaks.
@@ -82,11 +155,6 @@ What's New in IDLE 3.5.2?
MARK in README.txt and open this and NEWS.txt with 'ascii'.
Re-encode CREDITS.txt to utf-8 and open it with 'utf-8'.
-
-What's New in IDLE 3.5.1?
-=========================
-*Release date: 2015-12-06*
-
- Issue 15348: Stop the debugger engine (normally in a user process)
before closing the debugger window (running in the IDLE process).
This prevents the RuntimeErrors that were being caught and ignored.
diff --git a/Lib/idlelib/README.txt b/Lib/idlelib/README.txt
index ff4450495a..51e8ef5888 100644
--- a/Lib/idlelib/README.txt
+++ b/Lib/idlelib/README.txt
@@ -29,61 +29,61 @@ idle.pyw
Implementation
--------------
-AutoComplete.py # Complete attribute names or filenames.
-AutoCompleteWindow.py # Display completions.
-AutoExpand.py # Expand word with previous word in file.
-Bindings.py # Define most of IDLE menu.
-CallTipWindow.py # Display calltip.
-CallTips.py # Create calltip text.
-ClassBrowser.py # Create module browser window.
-CodeContext.py # Show compound statement headers otherwise not visible.
-ColorDelegator.py # Colorize text (nim).
-Debugger.py # Debug code run from editor; show window.
-Delegator.py # Define base class for delegators (nim).
-EditorWindow.py # Define most of editor and utility functions.
-FileList.py # Open files and manage list of open windows (nim).
-FormatParagraph.py# Re-wrap multiline strings and comments.
-GrepDialog.py # Find all occurrences of pattern in multiple files.
-HyperParser.py # Parse code around a given index.
-IOBinding.py # Open, read, and write files
-IdleHistory.py # Get previous or next user input in shell (nim)
-MultiCall.py # Wrap tk widget to allow multiple calls per event (nim).
-MultiStatusBar.py # Define status bar for windows (nim).
-ObjectBrowser.py # Define class used in StackViewer (nim).
-OutputWindow.py # Create window for grep output.
-ParenMatch.py # Match fenceposts: (), [], and {}.
-PathBrowser.py # Create path browser window.
-Percolator.py # Manage delegator stack (nim).
-PyParse.py # Give information on code indentation
-PyShell.py # Start IDLE, manage shell, complete editor window
-RemoteDebugger.py # Debug code run in remote process.
-RemoteObjectBrowser.py # Communicate objects between processes with rpc (nim).
-ReplaceDialog.py # Search and replace pattern in text.
-RstripExtension.py# Strip trailing whitespace
-ScriptBinding.py # Check and run user code.
-ScrolledList.py # Define ScrolledList widget for IDLE (nim).
-SearchDialog.py # Search for pattern in text.
-SearchDialogBase.py # Define base for search, replace, and grep dialogs.
-SearchEngine.py # Define engine for all 3 search dialogs.
-StackViewer.py # View stack after exception.
-TreeWidget.py # Define tree widger, used in browsers (nim).
-UndoDelegator.py # Manage undo stack.
-WidgetRedirector.py # Intercept widget subcommands (for percolator) (nim).
-WindowList.py # Manage window list and define listed top level.
-ZoomHeight.py # Zoom window to full height of screen.
-aboutDialog.py # Display About IDLE dialog.
-configDialog.py # Display user configuration dialogs.
-configHandler.py # Load, fetch, and save configuration (nim).
-configHelpSourceEdit.py # Specify help source.
-configSectionNameDialog.py # Spefify user config section name
-dynOptionMenuWidget.py # define mutable OptionMenu widget (nim).
+autocomplete.py # Complete attribute names or filenames.
+autocomplete_w.py # Display completions.
+autoexpand.py # Expand word with previous word in file.
+browser.py # Create module browser window.
+calltip_w.py # Display calltip.
+calltips.py # Create calltip text.
+codecontext.py # Show compound statement headers otherwise not visible.
+colorizer.py # Colorize text (nim)
+config.py # Load, fetch, and save configuration (nim).
+configdialog.py # Display user configuration dialogs.
+config_help.py # Specify help source in configdialog.
+config_key.py # Change keybindings.
+dynoption.py # Define mutable OptionMenu widget (nim).
+debugobj.py # Define class used in stackviewer.
+debugobj_r.py # Communicate objects between processes with rpc (nim).
+debugger.py # Debug code run from shell or editor; show window.
+debugger_r.py # Debug code run in remote process.
+delegator.py # Define base class for delegators (nim).
+editor.py # Define most of editor and utility functions.
+filelist.py # Open files and manage list of open windows (nim).
+grep.py # Find all occurrences of pattern in multiple files.
help.py # Display IDLE's html doc.
-keybindingDialog.py # Change keybindings.
-macosxSupport.py # Help IDLE run on Macs (nim).
+help_about.py # Display About IDLE dialog.
+history.py # Get previous or next user input in shell (nim)
+hyperparser.py # Parse code around a given index.
+iomenu.py # Open, read, and write files
+macosx.py # Help IDLE run on Macs (nim).
+mainmenu.py # Define most of IDLE menu.
+multicall.py # Wrap tk widget to allow multiple calls per event (nim).
+outwin.py # Create window for grep output.
+paragraph.py # Re-wrap multiline strings and comments.
+parenmatch.py # Match fenceposts: (), [], and {}.
+pathbrowser.py # Create path browser window.
+percolator.py # Manage delegator stack (nim).
+pyparse.py # Give information on code indentation
+pyshell.py # Start IDLE, manage shell, complete editor window
+query.py # Query user for information
+redirector.py # Intercept widget subcommands (for percolator) (nim).
+replace.py # Search and replace pattern in text.
rpc.py # Commuicate between idle and user processes (nim).
+rstrip.py # Strip trailing whitespace.
run.py # Manage user code execution subprocess.
+runscript.py # Check and run user code.
+scrolledlist.py # Define scrolledlist widget for IDLE (nim).
+search.py # Search for pattern in text.
+searchbase.py # Define base for search, replace, and grep dialogs.
+searchengine.py # Define engine for all 3 search dialogs.
+stackviewer.py # View stack after exception.
+statusbar.py # Define status bar for windows (nim).
tabbedpages.py # Define tabbed pages widget (nim).
-textView.py # Define read-only text widget (nim).
+textview.py # Define read-only text widget (nim).
+tree.py # Define tree widger, used in browsers (nim).
+undo.py # Manage undo stack.
+windows.py # Manage window list and define listed top level.
+zoomheight.py # Zoom window to full height of screen.
Configuration
-------------
@@ -104,127 +104,148 @@ help.html # copy of idle.html in docs, displayed by IDLE Help
Subdirectories
--------------
-Icons # small image files
-idle_test # files for human test and automated unit tests
+Icons # small image files
+idle_test # files for human test and automated unit tests
Unused and Deprecated files and objects (nim)
---------------------------------------------
-EditorWindow.py: Helpdialog and helpDialog
-ToolTip.py: unused.
-help.txt
-idlever.py
+tooltip.py # unused
+
IDLE MENUS
-Top level items and most submenu items are defined in Bindings.
+Top level items and most submenu items are defined in mainmenu.
Extenstions add submenu items when active. The names given are
found, quoted, in one of these modules, paired with a '<<pseudoevent>>'.
Each pseudoevent is bound to an event handler. Some event handlers
call another function that does the actual work. The annotations below
are intended to at least give the module where the actual work is done.
+'eEW' = editor.EditorWindow
-File # IOBindig except as noted
- New File
- Open... # IOBinding.open
- Open Module
+File
+ New File # eEW.new_callback
+ Open... # iomenu.open
+ Open Module # eEw.open_module
Recent Files
- Class Browser # Class Browser
- Path Browser # Path Browser
+ Class Browser # eEW.open_class_browser, browser.ClassBrowser
+ Path Browser # eEW.open_path_browser, pathbrowser
---
- Save # IDBinding.save
- Save As... # IOBinding.save_as
- Save Copy As... # IOBindling.save_a_copy
+ Save # iomenu.save
+ Save As... # iomenu.save_as
+ Save Copy As... # iomenu.save_a_copy
---
- Print Window # IOBinding.print_window
+ Print Window # iomenu.print_window
---
- Close
- Exit
+ Close # eEW.close_event
+ Exit # flist.close_all_callback (bound in eEW)
Edit
- Undo # undoDelegator
- Redo # undoDelegator
- ---
- Cut
- Copy
- Paste
- Select All
- --- # Next 5 items use SearchEngine; dialogs use SearchDialogBase
- Find # Search Dialog
- Find Again
- Find Selection
- Find in Files... # GrepDialog
- Replace... # ReplaceDialog
- Go to Line
- Show Completions # AutoComplete extension and AutoCompleteWidow (&HP)
- Expand Word # AutoExpand extension
- Show call tip # Calltips extension and CalltipWindow (& Hyperparser)
- Show surrounding parens # ParenMatch (& Hyperparser)
-
-Shell # PyShell
- View Last Restart # PyShell.PyShell.view_restart_mark
- Restart Shell # PyShell.PyShell.restart_shell
+ Undo # undodelegator
+ Redo # undodelegator
+ --- # eEW.right_menu_event
+ Cut # eEW.cut
+ Copy # eEW.copy
+ Paste # eEW.past
+ Select All # eEW.select_all (+ see eEW.remove_selection)
+ --- # Next 5 items use searchengine; dialogs use searchbase
+ Find # eEW.find_event, search.SearchDialog.find
+ Find Again # eEW.find_again_event, sSD.find_again
+ Find Selection # eEW.find_selection_event, sSD.find_selection
+ Find in Files... # eEW.find_in_files_event, grep
+ Replace... # eEW.replace_event, replace.ReplaceDialog.replace
+ Go to Line # eEW.goto_line_event
+ Show Completions # autocomplete extension and autocompleteWidow (&HP)
+ Expand Word # autoexpand extension
+ Show call tip # Calltips extension and CalltipWindow (& Hyperparser)
+ Show surrounding parens # parenmatch (& Hyperparser)
+
+Shell # pyshell
+ View Last Restart # pyshell.PyShell.view_restart_mark
+ Restart Shell # pyshell.PyShell.restart_shell
Interrupt Execution # pyshell.PyShell.cancel_callback
Debug (Shell only)
Go to File/Line
- Debugger # Debugger, RemoteDebugger, PyShell.toggle_debuger
- Stack Viewer # StackViewer, PyShell.open_stack_viewer
- Auto-open Stack Viewer # StackViewer
+ debugger # debugger, debugger_r, PyShell.toggle_debuger
+ Stack Viewer # stackviewer, PyShell.open_stack_viewer
+ Auto-open Stack Viewer # stackviewer
Format (Editor only)
- Indent Region
- Dedent Region
- Comment Out Region
- Uncomment Region
- Tabify Region
- Untabify Region
- Toggle Tabs
- New Indent Width
- Format Paragraph # FormatParagraph extension
+ Indent Region # eEW.indent_region_event
+ Dedent Region # eEW.dedent_region_event
+ Comment Out Reg. # eEW.comment_region_event
+ Uncomment Region # eEW.uncomment_region_event
+ Tabify Region # eEW.tabify_region_event
+ Untabify Region # eEW.untabify_region_event
+ Toggle Tabs # eEW.toggle_tabs_event
+ New Indent Width # eEW.change_indentwidth_event
+ Format Paragraph # paragraph extension
---
- Strip tailing whitespace # RstripExtension extension
+ Strip tailing whitespace # rstrip extension
Run (Editor only)
- Python Shell # PyShell
+ Python Shell # pyshell
---
- Check Module # ScriptBinding
- Run Module # ScriptBinding
+ Check Module # runscript
+ Run Module # runscript
Options
- Configure IDLE # configDialog
+ Configure IDLE # eEW.config_dialog, configdialog
(tabs in the dialog)
- Font tab # onfig-main.def
- Highlight tab # configSectionNameDialog, config-highlight.def
- Keys tab # keybindingDialog, configSectionNameDialog, onfig-keus.def
- General tab # configHelpSourceEdit, config-main.def
- Configure Extensions # configDialog
- Xyz tab # xyz.py, config-extensions.def
+ Font tab # config-main.def
+ Highlight tab # query, config-highlight.def
+ Keys tab # query, config_key, config_keys.def
+ General tab # config_help, config-main.def
+ Extensions tab # config-extensions.def, corresponding .py
---
- Code Context (editor only) # CodeContext extension
+ Code Context (ed)# codecontext extension
Window
- Zoomheight # ZoomHeight extension
+ Zoomheight # zoomheight extension
---
- <open windows> # WindowList
+ <open windows> # windows
Help
- About IDLE # aboutDialog
+ About IDLE # eEW.about_dialog, help_about.AboutDialog
---
- IDLE Help # help
- Python Doc
- Turtle Demo
+ IDLE Help # eEW.help_dialog, helpshow_idlehelp
+ Python Doc # eEW.python_docs
+ Turtle Demo # eEW.open_turtle_demo
---
<other help sources>
<Context Menu> (right click)
-Defined in EditorWindow, PyShell, Output
- Cut
- Copy
- Paste
- ---
- Go to file/line (shell and output only)
- Set Breakpoint (editor only)
- Clear Breakpoint (editor only)
- Defined in Debugger
- Go to source line
- Show stack frame
+ Defined in editor, PyShelpyshellut
+ Cut
+ Copy
+ Paste
+ ---
+ Go to file/line (shell and output only)
+ Set Breakpoint (editor only)
+ Clear Breakpoint (editor only)
+ Defined in debugger
+ Go to source line
+ Show stack frame
+
+<No menu>
+Center Insert # eEW.center_insert_event
+
+
+CODE STYLE -- Generally PEP 8.
+
+import
+------
+Put import at the top, unless there is a good reason otherwise.
+PEP 8 says to group stdlib, 3rd-party dependencies, and package imports.
+For idlelib, the groups are general stdlib, tkinter, and idlelib.
+Sort modules within each group, except that tkinter.ttk follows tkinter.
+Sort 'from idlelib import mod1' and 'from idlelib.mod2 import object'
+together by module, ignoring within module objects.
+Put 'import __main__' after other idlelib imports.
+
+Imports only needed for testing are put not at the top but in an
+htest function def or "if __name__ == '__main__'" clause.
+
+Within module imports like "from idlelib.mod import class" may cause
+circular imports to deadlock. Even without this, circular imports may
+require at least one of the imports to be delayed until a function call.
diff --git a/Lib/idlelib/__init__.py b/Lib/idlelib/__init__.py
index 711f61bb69..791ddeab79 100644
--- a/Lib/idlelib/__init__.py
+++ b/Lib/idlelib/__init__.py
@@ -1,8 +1,10 @@
"""The idlelib package implements the Idle application.
Idle includes an interactive shell and editor.
+Starting with Python 3.6, IDLE requires tcl/tk 8.5 or later.
Use the files named idle.* to start Idle.
The other files are private implementations. Their details are subject to
change. See PEP 434 for more. Import them at your own risk.
"""
+testing = False # Set True by test.test_idle.
diff --git a/Lib/idlelib/__main__.py b/Lib/idlelib/__main__.py
index 2edf5f7dc1..6349ec75c6 100644
--- a/Lib/idlelib/__main__.py
+++ b/Lib/idlelib/__main__.py
@@ -3,6 +3,6 @@ IDLE main entry point
Run IDLE as python -m idlelib
"""
-import idlelib.PyShell
-idlelib.PyShell.main()
+import idlelib.pyshell
+idlelib.pyshell.main()
# This file does not work for 2.7; See issue 24212.
diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/autocomplete.py
index ff085d5c70..1e44fa5bc6 100644
--- a/Lib/idlelib/AutoComplete.py
+++ b/Lib/idlelib/autocomplete.py
@@ -1,29 +1,30 @@
-"""AutoComplete.py - An IDLE extension for automatically completing names.
+"""autocomplete.py - An IDLE extension for automatically completing names.
This extension can complete either attribute names or file names. It can pop
a window with all available names, for the user to select from.
"""
import os
-import sys
import string
+import sys
-from idlelib.configHandler import idleConf
-
-# This string includes all chars that may be in an identifier
-ID_CHARS = string.ascii_letters + string.digits + "_"
-
-# These constants represent the two different types of completions
+# These constants represent the two different types of completions.
+# They must be defined here so autocomple_w can import them.
COMPLETE_ATTRIBUTES, COMPLETE_FILES = range(1, 2+1)
-from idlelib import AutoCompleteWindow
-from idlelib.HyperParser import HyperParser
-
+from idlelib import autocomplete_w
+from idlelib.config import idleConf
+from idlelib.hyperparser import HyperParser
import __main__
+# This string includes all chars that may be in an identifier.
+# TODO Update this here and elsewhere.
+ID_CHARS = string.ascii_letters + string.digits + "_"
+
SEPS = os.sep
if os.altsep: # e.g. '/' on Windows...
SEPS += os.altsep
+
class AutoComplete:
menudefs = [
@@ -37,19 +38,17 @@ class AutoComplete:
def __init__(self, editwin=None):
self.editwin = editwin
- if editwin is None: # subprocess and test
- return
- self.text = editwin.text
- self.autocompletewindow = None
-
- # id of delayed call, and the index of the text insert when the delayed
- # call was issued. If _delayed_completion_id is None, there is no
- # delayed call.
- self._delayed_completion_id = None
- self._delayed_completion_index = None
+ if editwin is not None: # not in subprocess or test
+ self.text = editwin.text
+ self.autocompletewindow = None
+ # id of delayed call, and the index of the text insert when
+ # the delayed call was issued. If _delayed_completion_id is
+ # None, there is no delayed call.
+ self._delayed_completion_id = None
+ self._delayed_completion_index = None
def _make_autocomplete_window(self):
- return AutoCompleteWindow.AutoCompleteWindow(self.text)
+ return autocomplete_w.AutoCompleteWindow(self.text)
def _remove_autocomplete_window(self, event=None):
if self.autocompletewindow:
@@ -80,16 +79,17 @@ class AutoComplete:
open a completion list after that (if there is more than one
completion)
"""
- if hasattr(event, "mc_state") and event.mc_state:
- # A modifier was pressed along with the tab, continue as usual.
- return
+ if hasattr(event, "mc_state") and event.mc_state or\
+ not self.text.get("insert linestart", "insert").strip():
+ # A modifier was pressed along with the tab or
+ # there is only previous whitespace on this line, so tab.
+ return None
if self.autocompletewindow and self.autocompletewindow.is_active():
self.autocompletewindow.complete()
return "break"
else:
opened = self.open_completions(False, True, True)
- if opened:
- return "break"
+ return "break" if opened else None
def _open_completions_later(self, *args):
self._delayed_completion_index = self.text.index("insert")
@@ -101,9 +101,8 @@ class AutoComplete:
def _delayed_open_completions(self, *args):
self._delayed_completion_id = None
- if self.text.index("insert") != self._delayed_completion_index:
- return
- self.open_completions(*args)
+ if self.text.index("insert") == self._delayed_completion_index:
+ self.open_completions(*args)
def open_completions(self, evalfuncs, complete, userWantsWin, mode=None):
"""Find the completions and create the AutoCompleteWindow.
@@ -148,17 +147,17 @@ class AutoComplete:
comp_what = hp.get_expression()
if not comp_what or \
(not evalfuncs and comp_what.find('(') != -1):
- return
+ return None
else:
comp_what = ""
else:
- return
+ return None
if complete and not comp_what and not comp_start:
- return
+ return None
comp_lists = self.fetch_completions(comp_what, mode)
if not comp_lists[0]:
- return
+ return None
self.autocompletewindow = self._make_autocomplete_window()
return not self.autocompletewindow.show_window(
comp_lists, "insert-%dc" % len(comp_start),
diff --git a/Lib/idlelib/AutoCompleteWindow.py b/Lib/idlelib/autocomplete_w.py
index 2ee6878396..3374c6e945 100644
--- a/Lib/idlelib/AutoCompleteWindow.py
+++ b/Lib/idlelib/autocomplete_w.py
@@ -1,9 +1,11 @@
"""
-An auto-completion window for IDLE, used by the AutoComplete extension
+An auto-completion window for IDLE, used by the autocomplete extension
"""
from tkinter import *
-from idlelib.MultiCall import MC_SHIFT
-from idlelib.AutoComplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES
+from tkinter.ttk import Scrollbar
+
+from idlelib.autocomplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES
+from idlelib.multicall import MC_SHIFT
HIDE_VIRTUAL_EVENT_NAME = "<<autocompletewindow-hide>>"
HIDE_SEQUENCES = ("<FocusOut>", "<ButtonPress>")
@@ -34,8 +36,8 @@ class AutoCompleteWindow:
self.completions = None
# A list with more completions, or None
self.morecompletions = None
- # The completion mode. Either AutoComplete.COMPLETE_ATTRIBUTES or
- # AutoComplete.COMPLETE_FILES
+ # The completion mode. Either autocomplete.COMPLETE_ATTRIBUTES or
+ # autocomplete.COMPLETE_FILES
self.mode = None
# The current completion start, on the text box (a string)
self.start = None
@@ -215,6 +217,7 @@ class AutoCompleteWindow:
self.winconfigid = acw.bind(WINCONFIG_SEQUENCE, self.winconfig_event)
self.doubleclickid = listbox.bind(DOUBLECLICK_SEQUENCE,
self.doubleclick_event)
+ return None
def winconfig_event(self, event):
if not self.is_active():
@@ -238,16 +241,14 @@ class AutoCompleteWindow:
acw.wm_geometry("+%d+%d" % (new_x, new_y))
def hide_event(self, event):
- if not self.is_active():
- return
- self.hide_window()
+ if self.is_active():
+ self.hide_window()
def listselect_event(self, event):
- if not self.is_active():
- return
- self.userwantswindow = True
- cursel = int(self.listbox.curselection()[0])
- self._change_start(self.completions[cursel])
+ if self.is_active():
+ self.userwantswindow = True
+ cursel = int(self.listbox.curselection()[0])
+ self._change_start(self.completions[cursel])
def doubleclick_event(self, event):
# Put the selected completion in the text, and close the list
@@ -257,7 +258,7 @@ class AutoCompleteWindow:
def keypress_event(self, event):
if not self.is_active():
- return
+ return None
keysym = event.keysym
if hasattr(event, "mc_state"):
state = event.mc_state
@@ -282,7 +283,7 @@ class AutoCompleteWindow:
# keysym == "BackSpace"
if len(self.start) == 0:
self.hide_window()
- return
+ return None
self._change_start(self.start[:-1])
self.lasttypedstart = self.start
self.listbox.select_clear(0, int(self.listbox.curselection()[0]))
@@ -292,7 +293,7 @@ class AutoCompleteWindow:
elif keysym == "Return":
self.hide_window()
- return
+ return None
elif (self.mode == COMPLETE_ATTRIBUTES and keysym in
("period", "space", "parenleft", "parenright", "bracketleft",
@@ -308,7 +309,7 @@ class AutoCompleteWindow:
and (self.mode == COMPLETE_ATTRIBUTES or self.start):
self._change_start(self.completions[cursel])
self.hide_window()
- return
+ return None
elif keysym in ("Home", "End", "Prior", "Next", "Up", "Down") and \
not state:
@@ -349,12 +350,12 @@ class AutoCompleteWindow:
# first tab; let AutoComplete handle the completion
self.userwantswindow = True
self.lastkey_was_tab = True
- return
+ return None
elif any(s in keysym for s in ("Shift", "Control", "Alt",
"Meta", "Command", "Option")):
# A modifier key, so ignore
- return
+ return None
elif event.char and event.char >= ' ':
# Regular character with a non-length-1 keycode
@@ -368,7 +369,7 @@ class AutoCompleteWindow:
else:
# Unknown event, close the window and let it through.
self.hide_window()
- return
+ return None
def keyrelease_event(self, event):
if not self.is_active():
diff --git a/Lib/idlelib/AutoExpand.py b/Lib/idlelib/autoexpand.py
index 7059054281..6b46bee69c 100644
--- a/Lib/idlelib/AutoExpand.py
+++ b/Lib/idlelib/autoexpand.py
@@ -12,8 +12,8 @@ its state.
This is an extension file and there is only one instance of AutoExpand.
'''
-import string
import re
+import string
###$ event <<expand-word>>
###$ win <Alt-slash>
@@ -31,6 +31,7 @@ class AutoExpand:
def __init__(self, editwin):
self.text = editwin.text
+ self.bell = self.text.bell
self.state = None
def expand_word_event(self, event):
@@ -46,14 +47,14 @@ class AutoExpand:
words = self.getwords()
index = 0
if not words:
- self.text.bell()
+ self.bell()
return "break"
word = self.getprevword()
self.text.delete("insert - %d chars" % len(word), "insert")
newword = words[index]
index = (index + 1) % len(words)
if index == 0:
- self.text.bell() # Warn we cycled around
+ self.bell() # Warn we cycled around
self.text.insert("insert", newword)
curinsert = self.text.index("insert")
curline = self.text.get("insert linestart", "insert lineend")
diff --git a/Lib/idlelib/ClassBrowser.py b/Lib/idlelib/browser.py
index d09c52fe4d..ea05638df1 100644
--- a/Lib/idlelib/ClassBrowser.py
+++ b/Lib/idlelib/browser.py
@@ -11,16 +11,16 @@ XXX TO DO:
"""
import os
-import sys
import pyclbr
+import sys
-from idlelib import PyShell
-from idlelib.WindowList import ListedToplevel
-from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas
-from idlelib.configHandler import idleConf
+from idlelib.config import idleConf
+from idlelib import pyshell
+from idlelib.tree import TreeNode, TreeItem, ScrolledCanvas
+from idlelib.windows import ListedToplevel
file_open = None # Method...Item and Class...Item use this.
-# Normally PyShell.flist.open, but there is no PyShell.flist for htest.
+# Normally pyshell.flist.open, but there is no pyshell.flist for htest.
class ClassBrowser:
@@ -32,7 +32,7 @@ class ClassBrowser:
"""
global file_open
if not _htest:
- file_open = PyShell.flist.open
+ file_open = pyshell.flist.open
self.name = name
self.file = os.path.join(path[0], self.name + ".py")
self._htest = _htest
@@ -95,7 +95,7 @@ class ModuleBrowserTreeItem(TreeItem):
return
if not os.path.exists(self.file):
return
- PyShell.flist.open(self.file)
+ pyshell.flist.open(self.file)
def IsExpandable(self):
return os.path.normcase(self.file[-3:]) == ".py"
@@ -226,7 +226,7 @@ def _class_browser(parent): #Wrapper for htest
file = sys.argv[0]
dir, file = os.path.split(file)
name = os.path.splitext(file)[0]
- flist = PyShell.PyShellFileList(parent)
+ flist = pyshell.PyShellFileList(parent)
global file_open
file_open = flist.open
ClassBrowser(flist, name, [dir], _htest=True)
diff --git a/Lib/idlelib/CallTipWindow.py b/Lib/idlelib/calltip_w.py
index 9eec17506d..c7361d18d6 100644
--- a/Lib/idlelib/CallTipWindow.py
+++ b/Lib/idlelib/calltip_w.py
@@ -1,7 +1,7 @@
"""A CallTip window class for Tkinter/IDLE.
-After ToolTip.py, which uses ideas gleaned from PySol
-Used by the CallTips IDLE extension.
+After tooltip.py, which uses ideas gleaned from PySol
+Used by the calltips IDLE extension.
"""
from tkinter import Toplevel, Label, LEFT, SOLID, TclError
@@ -138,8 +138,8 @@ def _calltip_window(parent): # htest #
top = Toplevel(parent)
top.title("Test calltips")
- top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200,
- parent.winfo_rooty() + 150))
+ x, y = map(int, parent.geometry().split('+')[1:])
+ top.geometry("200x100+%d+%d" % (x + 250, y + 175))
text = Text(top)
text.pack(side=LEFT, fill=BOTH, expand=1)
text.insert("insert", "string.split")
diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/calltips.py
index 81bd5f1890..4c5aea2acb 100644
--- a/Lib/idlelib/CallTips.py
+++ b/Lib/idlelib/calltips.py
@@ -1,19 +1,19 @@
-"""CallTips.py - An IDLE Extension to Jog Your Memory
+"""calltips.py - An IDLE Extension to Jog Your Memory
Call Tips are floating windows which display function, class, and method
parameter and docstring information when you type an opening parenthesis, and
which disappear when you type a closing parenthesis.
"""
-import __main__
import inspect
import re
import sys
import textwrap
import types
-from idlelib import CallTipWindow
-from idlelib.HyperParser import HyperParser
+from idlelib import calltip_w
+from idlelib.hyperparser import HyperParser
+import __main__
class CallTips:
@@ -37,7 +37,7 @@ class CallTips:
def _make_tk_calltip_window(self):
# See __init__ for usage
- return CallTipWindow.CallTip(self.text)
+ return calltip_w.CallTip(self.text)
def _remove_calltip_window(self, event=None):
if self.active_calltip:
@@ -120,7 +120,7 @@ def get_entity(expression):
_MAX_COLS = 85
_MAX_LINES = 5 # enough for bytes
_INDENT = ' '*4 # for wrapped signatures
-_first_param = re.compile('(?<=\()\w*\,?\s*')
+_first_param = re.compile(r'(?<=\()\w*\,?\s*')
_default_callable_argspec = "See source or doc"
diff --git a/Lib/idlelib/CodeContext.py b/Lib/idlelib/codecontext.py
index 7d25adaa4c..f25e1b33a0 100644
--- a/Lib/idlelib/CodeContext.py
+++ b/Lib/idlelib/codecontext.py
@@ -1,19 +1,21 @@
-"""CodeContext - Extension to display the block context above the edit window
+"""codecontext - Extension to display the block context above the edit window
Once code has scrolled off the top of a window, it can be difficult to
determine which block you are in. This extension implements a pane at the top
of each IDLE edit window which provides block structure hints. These hints are
the lines which contain the block opening keywords, e.g. 'if', for the
enclosing block. The number of hint lines is determined by the numlines
-variable in the CodeContext section of config-extensions.def. Lines which do
+variable in the codecontext section of config-extensions.def. Lines which do
not open blocks are not shown in the context hints pane.
"""
-import tkinter
-from tkinter.constants import TOP, LEFT, X, W, SUNKEN
import re
from sys import maxsize as INFINITY
-from idlelib.configHandler import idleConf
+
+import tkinter
+from tkinter.constants import TOP, LEFT, X, W, SUNKEN
+
+from idlelib.config import idleConf
BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for",
"if", "try", "while", "with"}
diff --git a/Lib/idlelib/ColorDelegator.py b/Lib/idlelib/colorizer.py
index 02eac47068..ff40845288 100644
--- a/Lib/idlelib/ColorDelegator.py
+++ b/Lib/idlelib/colorizer.py
@@ -1,10 +1,10 @@
-import time
-import re
-import keyword
import builtins
-from tkinter import TkVersion
-from idlelib.Delegator import Delegator
-from idlelib.configHandler import idleConf
+import keyword
+import re
+import time
+
+from idlelib.config import idleConf
+from idlelib.delegator import Delegator
DEBUG = False
@@ -21,7 +21,7 @@ def make_pat():
# 1st 'file' colorized normal, 2nd as builtin, 3rd as string
builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b"
comment = any("COMMENT", [r"#[^\n]*"])
- stringprefix = r"(\br|u|ur|R|U|UR|Ur|uR|b|B|br|Br|bR|BR|rb|rB|Rb|RB)?"
+ stringprefix = r"(?i:\br|u|f|fr|rf|b|br|rb)?"
sqstring = stringprefix + r"'[^'\\\n]*(\\.[^'\\\n]*)*'?"
dqstring = stringprefix + r'"[^"\\\n]*(\\.[^"\\\n]*)*"?'
sq3string = stringprefix + r"'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
@@ -49,11 +49,8 @@ def color_config(text): # Called from htest, Editor, and Turtle Demo.
insertbackground=cursor_color,
selectforeground=select_colors['foreground'],
selectbackground=select_colors['background'],
- )
- if TkVersion >= 8.5:
- text.config(
- inactiveselectbackground=select_colors['background'])
-
+ inactiveselectbackground=select_colors['background'], # new in 8.5
+ )
class ColorDelegator(Delegator):
@@ -259,13 +256,20 @@ class ColorDelegator(Delegator):
def _color_delegator(parent): # htest #
from tkinter import Toplevel, Text
- from idlelib.Percolator import Percolator
+ from idlelib.percolator import Percolator
top = Toplevel(parent)
top.title("Test ColorDelegator")
- top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200,
- parent.winfo_rooty() + 150))
- source = "if somename: x = 'abc' # comment\nprint\n"
+ x, y = map(int, parent.geometry().split('+')[1:])
+ top.geometry("700x250+%d+%d" % (x + 20, y + 175))
+ source = ("# Following has syntax errors\n"
+ "if True: then int 1\nelif False: print 0\nelse: float(None)\n"
+ "if iF + If + IF: 'keywork matching must respect case'\n"
+ "# All valid prefixes for unicode and byte strings should be colored\n"
+ "'x', '''x''', \"x\", \"\"\"x\"\"\"\n"
+ "r'x', u'x', R'x', U'x', f'x', F'x', ur'is invalid'\n"
+ "fr'x', Fr'x', fR'x', FR'x', rf'x', rF'x', Rf'x', RF'x'\n"
+ "b'x',B'x', br'x',Br'x',bR'x',BR'x', rb'x'.rB'x',Rb'x',RB'x'\n")
text = Text(top, background="white")
text.pack(expand=1, fill="both")
text.insert("insert", source)
@@ -277,5 +281,9 @@ def _color_delegator(parent): # htest #
p.insertfilter(d)
if __name__ == "__main__":
+ import unittest
+ unittest.main('idlelib.idle_test.test_colorizer',
+ verbosity=2, exit=False)
+
from idlelib.idle_test.htest import run
run(_color_delegator)
diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def
index 3bfcb69015..64788f9adf 100644
--- a/Lib/idlelib/config-keys.def
+++ b/Lib/idlelib/config-keys.def
@@ -109,6 +109,57 @@ change-indentwidth=<Alt-Key-u>
del-word-left=<Alt-Key-BackSpace>
del-word-right=<Alt-Key-d>
+[IDLE Modern Unix]
+copy = <Control-Shift-Key-C> <Control-Key-Insert>
+cut = <Control-Key-x> <Shift-Key-Delete>
+paste = <Control-Key-v> <Shift-Key-Insert>
+beginning-of-line = <Key-Home>
+center-insert = <Control-Key-l>
+close-all-windows = <Control-Key-q>
+close-window = <Control-Key-w> <Control-Shift-Key-W>
+do-nothing = <Control-Key-F12>
+end-of-file = <Control-Key-d>
+history-next = <Alt-Key-n> <Meta-Key-n>
+history-previous = <Alt-Key-p> <Meta-Key-p>
+interrupt-execution = <Control-Key-c>
+view-restart = <Key-F6>
+restart-shell = <Control-Key-F6>
+open-class-browser = <Control-Key-b>
+open-module = <Control-Key-m>
+open-new-window = <Control-Key-n>
+open-window-from-file = <Control-Key-o>
+plain-newline-and-indent = <Control-Key-j>
+print-window = <Control-Key-p>
+python-context-help = <Shift-Key-F1>
+python-docs = <Key-F1>
+redo = <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>
+toggle-auto-coloring = <Control-Key-slash>
+undo = <Control-Key-z>
+find = <Control-Key-f>
+find-again = <Key-F3>
+find-in-files = <Control-Shift-Key-f>
+find-selection = <Control-Key-h>
+replace = <Control-Key-r>
+goto-line = <Control-Key-g>
+smart-backspace = <Key-BackSpace>
+newline-and-indent = <Key-Return> <Key-KP_Enter>
+smart-indent = <Key-Tab>
+indent-region = <Control-Key-bracketright>
+dedent-region = <Control-Key-bracketleft>
+comment-region = <Control-Key-d>
+uncomment-region = <Control-Shift-Key-D>
+tabify-region = <Alt-Key-5>
+untabify-region = <Alt-Key-6>
+toggle-tabs = <Control-Key-T>
+change-indentwidth = <Alt-Key-u>
+del-word-left = <Control-Key-BackSpace>
+del-word-right = <Control-Key-Delete>
+
[IDLE Classic Mac]
copy=<Command-Key-c>
cut=<Command-Key-x>
diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def
index 8ebbc1b4c2..16f4b0959c 100644
--- a/Lib/idlelib/config-main.def
+++ b/Lib/idlelib/config-main.def
@@ -4,44 +4,50 @@
# When IDLE starts, it will look in
# the following two sets of files, in order:
#
-# default configuration
-# ---------------------
-# config-main.def the default general config file
-# config-extensions.def the default extension config file
-# config-highlight.def the default highlighting config file
-# config-keys.def the default keybinding config file
+# default configuration files in idlelib
+# --------------------------------------
+# config-main.def default general config file
+# config-extensions.def default extension config file
+# config-highlight.def default highlighting config file
+# config-keys.def default keybinding config file
#
-# user configuration
-# -------------------
-# ~/.idlerc/config-main.cfg the user general config file
-# ~/.idlerc/config-extensions.cfg the user extension config file
-# ~/.idlerc/config-highlight.cfg the user highlighting config file
-# ~/.idlerc/config-keys.cfg the user keybinding config file
+# user configuration files in ~/.idlerc
+# -------------------------------------
+# config-main.cfg user general config file
+# config-extensions.cfg user extension config file
+# config-highlight.cfg user highlighting config file
+# config-keys.cfg user keybinding config file
#
-# On Windows2000 and Windows XP the .idlerc directory is at
-# Documents and Settings\<username>\.idlerc
-#
-# On Windows98 it is at c:\.idlerc
+# On Windows, the default location of the home directory ('~' above)
+# depends on the version. For Windows 10, it is C:\Users\<username>.
#
# Any options the user saves through the config dialog will be saved to
-# the relevant user config file. Reverting any general setting to the
-# default causes that entry to be wiped from the user file and re-read
-# from the default file. User highlighting themes or keybinding sets are
-# retained unless specifically deleted within the config dialog. Choosing
-# one of the default themes or keysets just applies the relevant settings
-# from the default file.
+# the relevant user config file. Reverting any general or extension
+# setting to the default causes that entry to be wiped from the user
+# file and re-read from the default file. This rule applies to each
+# item, except that the three editor font items are saved as a group.
+#
+# User highlighting themes and keybinding sets must have (section) names
+# distinct from the default names. All items are added and saved as a
+# group. They are retained unless specifically deleted within the config
+# dialog. Choosing one of the default themes or keysets just applies the
+# relevant settings from the default file.
+#
+# Additional help sources are listed in the [HelpFiles] section below
+# and should be viewable by a web browser (or the Windows Help viewer in
+# the case of .chm files). These sources will be listed on the Help
+# menu. The pattern, and two examples, are
#
-# Additional help sources are listed in the [HelpFiles] section and must be
-# viewable by a web browser (or the Windows Help viewer in the case of .chm
-# files). These sources will be listed on the Help menu. The pattern is
# <sequence_number = menu item;/path/to/help/source>
-# You can't use a semi-colon in a menu item or path. The path will be platform
-# specific because of path separators, drive specs etc.
+# 1 = IDLE;C:/Programs/Python36/Lib/idlelib/help.html
+# 2 = Pillow;https://pillow.readthedocs.io/en/latest/
#
-# It is best to use the Configuration GUI to set up additional help sources!
-# Example:
-#1 = My Extra Help Source;/usr/share/doc/foo/index.html
-#2 = Another Help Source;/path/to/another.pdf
+# You can't use a semi-colon in a menu item or path. The path will be
+# platform specific because of path separators, drive specs etc.
+#
+# The default files should not be edited except to add new sections to
+# config-extensions.def for added extensions . The user files should be
+# modified through the Settings dialog.
[General]
editor-on-startup= 0
@@ -54,6 +60,8 @@ delete-exitfunc= 1
width= 80
height= 40
font= TkFixedFont
+# For TkFixedFont, the actual size and boldness are obtained from tk
+# and override 10 and 0. See idlelib.config.IdleConf.GetFont
font-size= 10
font-bold= 0
encoding= none
@@ -70,7 +78,9 @@ name2=
[Keys]
default= 1
-name= IDLE Classic Windows
+name=
+name2=
+# name2 set in user config-main.cfg for keys added after 2016 July 1
[History]
cyclic=1
diff --git a/Lib/idlelib/configHandler.py b/Lib/idlelib/config.py
index 8954488fd6..358bee4803 100644
--- a/Lib/idlelib/configHandler.py
+++ b/Lib/idlelib/config.py
@@ -1,13 +1,20 @@
-"""Provides access to stored IDLE configuration information.
-
-Refer to the comments at the beginning of config-main.def for a description of
-the available configuration files and the design implemented to update user
-configuration information. In particular, user configuration choices which
-duplicate the defaults will be removed from the user's configuration files,
-and if a file becomes empty, it will be deleted.
-
-The contents of the user files may be altered using the Options/Configure IDLE
-menu to access the configuration GUI (configDialog.py), or manually.
+"""idlelib.config -- Manage IDLE configuration information.
+
+The comments at the beginning of config-main.def describe the
+configuration files and the design implemented to update user
+configuration information. In particular, user configuration choices
+which duplicate the defaults will be removed from the user's
+configuration files, and if a user file becomes empty, it will be
+deleted.
+
+The configuration database maps options to values. Comceptually, the
+database keys are tuples (config-type, section, item). As implemented,
+there are separate dicts for default and user values. Each has
+config-type keys 'main', 'extensions', 'highlight', and 'keys'. The
+value for each key is a ConfigParser instance that maps section and item
+to values. For 'main' and 'extenstons', user values override
+default values. For 'highlight' and 'keys', user sections augment the
+default sections (and must, therefore, have distinct names).
Throughout this module there is an emphasis on returning useable defaults
when a problem occurs in returning a requested configuration value back to
@@ -18,11 +25,10 @@ configuration problem notification and resolution.
"""
# TODOs added Oct 2014, tjr
+from configparser import ConfigParser
import os
import sys
-from configparser import ConfigParser
-from tkinter import TkVersion
from tkinter.font import Font, nametofont
class InvalidConfigType(Exception): pass
@@ -230,15 +236,12 @@ class IdleConf:
return self.userCfg[configType].Get(section, option,
type=type, raw=raw)
except ValueError:
- warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
+ warning = ('\n Warning: config.py - IdleConf.GetOption -\n'
' invalid %r value for configuration option %r\n'
' from section %r: %r' %
(type, option, section,
self.userCfg[configType].Get(section, option, raw=raw)))
- try:
- print(warning, file=sys.stderr)
- except OSError:
- pass
+ _warn(warning, configType, section, option)
try:
if self.defaultCfg[configType].has_option(section,option):
return self.defaultCfg[configType].Get(
@@ -247,15 +250,12 @@ class IdleConf:
pass
#returning default, print warning
if warn_on_default:
- warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
+ warning = ('\n Warning: config.py - IdleConf.GetOption -\n'
' problem retrieving configuration option %r\n'
' from section %r.\n'
' returning default value: %r' %
(option, section, default))
- try:
- print(warning, file=sys.stderr)
- except OSError:
- pass
+ _warn(warning, configType, section, option)
return default
def SetOption(self, configType, section, option, value):
@@ -358,52 +358,73 @@ class IdleConf:
for element in theme:
if not cfgParser.has_option(themeName, element):
# Print warning that will return a default color
- warning = ('\n Warning: configHandler.IdleConf.GetThemeDict'
+ warning = ('\n Warning: config.IdleConf.GetThemeDict'
' -\n problem retrieving theme element %r'
'\n from theme %r.\n'
' returning default color: %r' %
(element, themeName, theme[element]))
- try:
- print(warning, file=sys.stderr)
- except OSError:
- pass
+ _warn(warning, 'highlight', themeName, element)
theme[element] = cfgParser.Get(
themeName, element, default=theme[element])
return theme
def CurrentTheme(self):
- """Return the name of the currently active text color theme.
+ "Return the name of the currently active text color theme."
+ return self.current_colors_and_keys('Theme')
+
+ def CurrentKeys(self):
+ """Return the name of the currently active key set."""
+ return self.current_colors_and_keys('Keys')
+
+ def current_colors_and_keys(self, section):
+ """Return the currently active name for Theme or Keys section.
+
+ idlelib.config-main.def ('default') includes these sections
- idlelib.config-main.def includes this section
[Theme]
default= 1
name= IDLE Classic
name2=
- # name2 set in user config-main.cfg for themes added after 2015 Oct 1
- Item name2 is needed because setting name to a new builtin
- causes older IDLEs to display multiple error messages or quit.
+ [Keys]
+ default= 1
+ name=
+ name2=
+
+ Item 'name2', is used for built-in ('default') themes and keys
+ added after 2015 Oct 1 and 2016 July 1. This kludge is needed
+ because setting 'name' to a builtin not defined in older IDLEs
+ to display multiple error messages or quit.
See https://bugs.python.org/issue25313.
- When default = True, name2 takes precedence over name,
- while older IDLEs will just use name.
+ When default = True, 'name2' takes precedence over 'name',
+ while older IDLEs will just use name. When default = False,
+ 'name2' may still be set, but it is ignored.
"""
- default = self.GetOption('main', 'Theme', 'default',
+ cfgname = 'highlight' if section == 'Theme' else 'keys'
+ default = self.GetOption('main', section, 'default',
type='bool', default=True)
+ name = ''
if default:
- theme = self.GetOption('main', 'Theme', 'name2', default='')
- if default and not theme or not default:
- theme = self.GetOption('main', 'Theme', 'name', default='')
- source = self.defaultCfg if default else self.userCfg
- if source['highlight'].has_section(theme):
- return theme
+ name = self.GetOption('main', section, 'name2', default='')
+ if not name:
+ name = self.GetOption('main', section, 'name', default='')
+ if name:
+ source = self.defaultCfg if default else self.userCfg
+ if source[cfgname].has_section(name):
+ return name
+ return "IDLE Classic" if section == 'Theme' else self.default_keys()
+
+ @staticmethod
+ def default_keys():
+ if sys.platform[:3] == 'win':
+ return 'IDLE Classic Windows'
+ elif sys.platform == 'darwin':
+ return 'IDLE Classic OSX'
else:
- return "IDLE Classic"
+ return 'IDLE Modern Unix'
- def CurrentKeys(self):
- "Return the name of the currently active key set."
- return self.GetOption('main', 'Keys', 'name', default='')
-
- def GetExtensions(self, active_only=True, editor_only=False, shell_only=False):
+ def GetExtensions(self, active_only=True,
+ editor_only=False, shell_only=False):
"""Return extensions in default and user config-extensions files.
If active_only True, only return active (enabled) extensions
@@ -423,7 +444,7 @@ class IdleConf:
if self.GetOption('extensions', extn, 'enable', default=True,
type='bool'):
#the extension is enabled
- if editor_only or shell_only: # TODO if both, contradictory
+ if editor_only or shell_only: # TODO both True contradict
if editor_only:
option = "enable_editor"
else:
@@ -528,7 +549,8 @@ class IdleConf:
eventStr - virtual event, including brackets, as in '<<event>>'.
"""
eventName = eventStr[2:-2] #trim off the angle brackets
- binding = self.GetOption('keys', keySetName, eventName, default='').split()
+ binding = self.GetOption('keys', keySetName, eventName, default='',
+ warn_on_default=False).split()
return binding
def GetCurrentKeySet(self):
@@ -639,20 +661,28 @@ class IdleConf:
'<<del-word-right>>': ['<Control-Key-Delete>']
}
if keySetName:
- for event in keyBindings:
- binding = self.GetKeyBinding(keySetName, event)
- if binding:
- keyBindings[event] = binding
- else: #we are going to return a default, print warning
- warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
- ' -\n problem retrieving key binding for event %r'
- '\n from key set %r.\n'
- ' returning default value: %r' %
- (event, keySetName, keyBindings[event]))
- try:
- print(warning, file=sys.stderr)
- except OSError:
- pass
+ if not (self.userCfg['keys'].has_section(keySetName) or
+ self.defaultCfg['keys'].has_section(keySetName)):
+ warning = (
+ '\n Warning: config.py - IdleConf.GetCoreKeys -\n'
+ ' key set %r is not defined, using default bindings.' %
+ (keySetName,)
+ )
+ _warn(warning, 'keys', keySetName)
+ else:
+ for event in keyBindings:
+ binding = self.GetKeyBinding(keySetName, event)
+ if binding:
+ keyBindings[event] = binding
+ else: #we are going to return a default, print warning
+ warning = (
+ '\n Warning: config.py - IdleConf.GetCoreKeys -\n'
+ ' problem retrieving key binding for event %r\n'
+ ' from key set %r.\n'
+ ' returning default value: %r' %
+ (event, keySetName, keyBindings[event])
+ )
+ _warn(warning, 'keys', keySetName, event)
return keyBindings
def GetExtraHelpSourceList(self, configSet):
@@ -713,16 +743,13 @@ class IdleConf:
bold = self.GetOption(configType, section, 'font-bold', default=0,
type='bool')
if (family == 'TkFixedFont'):
- if TkVersion < 8.5:
- family = 'Courier'
- else:
- f = Font(name='TkFixedFont', exists=True, root=root)
- actualFont = Font.actual(f)
- family = actualFont['family']
- size = actualFont['size']
- if size <= 0:
- size = 10 # if font in pixels, ignore actual size
- bold = actualFont['weight']=='bold'
+ f = Font(name='TkFixedFont', exists=True, root=root)
+ actualFont = Font.actual(f)
+ family = actualFont['family']
+ size = actualFont['size']
+ if size <= 0:
+ size = 10 # if font in pixels, ignore actual size
+ bold = actualFont['weight'] == 'bold'
return (family, size, 'bold' if bold else 'normal')
def LoadCfgFiles(self):
@@ -739,6 +766,18 @@ class IdleConf:
idleConf = IdleConf()
+
+_warned = set()
+def _warn(msg, *key):
+ key = (msg,) + key
+ if key not in _warned:
+ try:
+ print(msg, file=sys.stderr)
+ except OSError:
+ pass
+ _warned.add(key)
+
+
# TODO Revise test output, write expanded unittest
#
if __name__ == '__main__':
diff --git a/Lib/idlelib/configHelpSourceEdit.py b/Lib/idlelib/configHelpSourceEdit.py
deleted file mode 100644
index cde8118fe6..0000000000
--- a/Lib/idlelib/configHelpSourceEdit.py
+++ /dev/null
@@ -1,170 +0,0 @@
-"Dialog to specify or edit the parameters for a user configured help source."
-
-import os
-import sys
-
-from tkinter import *
-import tkinter.messagebox as tkMessageBox
-import tkinter.filedialog as tkFileDialog
-
-class GetHelpSourceDialog(Toplevel):
- def __init__(self, parent, title, menuItem='', filePath='', _htest=False):
- """Get menu entry and url/ local file location for Additional Help
-
- User selects a name for the Help resource and provides a web url
- or a local file as its source. The user can enter a url or browse
- for the file.
-
- _htest - bool, change box location when running htest
- """
- Toplevel.__init__(self, parent)
- self.configure(borderwidth=5)
- self.resizable(height=FALSE, width=FALSE)
- self.title(title)
- self.transient(parent)
- self.grab_set()
- self.protocol("WM_DELETE_WINDOW", self.cancel)
- self.parent = parent
- self.result = None
- self.create_widgets()
- self.menu.set(menuItem)
- self.path.set(filePath)
- self.withdraw() #hide while setting geometry
- #needs to be done here so that the winfo_reqwidth is valid
- self.update_idletasks()
- #centre dialog over parent. below parent if running htest.
- self.geometry(
- "+%d+%d" % (
- parent.winfo_rootx() +
- (parent.winfo_width()/2 - self.winfo_reqwidth()/2),
- parent.winfo_rooty() +
- ((parent.winfo_height()/2 - self.winfo_reqheight()/2)
- if not _htest else 150)))
- self.deiconify() #geometry set, unhide
- self.bind('<Return>', self.ok)
- self.wait_window()
-
- def create_widgets(self):
- self.menu = StringVar(self)
- self.path = StringVar(self)
- self.fontSize = StringVar(self)
- self.frameMain = Frame(self, borderwidth=2, relief=GROOVE)
- self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH)
- labelMenu = Label(self.frameMain, anchor=W, justify=LEFT,
- text='Menu Item:')
- self.entryMenu = Entry(self.frameMain, textvariable=self.menu,
- width=30)
- self.entryMenu.focus_set()
- labelPath = Label(self.frameMain, anchor=W, justify=LEFT,
- text='Help File Path: Enter URL or browse for file')
- self.entryPath = Entry(self.frameMain, textvariable=self.path,
- width=40)
- self.entryMenu.focus_set()
- labelMenu.pack(anchor=W, padx=5, pady=3)
- self.entryMenu.pack(anchor=W, padx=5, pady=3)
- labelPath.pack(anchor=W, padx=5, pady=3)
- self.entryPath.pack(anchor=W, padx=5, pady=3)
- browseButton = Button(self.frameMain, text='Browse', width=8,
- command=self.browse_file)
- browseButton.pack(pady=3)
- frameButtons = Frame(self)
- frameButtons.pack(side=BOTTOM, fill=X)
- self.buttonOk = Button(frameButtons, text='OK',
- width=8, default=ACTIVE, command=self.ok)
- self.buttonOk.grid(row=0, column=0, padx=5,pady=5)
- self.buttonCancel = Button(frameButtons, text='Cancel',
- width=8, command=self.cancel)
- self.buttonCancel.grid(row=0, column=1, padx=5, pady=5)
-
- def browse_file(self):
- filetypes = [
- ("HTML Files", "*.htm *.html", "TEXT"),
- ("PDF Files", "*.pdf", "TEXT"),
- ("Windows Help Files", "*.chm"),
- ("Text Files", "*.txt", "TEXT"),
- ("All Files", "*")]
- path = self.path.get()
- if path:
- dir, base = os.path.split(path)
- else:
- base = None
- if sys.platform[:3] == 'win':
- dir = os.path.join(os.path.dirname(sys.executable), 'Doc')
- if not os.path.isdir(dir):
- dir = os.getcwd()
- else:
- dir = os.getcwd()
- opendialog = tkFileDialog.Open(parent=self, filetypes=filetypes)
- file = opendialog.show(initialdir=dir, initialfile=base)
- if file:
- self.path.set(file)
-
- def menu_ok(self):
- "Simple validity check for a sensible menu item name"
- menu_ok = True
- menu = self.menu.get()
- menu.strip()
- if not menu:
- tkMessageBox.showerror(title='Menu Item Error',
- message='No menu item specified',
- parent=self)
- self.entryMenu.focus_set()
- menu_ok = False
- elif len(menu) > 30:
- tkMessageBox.showerror(title='Menu Item Error',
- message='Menu item too long:'
- '\nLimit 30 characters.',
- parent=self)
- self.entryMenu.focus_set()
- menu_ok = False
- return menu_ok
-
- def path_ok(self):
- "Simple validity check for menu file path"
- path_ok = True
- path = self.path.get()
- path.strip()
- if not path: #no path specified
- tkMessageBox.showerror(title='File Path Error',
- message='No help file path specified.',
- parent=self)
- self.entryPath.focus_set()
- path_ok = False
- elif path.startswith(('www.', 'http')):
- pass
- else:
- if path[:5] == 'file:':
- path = path[5:]
- if not os.path.exists(path):
- tkMessageBox.showerror(title='File Path Error',
- message='Help file path does not exist.',
- parent=self)
- self.entryPath.focus_set()
- path_ok = False
- return path_ok
-
- def ok(self, event=None):
- if self.menu_ok() and self.path_ok():
- self.result = (self.menu.get().strip(),
- self.path.get().strip())
- if sys.platform == 'darwin':
- path = self.result[1]
- if path.startswith(('www', 'file:', 'http:', 'https:')):
- pass
- else:
- # Mac Safari insists on using the URI form for local files
- self.result = list(self.result)
- self.result[1] = "file://" + path
- self.destroy()
-
- def cancel(self, event=None):
- self.result = None
- self.destroy()
-
-if __name__ == '__main__':
- import unittest
- unittest.main('idlelib.idle_test.test_config_help',
- verbosity=2, exit=False)
-
- from idlelib.idle_test.htest import run
- run(GetHelpSourceDialog)
diff --git a/Lib/idlelib/configSectionNameDialog.py b/Lib/idlelib/configSectionNameDialog.py
deleted file mode 100644
index 5137836981..0000000000
--- a/Lib/idlelib/configSectionNameDialog.py
+++ /dev/null
@@ -1,98 +0,0 @@
-"""
-Dialog that allows user to specify a new config file section name.
-Used to get new highlight theme and keybinding set names.
-The 'return value' for the dialog, used two placed in configDialog.py,
-is the .result attribute set in the Ok and Cancel methods.
-"""
-from tkinter import *
-import tkinter.messagebox as tkMessageBox
-
-class GetCfgSectionNameDialog(Toplevel):
- def __init__(self, parent, title, message, used_names, _htest=False):
- """
- message - string, informational message to display
- used_names - string collection, names already in use for validity check
- _htest - bool, change box location when running htest
- """
- Toplevel.__init__(self, parent)
- self.configure(borderwidth=5)
- self.resizable(height=FALSE, width=FALSE)
- self.title(title)
- self.transient(parent)
- self.grab_set()
- self.protocol("WM_DELETE_WINDOW", self.Cancel)
- self.parent = parent
- self.message = message
- self.used_names = used_names
- self.create_widgets()
- self.withdraw() #hide while setting geometry
- self.update_idletasks()
- #needs to be done here so that the winfo_reqwidth is valid
- self.messageInfo.config(width=self.frameMain.winfo_reqwidth())
- self.geometry(
- "+%d+%d" % (
- parent.winfo_rootx() +
- (parent.winfo_width()/2 - self.winfo_reqwidth()/2),
- parent.winfo_rooty() +
- ((parent.winfo_height()/2 - self.winfo_reqheight()/2)
- if not _htest else 100)
- ) ) #centre dialog over parent (or below htest box)
- self.deiconify() #geometry set, unhide
- self.wait_window()
-
- def create_widgets(self):
- self.name = StringVar(self.parent)
- self.fontSize = StringVar(self.parent)
- self.frameMain = Frame(self, borderwidth=2, relief=SUNKEN)
- self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH)
- self.messageInfo = Message(self.frameMain, anchor=W, justify=LEFT,
- padx=5, pady=5, text=self.message) #,aspect=200)
- entryName = Entry(self.frameMain, textvariable=self.name, width=30)
- entryName.focus_set()
- self.messageInfo.pack(padx=5, pady=5) #, expand=TRUE, fill=BOTH)
- entryName.pack(padx=5, pady=5)
-
- frameButtons = Frame(self, pady=2)
- frameButtons.pack(side=BOTTOM)
- self.buttonOk = Button(frameButtons, text='Ok',
- width=8, command=self.Ok)
- self.buttonOk.pack(side=LEFT, padx=5)
- self.buttonCancel = Button(frameButtons, text='Cancel',
- width=8, command=self.Cancel)
- self.buttonCancel.pack(side=RIGHT, padx=5)
-
- def name_ok(self):
- ''' After stripping entered name, check that it is a sensible
- ConfigParser file section name. Return it if it is, '' if not.
- '''
- name = self.name.get().strip()
- if not name: #no name specified
- tkMessageBox.showerror(title='Name Error',
- message='No name specified.', parent=self)
- elif len(name)>30: #name too long
- tkMessageBox.showerror(title='Name Error',
- message='Name too long. It should be no more than '+
- '30 characters.', parent=self)
- name = ''
- elif name in self.used_names:
- tkMessageBox.showerror(title='Name Error',
- message='This name is already in use.', parent=self)
- name = ''
- return name
-
- def Ok(self, event=None):
- name = self.name_ok()
- if name:
- self.result = name
- self.destroy()
-
- def Cancel(self, event=None):
- self.result = ''
- self.destroy()
-
-if __name__ == '__main__':
- import unittest
- unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False)
-
- from idlelib.idle_test.htest import run
- run(GetCfgSectionNameDialog)
diff --git a/Lib/idlelib/keybindingDialog.py b/Lib/idlelib/config_key.py
index e6438bfc39..26022934c3 100644
--- a/Lib/idlelib/keybindingDialog.py
+++ b/Lib/idlelib/config_key.py
@@ -2,31 +2,35 @@
Dialog for building Tkinter accelerator key bindings
"""
from tkinter import *
+from tkinter.ttk import Scrollbar
import tkinter.messagebox as tkMessageBox
import string
import sys
class GetKeysDialog(Toplevel):
- def __init__(self,parent,title,action,currentKeySequences,_htest=False):
+ def __init__(self, parent, title, action, currentKeySequences,
+ _htest=False, _utest=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
+ _utest - bool, do not wait when running unittest
_htest - bool, change box location when running htest
"""
Toplevel.__init__(self, parent)
+ self.withdraw() #hide while setting geometry
self.configure(borderwidth=5)
- self.resizable(height=FALSE,width=FALSE)
+ self.resizable(height=FALSE, width=FALSE)
self.title(title)
self.transient(parent)
self.grab_set()
self.protocol("WM_DELETE_WINDOW", self.Cancel)
self.parent = parent
self.action=action
- self.currentKeySequences=currentKeySequences
- self.result=''
- self.keyString=StringVar(self)
+ self.currentKeySequences = currentKeySequences
+ self.result = ''
+ self.keyString = StringVar(self)
self.keyString.set('')
self.SetModifiersForPlatform() # set self.modifiers, self.modifier_label
self.modifier_vars = []
@@ -37,7 +41,6 @@ class GetKeysDialog(Toplevel):
self.advanced = False
self.CreateWidgets()
self.LoadFinalKeyList()
- self.withdraw() #hide while setting geometry
self.update_idletasks()
self.geometry(
"+%d+%d" % (
@@ -47,8 +50,9 @@ class GetKeysDialog(Toplevel):
((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()
+ if not _utest:
+ self.deiconify() #geometry set, unhide
+ self.wait_window()
def CreateWidgets(self):
frameMain = Frame(self,borderwidth=2,relief=SUNKEN)
@@ -261,6 +265,7 @@ class GetKeysDialog(Toplevel):
keysOK = True
return keysOK
+
if __name__ == '__main__':
from idlelib.idle_test.htest import run
run(GetKeysDialog)
diff --git a/Lib/idlelib/configDialog.py b/Lib/idlelib/configdialog.py
index 5f5bd36f8a..8184582a3e 100644
--- a/Lib/idlelib/configDialog.py
+++ b/Lib/idlelib/configdialog.py
@@ -10,18 +10,18 @@ Refer to comments in EditorWindow autoindent code for details.
"""
from tkinter import *
-import tkinter.messagebox as tkMessageBox
+from tkinter.ttk import Scrollbar
import tkinter.colorchooser as tkColorChooser
import tkinter.font as tkFont
+import tkinter.messagebox as tkMessageBox
-from idlelib.configHandler import idleConf
-from idlelib.dynOptionMenuWidget import DynOptionMenu
-from idlelib.keybindingDialog import GetKeysDialog
-from idlelib.configSectionNameDialog import GetCfgSectionNameDialog
-from idlelib.configHelpSourceEdit import GetHelpSourceDialog
+from idlelib.config import idleConf
+from idlelib.config_key import GetKeysDialog
+from idlelib.dynoption import DynOptionMenu
+from idlelib import macosx
+from idlelib.query import SectionName, HelpSource
from idlelib.tabbedpages import TabbedPageSet
-from idlelib.textView import view_text
-from idlelib import macosxSupport
+from idlelib.textview import view_text
class ConfigDialog(Toplevel):
@@ -91,7 +91,7 @@ class ConfigDialog(Toplevel):
self.create_action_buttons().pack(side=BOTTOM)
def create_action_buttons(self):
- if macosxSupport.isAquaTk():
+ if macosx.isAquaTk():
# Changing the default padding on OSX results in unreadable
# text in the buttons
paddingArgs = {}
@@ -341,6 +341,7 @@ class ConfigDialog(Toplevel):
buttonSaveCustomKeys = Button(
frames[1], text='Save as New Custom Key Set',
command=self.SaveAsNewKeySet)
+ self.new_custom_keys = Label(frames[0], bd=2)
##widget packing
#body
@@ -361,6 +362,7 @@ class ConfigDialog(Toplevel):
self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS)
self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW)
self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW)
+ self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
frames[0].pack(side=TOP, fill=BOTH, expand=True)
@@ -390,28 +392,28 @@ class ConfigDialog(Toplevel):
text=' Additional Help Sources ')
#frameRun
labelRunChoiceTitle = Label(frameRun, text='At Startup')
- radioStartupEdit = Radiobutton(
+ self.radioStartupEdit = Radiobutton(
frameRun, variable=self.startupEdit, value=1,
- command=self.SetKeysType, text="Open Edit Window")
- radioStartupShell = Radiobutton(
+ text="Open Edit Window")
+ self.radioStartupShell = Radiobutton(
frameRun, variable=self.startupEdit, value=0,
- command=self.SetKeysType, text='Open Shell Window')
+ text='Open Shell Window')
#frameSave
labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5) ')
- radioSaveAsk = Radiobutton(
+ self.radioSaveAsk = Radiobutton(
frameSave, variable=self.autoSave, value=0,
- command=self.SetKeysType, text="Prompt to Save")
- radioSaveAuto = Radiobutton(
+ text="Prompt to Save")
+ self.radioSaveAuto = Radiobutton(
frameSave, variable=self.autoSave, value=1,
- command=self.SetKeysType, text='No Prompt')
+ text='No Prompt')
#frameWinSize
labelWinSizeTitle = Label(
frameWinSize, text='Initial Window Size (in characters)')
labelWinWidthTitle = Label(frameWinSize, text='Width')
- entryWinWidth = Entry(
+ self.entryWinWidth = Entry(
frameWinSize, textvariable=self.winWidth, width=3)
labelWinHeightTitle = Label(frameWinSize, text='Height')
- entryWinHeight = Entry(
+ self.entryWinHeight = Entry(
frameWinSize, textvariable=self.winHeight, width=3)
#frameHelp
frameHelpList = Frame(frameHelp)
@@ -441,17 +443,17 @@ class ConfigDialog(Toplevel):
frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
#frameRun
labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
- radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
- radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
+ self.radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
+ self.radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
#frameSave
labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
- radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
- radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5)
+ self.radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
+ self.radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5)
#frameWinSize
labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
- entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5)
+ self.entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5)
labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5)
- entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5)
+ self.entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5)
labelWinWidthTitle.pack(side=RIGHT, anchor=E, pady=5)
#frameHelp
frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
@@ -464,24 +466,24 @@ class ConfigDialog(Toplevel):
return frame
def AttachVarCallbacks(self):
- self.fontSize.trace_variable('w', self.VarChanged_font)
- self.fontName.trace_variable('w', self.VarChanged_font)
- self.fontBold.trace_variable('w', self.VarChanged_font)
- self.spaceNum.trace_variable('w', self.VarChanged_spaceNum)
- self.colour.trace_variable('w', self.VarChanged_colour)
- self.builtinTheme.trace_variable('w', self.VarChanged_builtinTheme)
- self.customTheme.trace_variable('w', self.VarChanged_customTheme)
- self.themeIsBuiltin.trace_variable('w', self.VarChanged_themeIsBuiltin)
- self.highlightTarget.trace_variable('w', self.VarChanged_highlightTarget)
- self.keyBinding.trace_variable('w', self.VarChanged_keyBinding)
- self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys)
- self.customKeys.trace_variable('w', self.VarChanged_customKeys)
- self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin)
- self.winWidth.trace_variable('w', self.VarChanged_winWidth)
- self.winHeight.trace_variable('w', self.VarChanged_winHeight)
- self.startupEdit.trace_variable('w', self.VarChanged_startupEdit)
- self.autoSave.trace_variable('w', self.VarChanged_autoSave)
- self.encoding.trace_variable('w', self.VarChanged_encoding)
+ self.fontSize.trace_add('write', self.VarChanged_font)
+ self.fontName.trace_add('write', self.VarChanged_font)
+ self.fontBold.trace_add('write', self.VarChanged_font)
+ self.spaceNum.trace_add('write', self.VarChanged_spaceNum)
+ self.colour.trace_add('write', self.VarChanged_colour)
+ self.builtinTheme.trace_add('write', self.VarChanged_builtinTheme)
+ self.customTheme.trace_add('write', self.VarChanged_customTheme)
+ self.themeIsBuiltin.trace_add('write', self.VarChanged_themeIsBuiltin)
+ self.highlightTarget.trace_add('write', self.VarChanged_highlightTarget)
+ self.keyBinding.trace_add('write', self.VarChanged_keyBinding)
+ self.builtinKeys.trace_add('write', self.VarChanged_builtinKeys)
+ self.customKeys.trace_add('write', self.VarChanged_customKeys)
+ self.keysAreBuiltin.trace_add('write', self.VarChanged_keysAreBuiltin)
+ self.winWidth.trace_add('write', self.VarChanged_winWidth)
+ self.winHeight.trace_add('write', self.VarChanged_winHeight)
+ self.startupEdit.trace_add('write', self.VarChanged_startupEdit)
+ self.autoSave.trace_add('write', self.VarChanged_autoSave)
+ self.encoding.trace_add('write', self.VarChanged_encoding)
def remove_var_callbacks(self):
"Remove callbacks to prevent memory leaks."
@@ -492,7 +494,7 @@ class ConfigDialog(Toplevel):
self.keyBinding, self.builtinKeys, self.customKeys,
self.keysAreBuiltin, self.winWidth, self.winHeight,
self.startupEdit, self.autoSave, self.encoding,):
- var.trace_vdelete('w', var.trace_vinfo()[0][1])
+ var.trace_remove('write', var.trace_info()[0][1])
def VarChanged_font(self, *params):
'''When one font attribute changes, save them all, as they are
@@ -514,10 +516,11 @@ class ConfigDialog(Toplevel):
self.OnNewColourSet()
def VarChanged_builtinTheme(self, *params):
+ oldthemes = ('IDLE Classic', 'IDLE New')
value = self.builtinTheme.get()
- if value == 'IDLE Dark':
- if idleConf.GetOption('main', 'Theme', 'name') != 'IDLE New':
- self.AddChangedItem('main', 'Theme', 'name', 'IDLE Classic')
+ if value not in oldthemes:
+ if idleConf.GetOption('main', 'Theme', 'name') not in oldthemes:
+ self.AddChangedItem('main', 'Theme', 'name', oldthemes[0])
self.AddChangedItem('main', 'Theme', 'name2', value)
self.new_custom_theme.config(text='New theme, see Help',
fg='#500000')
@@ -557,8 +560,23 @@ class ConfigDialog(Toplevel):
self.AddChangedItem('extensions', extKeybindSection, event, value)
def VarChanged_builtinKeys(self, *params):
+ oldkeys = (
+ 'IDLE Classic Windows',
+ 'IDLE Classic Unix',
+ 'IDLE Classic Mac',
+ 'IDLE Classic OSX',
+ )
value = self.builtinKeys.get()
- self.AddChangedItem('main', 'Keys', 'name', value)
+ if value not in oldkeys:
+ if idleConf.GetOption('main', 'Keys', 'name') not in oldkeys:
+ self.AddChangedItem('main', 'Keys', 'name', oldkeys[0])
+ self.AddChangedItem('main', 'Keys', 'name2', value)
+ self.new_custom_keys.config(text='New key set, see Help',
+ fg='#500000')
+ else:
+ self.AddChangedItem('main', 'Keys', 'name', value)
+ self.AddChangedItem('main', 'Keys', 'name2', '')
+ self.new_custom_keys.config(text='', fg='black')
self.LoadKeysList(value)
def VarChanged_customKeys(self, *params):
@@ -683,7 +701,7 @@ class ConfigDialog(Toplevel):
def GetNewKeysName(self, message):
usedNames = (idleConf.GetSectionList('user', 'keys') +
idleConf.GetSectionList('default', 'keys'))
- newKeySet = GetCfgSectionNameDialog(
+ newKeySet = SectionName(
self, 'New Custom Key Set', message, usedNames).result
return newKeySet
@@ -767,8 +785,10 @@ class ConfigDialog(Toplevel):
else:
self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
#revert to default key set
- self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default'))
- self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name'))
+ self.keysAreBuiltin.set(idleConf.defaultCfg['main']
+ .Get('Keys', 'default'))
+ self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
+ or idleConf.default_keys())
#user can't back out of these changes, they must be applied now
self.SaveAllChangedConfigs()
self.ActivateConfigChanges()
@@ -836,7 +856,7 @@ class ConfigDialog(Toplevel):
def GetNewThemeName(self, message):
usedNames = (idleConf.GetSectionList('user', 'highlight') +
idleConf.GetSectionList('default', 'highlight'))
- newTheme = GetCfgSectionNameDialog(
+ newTheme = SectionName(
self, 'New Custom Theme', message, usedNames).result
return newTheme
@@ -939,7 +959,8 @@ class ConfigDialog(Toplevel):
self.buttonHelpListRemove.config(state=DISABLED)
def HelpListItemAdd(self):
- helpSource = GetHelpSourceDialog(self, 'New Help Source').result
+ helpSource = HelpSource(self, 'New Help Source',
+ ).result
if helpSource:
self.userHelpList.append((helpSource[0], helpSource[1]))
self.listHelp.insert(END, helpSource[0])
@@ -949,16 +970,17 @@ class ConfigDialog(Toplevel):
def HelpListItemEdit(self):
itemIndex = self.listHelp.index(ANCHOR)
helpSource = self.userHelpList[itemIndex]
- newHelpSource = GetHelpSourceDialog(
- self, 'Edit Help Source', menuItem=helpSource[0],
- filePath=helpSource[1]).result
- if (not newHelpSource) or (newHelpSource == helpSource):
- return #no changes
- self.userHelpList[itemIndex] = newHelpSource
- self.listHelp.delete(itemIndex)
- self.listHelp.insert(itemIndex, newHelpSource[0])
- self.UpdateUserHelpChangedItems()
- self.SetHelpListButtonStates()
+ newHelpSource = HelpSource(
+ self, 'Edit Help Source',
+ menuitem=helpSource[0],
+ filepath=helpSource[1],
+ ).result
+ if newHelpSource and newHelpSource != helpSource:
+ self.userHelpList[itemIndex] = newHelpSource
+ self.listHelp.delete(itemIndex)
+ self.listHelp.insert(itemIndex, newHelpSource[0])
+ self.UpdateUserHelpChangedItems()
+ self.SetHelpListButtonStates()
def HelpListItemRemove(self):
itemIndex = self.listHelp.index(ANCHOR)
@@ -996,7 +1018,8 @@ class ConfigDialog(Toplevel):
pass
##font size dropdown
self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13',
- '14', '16', '18', '20', '22'), fontSize )
+ '14', '16', '18', '20', '22',
+ '25', '29', '34', '40'), fontSize )
##fontWeight
self.fontBold.set(fontBold)
##font sample
@@ -1065,7 +1088,7 @@ class ConfigDialog(Toplevel):
self.optMenuKeysCustom.SetMenu(itemList, currentOption)
itemList = idleConf.GetSectionList('default', 'keys')
itemList.sort()
- self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0])
+ self.optMenuKeysBuiltin.SetMenu(itemList, idleConf.default_keys())
self.SetKeysType()
##load keyset element list
keySetName = idleConf.CurrentKeys()
@@ -1367,12 +1390,18 @@ machine. Some do not take affect until IDLE is restarted.
[Cancel] only cancels changes made since the last save.
'''
help_pages = {
- 'Highlighting':'''
+ 'Highlighting': '''
Highlighting:
The IDLE Dark color theme is new in October 2015. It can only
be used with older IDLE releases if it is saved as a custom
theme, with a different name.
-'''
+''',
+ 'Keys': '''
+Keys:
+The IDLE Modern Unix key set is new in June 2016. It can only
+be used with older IDLE releases if it is saved as a custom
+key set, with a different name.
+''',
}
diff --git a/Lib/idlelib/Debugger.py b/Lib/idlelib/debugger.py
index d5e217dde9..114d0d128e 100644
--- a/Lib/idlelib/Debugger.py
+++ b/Lib/idlelib/debugger.py
@@ -1,9 +1,12 @@
-import os
import bdb
+import os
+
from tkinter import *
-from idlelib.WindowList import ListedToplevel
-from idlelib.ScrolledList import ScrolledList
-from idlelib import macosxSupport
+from tkinter.ttk import Scrollbar
+
+from idlelib import macosx
+from idlelib.scrolledlist import ScrolledList
+from idlelib.windows import ListedToplevel
class Idb(bdb.Bdb):
@@ -34,8 +37,10 @@ class Idb(bdb.Bdb):
return True
else:
prev_frame = frame.f_back
- if prev_frame.f_code.co_filename.count('Debugger.py'):
- # (that test will catch both Debugger.py and RemoteDebugger.py)
+ prev_name = prev_frame.f_code.co_filename
+ if 'idlelib' in prev_name and 'debugger' in prev_name:
+ # catch both idlelib/debugger.py and idlelib/debugger_r.py
+ # on both posix and windows
return False
return self.in_rpc_code(prev_frame)
@@ -370,7 +375,7 @@ class Debugger:
class StackViewer(ScrolledList):
def __init__(self, master, flist, gui):
- if macosxSupport.isAquaTk():
+ if macosx.isAquaTk():
# At least on with the stock AquaTk version on OSX 10.4 you'll
# get a shaking GUI that eventually kills IDLE if the width
# argument is specified.
@@ -502,7 +507,7 @@ class NamespaceViewer:
#
# There is also an obscure bug in sorted(dict) where the
# interpreter gets into a loop requesting non-existing dict[0],
- # dict[1], dict[2], etc from the RemoteDebugger.DictProxy.
+ # dict[1], dict[2], etc from the debugger_r.DictProxy.
###
keys_list = dict.keys()
names = sorted(keys_list)
diff --git a/Lib/idlelib/RemoteDebugger.py b/Lib/idlelib/debugger_r.py
index be2262f080..bc971276de 100644
--- a/Lib/idlelib/RemoteDebugger.py
+++ b/Lib/idlelib/debugger_r.py
@@ -21,7 +21,7 @@ barrier, in particular frame and traceback objects.
"""
import types
-from idlelib import Debugger
+from idlelib import debugger
debugging = 0
@@ -187,7 +187,7 @@ def start_debugger(rpchandler, gui_adap_oid):
"""
gui_proxy = GUIProxy(rpchandler, gui_adap_oid)
- idb = Debugger.Idb(gui_proxy)
+ idb = debugger.Idb(gui_proxy)
idb_adap = IdbAdapter(idb)
rpchandler.register(idb_adap_oid, idb_adap)
return idb_adap_oid
@@ -362,7 +362,7 @@ def start_remote_debugger(rpcclt, pyshell):
idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\
(gui_adap_oid,), {})
idb_proxy = IdbProxy(rpcclt, pyshell, idb_adap_oid)
- gui = Debugger.Debugger(pyshell, idb_proxy)
+ gui = debugger.Debugger(pyshell, idb_proxy)
gui_adap = GUIAdapter(rpcclt, gui)
rpcclt.register(gui_adap_oid, gui_adap)
return gui
@@ -373,7 +373,7 @@ def close_remote_debugger(rpcclt):
Request that the RPCServer shut down the subprocess debugger and link.
Unregister the GUIAdapter, which will cause a GC on the Idle process
debugger and RPC link objects. (The second reference to the debugger GUI
- is deleted in PyShell.close_remote_debugger().)
+ is deleted in pyshell.close_remote_debugger().)
"""
close_subprocess_debugger(rpcclt)
diff --git a/Lib/idlelib/ObjectBrowser.py b/Lib/idlelib/debugobj.py
index 7b57aa4c68..b70b13cec4 100644
--- a/Lib/idlelib/ObjectBrowser.py
+++ b/Lib/idlelib/debugobj.py
@@ -8,13 +8,10 @@
# 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
+from idlelib.tree import TreeItem, TreeNode, ScrolledCanvas
+
myrepr = Repr()
myrepr.maxstring = 100
myrepr.maxother = 100
@@ -122,21 +119,20 @@ def make_objecttreeitem(labeltext, object, setfunction=None):
return c(labeltext, object, setfunction)
-def _object_browser(parent):
+def _object_browser(parent): # htest #
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)
+ from tkinter import Toplevel
+ top = Toplevel(parent)
+ top.title("Test debug object browser")
+ x, y = map(int, parent.geometry().split('+')[1:])
+ top.geometry("+%d+%d" % (x + 100, y + 175))
+ top.configure(bd=0, bg="yellow")
+ top.focus_set()
+ sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1)
sc.frame.pack(expand=1, fill="both")
item = make_objecttreeitem("sys", sys)
node = TreeNode(sc.canvas, None, item)
node.update()
- root.mainloop()
if __name__ == '__main__':
from idlelib.idle_test.htest import run
diff --git a/Lib/idlelib/RemoteObjectBrowser.py b/Lib/idlelib/debugobj_r.py
index 8031aaeaf1..8031aaeaf1 100644
--- a/Lib/idlelib/RemoteObjectBrowser.py
+++ b/Lib/idlelib/debugobj_r.py
diff --git a/Lib/idlelib/Delegator.py b/Lib/idlelib/delegator.py
index dc2a1aaeea..dc2a1aaeea 100644
--- a/Lib/idlelib/Delegator.py
+++ b/Lib/idlelib/delegator.py
diff --git a/Lib/idlelib/dynOptionMenuWidget.py b/Lib/idlelib/dynoption.py
index 515b4bafc2..9c6ffa435a 100644
--- a/Lib/idlelib/dynOptionMenuWidget.py
+++ b/Lib/idlelib/dynoption.py
@@ -3,6 +3,7 @@ OptionMenu widget modified to allow dynamic menu reconfiguration
and setting of highlightthickness
"""
import copy
+
from tkinter import OptionMenu, _setit, StringVar, Button
class DynOptionMenu(OptionMenu):
@@ -34,12 +35,12 @@ class DynOptionMenu(OptionMenu):
self.variable.set(value)
def _dyn_option_menu(parent): # htest #
- from tkinter import Toplevel
+ from tkinter import Toplevel # + StringVar, Button
- top = Toplevel()
+ top = Toplevel(parent)
top.title("Tets dynamic option menu")
- top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200,
- parent.winfo_rooty() + 150))
+ x, y = map(int, parent.geometry().split('+')[1:])
+ top.geometry("200x100+%d+%d" % (x + 250, y + 175))
top.focus_set()
var = StringVar(top)
diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/editor.py
index 9944da3e70..ae475cb9f9 100644
--- a/Lib/idlelib/EditorWindow.py
+++ b/Lib/idlelib/editor.py
@@ -6,28 +6,34 @@ import platform
import re
import string
import sys
+import tokenize
+import traceback
+import webbrowser
+
from tkinter import *
+from tkinter.ttk import Scrollbar
import tkinter.simpledialog as tkSimpleDialog
import tkinter.messagebox as tkMessageBox
-import traceback
-import webbrowser
-from idlelib.MultiCall import MultiCallCreator
-from idlelib import WindowList
-from idlelib import SearchDialog
-from idlelib import GrepDialog
-from idlelib import ReplaceDialog
-from idlelib import PyParse
-from idlelib.configHandler import idleConf
-from idlelib import aboutDialog, textView, configDialog
-from idlelib import macosxSupport
+from idlelib.config import idleConf
+from idlelib import configdialog
+from idlelib import grep
from idlelib import help
+from idlelib import help_about
+from idlelib import macosx
+from idlelib.multicall import MultiCallCreator
+from idlelib import pyparse
+from idlelib import query
+from idlelib import replace
+from idlelib import search
+from idlelib import textview
+from idlelib import windows
# The default tab setting for a Text widget, in average-width characters.
TK_TABWIDTH_DEFAULT = 8
-
_py_version = ' (%s)' % platform.python_version()
+
def _sphinx_version():
"Format sys.version_info to produce the Sphinx version string used to install the chm docs"
major, minor, micro, level, serial = sys.version_info
@@ -40,63 +46,16 @@ def _sphinx_version():
return release
-class HelpDialog(object):
-
- def __init__(self):
- self.parent = None # parent of help window
- self.dlg = None # the help window iteself
-
- def display(self, parent, near=None):
- """ Display the help dialog.
-
- parent - parent widget for the help window
-
- near - a Toplevel widget (e.g. EditorWindow or PyShell)
- to use as a reference for placing the help window
- """
- import warnings as w
- w.warn("EditorWindow.HelpDialog is no longer used by Idle.\n"
- "It will be removed in 3.6 or later.\n"
- "It has been replaced by private help.HelpWindow\n",
- DeprecationWarning, stacklevel=2)
- if self.dlg is None:
- self.show_dialog(parent)
- if near:
- self.nearwindow(near)
-
- def show_dialog(self, parent):
- self.parent = parent
- fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')
- self.dlg = dlg = textView.view_file(parent,'Help',fn, modal=False)
- dlg.bind('<Destroy>', self.destroy, '+')
-
- def nearwindow(self, near):
- # Place the help dialog near the window specified by parent.
- # Note - this may not reposition the window in Metacity
- # if "/apps/metacity/general/disable_workarounds" is enabled
- dlg = self.dlg
- geom = (near.winfo_rootx() + 10, near.winfo_rooty() + 10)
- dlg.withdraw()
- dlg.geometry("=+%d+%d" % geom)
- dlg.deiconify()
- dlg.lift()
-
- def destroy(self, ev=None):
- self.dlg = None
- self.parent = None
-
-helpDialog = HelpDialog() # singleton instance, no longer used
-
-
class EditorWindow(object):
- from idlelib.Percolator import Percolator
- from idlelib.ColorDelegator import ColorDelegator, color_config
- from idlelib.UndoDelegator import UndoDelegator
- from idlelib.IOBinding import IOBinding, filesystemencoding, encoding
- from idlelib import Bindings
+ from idlelib.percolator import Percolator
+ from idlelib.colorizer import ColorDelegator, color_config
+ from idlelib.undo import UndoDelegator
+ from idlelib.iomenu import IOBinding, encoding
+ from idlelib import mainmenu
from tkinter import Toplevel
- from idlelib.MultiStatusBar import MultiStatusBar
+ from idlelib.statusbar import MultiStatusBar
+ filesystemencoding = sys.getfilesystemencoding() # for file names
help_url = None
def __init__(self, flist=None, filename=None, key=None, root=None):
@@ -136,11 +95,11 @@ class EditorWindow(object):
except AttributeError:
sys.ps1 = '>>> '
self.menubar = Menu(root)
- self.top = top = WindowList.ListedToplevel(root, menu=self.menubar)
+ self.top = top = windows.ListedToplevel(root, menu=self.menubar)
if flist:
self.tkinter_vars = flist.vars
#self.top.instance_dict makes flist.inversedict available to
- #configDialog.py so it can access all EditorWindow instances
+ #configdialog.py so it can access all EditorWindow instances
self.top.instance_dict = flist.inversedict
else:
self.tkinter_vars = {} # keys: Tkinter event names
@@ -158,13 +117,10 @@ class EditorWindow(object):
'wrap': 'none',
'highlightthickness': 0,
'width': self.width,
- 'height': idleConf.GetOption('main', 'EditorWindow',
- 'height', type='int')}
- if TkVersion >= 8.5:
- # Starting with tk 8.5 we have to set the new tabstyle option
- # to 'wordprocessor' to achieve the same display of tabs as in
- # older tk versions.
- text_options['tabstyle'] = 'wordprocessor'
+ 'tabstyle': 'wordprocessor', # new in 8.5
+ 'height': idleConf.GetOption(
+ 'main', 'EditorWindow', 'height', type='int'),
+ }
self.text = text = MultiCallCreator(Text)(text_frame, **text_options)
self.top.focused_widget = self.text
@@ -173,7 +129,7 @@ class EditorWindow(object):
self.top.protocol("WM_DELETE_WINDOW", self.close)
self.top.bind("<<close-window>>", self.close_event)
- if macosxSupport.isAquaTk():
+ if macosx.isAquaTk():
# Command-W on editorwindows doesn't work without this.
text.bind('<<close-window>>', self.close_event)
# Some OS X systems have only one mouse button, so use
@@ -309,7 +265,7 @@ class EditorWindow(object):
menu.add_separator()
end = end + 1
self.wmenu_end = end
- WindowList.register_callback(self.postwindowsmenu)
+ windows.register_callback(self.postwindowsmenu)
# Some abstractions so IDLE extensions are cross-IDE
self.askyesno = tkMessageBox.askyesno
@@ -418,7 +374,7 @@ class EditorWindow(object):
underline, label = prepstr(label)
menudict[name] = menu = Menu(mbar, name=name, tearoff=0)
mbar.add_cascade(label=label, menu=menu, underline=underline)
- if macosxSupport.isCarbonTk():
+ if macosx.isCarbonTk():
# Insert the application menu
menudict['application'] = menu = Menu(mbar, name='apple',
tearoff=0)
@@ -439,7 +395,7 @@ class EditorWindow(object):
end = -1
if end > self.wmenu_end:
menu.delete(self.wmenu_end+1, end)
- WindowList.add_windows_to_menu(menu)
+ windows.add_windows_to_menu(menu)
rmenu = None
@@ -507,17 +463,17 @@ class EditorWindow(object):
def about_dialog(self, event=None):
"Handle Help 'About IDLE' event."
- # Synchronize with macosxSupport.overrideRootMenu.about_dialog.
- aboutDialog.AboutDialog(self.top,'About IDLE')
+ # Synchronize with macosx.overrideRootMenu.about_dialog.
+ help_about.AboutDialog(self.top,'About IDLE')
def config_dialog(self, event=None):
"Handle Options 'Configure IDLE' event."
- # Synchronize with macosxSupport.overrideRootMenu.config_dialog.
- configDialog.ConfigDialog(self.top,'Settings')
+ # Synchronize with macosx.overrideRootMenu.config_dialog.
+ configdialog.ConfigDialog(self.top,'Settings')
def help_dialog(self, event=None):
"Handle Help 'IDLE Help' event."
- # Synchronize with macosxSupport.overrideRootMenu.help_dialog.
+ # Synchronize with macosx.overrideRootMenu.help_dialog.
if self.root:
parent = self.root
else:
@@ -590,23 +546,23 @@ class EditorWindow(object):
return "break"
def find_event(self, event):
- SearchDialog.find(self.text)
+ search.find(self.text)
return "break"
def find_again_event(self, event):
- SearchDialog.find_again(self.text)
+ search.find_again(self.text)
return "break"
def find_selection_event(self, event):
- SearchDialog.find_selection(self.text)
+ search.find_selection(self.text)
return "break"
def find_in_files_event(self, event):
- GrepDialog.grep(self.text, self.io, self.flist)
+ grep.grep(self.text, self.io, self.flist)
return "break"
def replace_event(self, event):
- ReplaceDialog.replace(self.text)
+ replace.replace(self.text)
return "break"
def goto_line_event(self, event):
@@ -622,46 +578,27 @@ class EditorWindow(object):
text.see("insert")
def open_module(self, event=None):
- # XXX Shouldn't this be in IOBinding?
+ """Get module name from user and open it.
+
+ Return module path or None for calls by open_class_browser
+ when latter is not invoked in named editor window.
+ """
+ # XXX This, open_class_browser, and open_path_browser
+ # would fit better in iomenu.IOBinding.
try:
- name = self.text.get("sel.first", "sel.last")
+ name = self.text.get("sel.first", "sel.last").strip()
except TclError:
- name = ""
- else:
- name = name.strip()
- name = tkSimpleDialog.askstring("Module",
- "Enter the name of a Python module\n"
- "to search on sys.path and open:",
- parent=self.text, initialvalue=name)
- if name:
- name = name.strip()
- if not name:
- return
- # XXX Ought to insert current file's directory in front of path
- try:
- spec = importlib.util.find_spec(name)
- except (ValueError, ImportError) as msg:
- tkMessageBox.showerror("Import error", str(msg), parent=self.text)
- return
- if spec is None:
- tkMessageBox.showerror("Import error", "module not found",
- parent=self.text)
- return
- if not isinstance(spec.loader, importlib.abc.SourceLoader):
- tkMessageBox.showerror("Import error", "not a source-based module",
- parent=self.text)
- return
- try:
- file_path = spec.loader.get_filename(name)
- except AttributeError:
- tkMessageBox.showerror("Import error",
- "loader does not support get_filename",
- parent=self.text)
- return
- if self.flist:
- self.flist.open(file_path)
- else:
- self.io.loadfile(file_path)
+ name = ''
+ file_path = query.ModuleName(
+ self.text, "Open Module",
+ "Enter the name of a Python module\n"
+ "to search on sys.path and open:",
+ name).result
+ if file_path is not None:
+ if self.flist:
+ self.flist.open(file_path)
+ else:
+ self.io.loadfile(file_path)
return file_path
def open_class_browser(self, event=None):
@@ -673,12 +610,12 @@ class EditorWindow(object):
return
head, tail = os.path.split(filename)
base, ext = os.path.splitext(tail)
- from idlelib import ClassBrowser
- ClassBrowser.ClassBrowser(self.flist, base, [head])
+ from idlelib import browser
+ browser.ClassBrowser(self.flist, base, [head])
def open_path_browser(self, event=None):
- from idlelib import PathBrowser
- PathBrowser.PathBrowser(self.flist)
+ from idlelib import pathbrowser
+ pathbrowser.PathBrowser(self.flist)
def open_turtle_demo(self, event = None):
import subprocess
@@ -739,7 +676,7 @@ class EditorWindow(object):
def ResetColorizer(self):
"Update the color theme"
- # Called from self.filename_change_hook and from configDialog.py
+ # Called from self.filename_change_hook and from configdialog.py
self._rmcolorizer()
self._addcolorizer()
EditorWindow.color_config(self.text)
@@ -759,14 +696,14 @@ class EditorWindow(object):
def ResetFont(self):
"Update the text widgets' font if it is changed"
- # Called from configDialog.py
+ # Called from configdialog.py
self.text['font'] = idleConf.GetFont(self.root, 'main','EditorWindow')
def RemoveKeybindings(self):
"Remove the keybindings before they are changed."
- # Called from configDialog.py
- self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
+ # Called from configdialog.py
+ self.mainmenu.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
for event, keylist in keydefs.items():
self.text.event_delete(event, *keylist)
for extensionName in self.get_standard_extension_names():
@@ -777,8 +714,8 @@ class EditorWindow(object):
def ApplyKeybindings(self):
"Update the keybindings after they are changed"
- # Called from configDialog.py
- self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
+ # Called from configdialog.py
+ self.mainmenu.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
self.apply_bindings()
for extensionName in self.get_standard_extension_names():
xkeydefs = idleConf.GetExtensionBindings(extensionName)
@@ -786,7 +723,7 @@ class EditorWindow(object):
self.apply_bindings(xkeydefs)
#update menu accelerators
menuEventDict = {}
- for menu in self.Bindings.menudefs:
+ for menu in self.mainmenu.menudefs:
menuEventDict[menu[0]] = {}
for item in menu[1]:
if item:
@@ -813,7 +750,7 @@ class EditorWindow(object):
def set_notabs_indentwidth(self):
"Update the indentwidth if changed and not using tabs in this window"
- # Called from configDialog.py
+ # Called from configdialog.py
if not self.usetabs:
self.indentwidth = idleConf.GetOption('main', 'Indent','num-spaces',
type='int')
@@ -993,7 +930,7 @@ class EditorWindow(object):
def _close(self):
if self.io.filename:
self.update_recent_files_list(new_file=self.io.filename)
- WindowList.unregister_callback(self.postwindowsmenu)
+ windows.unregister_callback(self.postwindowsmenu)
self.unload_extensions()
self.io.close()
self.io = None
@@ -1031,12 +968,25 @@ class EditorWindow(object):
def get_standard_extension_names(self):
return idleConf.GetExtensions(editor_only=True)
+ extfiles = { # map config-extension section names to new file names
+ 'AutoComplete': 'autocomplete',
+ 'AutoExpand': 'autoexpand',
+ 'CallTips': 'calltips',
+ 'CodeContext': 'codecontext',
+ 'FormatParagraph': 'paragraph',
+ 'ParenMatch': 'parenmatch',
+ 'RstripExtension': 'rstrip',
+ 'ScriptBinding': 'runscript',
+ 'ZoomHeight': 'zoomheight',
+ }
+
def load_extension(self, name):
+ fname = self.extfiles.get(name, name)
try:
try:
- mod = importlib.import_module('.' + name, package=__package__)
+ mod = importlib.import_module('.' + fname, package=__package__)
except (ImportError, TypeError):
- mod = importlib.import_module(name)
+ mod = importlib.import_module(fname)
except ImportError:
print("\nFailed to import extension: ", name)
raise
@@ -1060,7 +1010,7 @@ class EditorWindow(object):
def apply_bindings(self, keydefs=None):
if keydefs is None:
- keydefs = self.Bindings.default_keydefs
+ keydefs = self.mainmenu.default_keydefs
text = self.text
text.keydefs = keydefs
for event, keylist in keydefs.items():
@@ -1073,9 +1023,9 @@ class EditorWindow(object):
Menus that are absent or None in self.menudict are ignored.
"""
if menudefs is None:
- menudefs = self.Bindings.menudefs
+ menudefs = self.mainmenu.menudefs
if keydefs is None:
- keydefs = self.Bindings.default_keydefs
+ keydefs = self.mainmenu.default_keydefs
menudict = self.menudict
text = self.text
for mname, entrylist in menudefs:
@@ -1302,7 +1252,7 @@ class EditorWindow(object):
# adjust indentation for continuations and block
# open/close first need to find the last stmt
lno = index2line(text.index('insert'))
- y = PyParse.Parser(self.indentwidth, self.tabwidth)
+ y = pyparse.Parser(self.indentwidth, self.tabwidth)
if not self.context_use_ps1:
for context in self.num_context_lines:
startat = max(lno - context, 1)
@@ -1326,22 +1276,22 @@ class EditorWindow(object):
y.set_lo(0)
c = y.get_continuation_type()
- if c != PyParse.C_NONE:
+ if c != pyparse.C_NONE:
# The current stmt hasn't ended yet.
- if c == PyParse.C_STRING_FIRST_LINE:
+ if c == pyparse.C_STRING_FIRST_LINE:
# after the first line of a string; do not indent at all
pass
- elif c == PyParse.C_STRING_NEXT_LINES:
+ elif c == pyparse.C_STRING_NEXT_LINES:
# inside a string which started before this line;
# just mimic the current indent
text.insert("insert", indent)
- elif c == PyParse.C_BRACKET:
+ elif c == pyparse.C_BRACKET:
# line up with the first (if any) element of the
# last open bracket structure; else indent one
# level beyond the indent of the line with the
# last open bracket
self.reindent_to(y.compute_bracket_indent())
- elif c == PyParse.C_BACKSLASH:
+ elif c == pyparse.C_BACKSLASH:
# if more than one line in this stmt already, just
# mimic the current indent; else if initial line
# has a start on an assignment stmt, indent to
@@ -1569,9 +1519,6 @@ def classifyws(s, tabwidth):
break
return raw, effective
-import tokenize
-_tokenize = tokenize
-del tokenize
class IndentSearcher(object):
@@ -1596,8 +1543,8 @@ class IndentSearcher(object):
return self.text.get(mark, mark + " lineend+1c")
def tokeneater(self, type, token, start, end, line,
- INDENT=_tokenize.INDENT,
- NAME=_tokenize.NAME,
+ INDENT=tokenize.INDENT,
+ NAME=tokenize.NAME,
OPENERS=('class', 'def', 'for', 'if', 'try', 'while')):
if self.finished:
pass
@@ -1608,19 +1555,19 @@ class IndentSearcher(object):
self.finished = 1
def run(self):
- save_tabsize = _tokenize.tabsize
- _tokenize.tabsize = self.tabwidth
+ save_tabsize = tokenize.tabsize
+ tokenize.tabsize = self.tabwidth
try:
try:
- tokens = _tokenize.generate_tokens(self.readline)
+ tokens = tokenize.generate_tokens(self.readline)
for token in tokens:
self.tokeneater(*token)
- except (_tokenize.TokenError, SyntaxError):
+ except (tokenize.TokenError, SyntaxError):
# since we cut off the tokenizer early, we can trigger
# spurious errors
pass
finally:
- _tokenize.tabsize = save_tabsize
+ tokenize.tabsize = save_tabsize
return self.blkopenline, self.indentedline
### end autoindent code ###
@@ -1644,7 +1591,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.isCocoaTk() and eventname in {
+ if (not keylist) or (macosx.isCocoaTk() and eventname in {
"<<open-module>>",
"<<goto-line>>",
"<<change-indentwidth>>"}):
@@ -1679,12 +1626,15 @@ def _editor_window(parent): # htest #
filename = sys.argv[1]
else:
filename = None
- macosxSupport.setupApp(root, None)
+ macosx.setupApp(root, None)
edit = EditorWindow(root=root, filename=filename)
edit.text.bind("<<close-all-windows>>", edit.close_event)
# Does not stop error, neither does following
# edit.text.bind("<<close-window>>", edit.close_event)
if __name__ == '__main__':
+ import unittest
+ unittest.main('idlelib.idle_test.test_editor', verbosity=2, exit=False)
+
from idlelib.idle_test.htest import run
run(_editor_window)
diff --git a/Lib/idlelib/FileList.py b/Lib/idlelib/filelist.py
index a9989a8624..f46ad7cd7e 100644
--- a/Lib/idlelib/FileList.py
+++ b/Lib/idlelib/filelist.py
@@ -1,4 +1,5 @@
import os
+
from tkinter import *
import tkinter.messagebox as tkMessageBox
@@ -6,7 +7,7 @@ import tkinter.messagebox as tkMessageBox
class FileList:
# N.B. this import overridden in PyShellFileList.
- from idlelib.EditorWindow import EditorWindow
+ from idlelib.editor import EditorWindow
def __init__(self, root):
self.root = root
@@ -111,7 +112,7 @@ class FileList:
def _test():
- from idlelib.EditorWindow import fixwordbreaks
+ from idlelib.editor import fixwordbreaks
import sys
root = Tk()
fixwordbreaks(root)
diff --git a/Lib/idlelib/GrepDialog.py b/Lib/idlelib/grep.py
index 721b231a9e..64ba28d94a 100644
--- a/Lib/idlelib/GrepDialog.py
+++ b/Lib/idlelib/grep.py
@@ -1,17 +1,19 @@
-import os
import fnmatch
-import re # for htest
+import os
import sys
-from tkinter import StringVar, BooleanVar, Checkbutton # for GrepDialog
-from tkinter import Tk, Text, Button, SEL, END # for htest
-from idlelib import SearchEngine
-from idlelib.SearchDialogBase import SearchDialogBase
-# Importing OutputWindow fails due to import loop
+
+from tkinter import StringVar, BooleanVar
+from tkinter.ttk import Checkbutton
+
+from idlelib.searchbase import SearchDialogBase
+from idlelib import searchengine
+
+# Importing OutputWindow here fails due to import loop
# EditorWindow -> GrepDialop -> OutputWindow -> EditorWindow
def grep(text, io=None, flist=None):
root = text._root()
- engine = SearchEngine.get(root)
+ engine = searchengine.get(root)
if not hasattr(engine, "_grepdialog"):
engine._grepdialog = GrepDialog(root, engine, flist)
dialog = engine._grepdialog
@@ -47,13 +49,10 @@ class GrepDialog(SearchDialogBase):
self.globent = self.make_entry("In files:", self.globvar)[0]
def create_other_buttons(self):
- f = self.make_frame()[0]
-
- btn = Checkbutton(f, anchor="w",
- variable=self.recvar,
+ btn = Checkbutton(
+ self.make_frame()[0], variable=self.recvar,
text="Recurse down subdirectories")
btn.pack(side="top", fill="both")
- btn.select()
def create_command_buttons(self):
SearchDialogBase.create_command_buttons(self)
@@ -67,7 +66,7 @@ class GrepDialog(SearchDialogBase):
if not path:
self.top.bell()
return
- from idlelib.OutputWindow import OutputWindow # leave here!
+ from idlelib.outwin import OutputWindow # leave here!
save = sys.stdout
try:
sys.stdout = OutputWindow(self.flist)
@@ -131,14 +130,16 @@ class GrepDialog(SearchDialogBase):
def _grep_dialog(parent): # htest #
- from idlelib.PyShell import PyShellFileList
- root = Tk()
- root.title("Test GrepDialog")
- width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
- root.geometry("+%d+%d"%(x, y + 150))
-
- flist = PyShellFileList(root)
- text = Text(root, height=5)
+ from tkinter import Toplevel, Text, SEL, END
+ from tkinter.ttk import Button
+ from idlelib.pyshell import PyShellFileList
+ top = Toplevel(parent)
+ top.title("Test GrepDialog")
+ x, y = map(int, parent.geometry().split('+')[1:])
+ top.geometry("+%d+%d" % (x, y + 175))
+
+ flist = PyShellFileList(top)
+ text = Text(top, height=5)
text.pack()
def show_grep_dialog():
@@ -146,9 +147,8 @@ def _grep_dialog(parent): # htest #
grep(text, flist=flist)
text.tag_remove(SEL, "1.0", END)
- button = Button(root, text="Show GrepDialog", command=show_grep_dialog)
+ button = Button(top, text="Show GrepDialog", command=show_grep_dialog)
button.pack()
- root.mainloop()
if __name__ == "__main__":
import unittest
diff --git a/Lib/idlelib/help.py b/Lib/idlelib/help.py
index a7008e94ed..77e01a31c0 100644
--- a/Lib/idlelib/help.py
+++ b/Lib/idlelib/help.py
@@ -4,7 +4,7 @@ Contents are subject to revision at any time, without notice.
Help => About IDLE: diplay About Idle dialog
-<to be moved here from aboutDialog.py>
+<to be moved here from help_about.py>
Help => IDLE Help: Display help.html with proper formatting.
@@ -25,15 +25,14 @@ copy_strip - Copy idle.html to help.html, rstripping each line.
show_idlehelp - Create HelpWindow. Called in EditorWindow.help_dialog.
"""
from html.parser import HTMLParser
-from os.path import abspath, dirname, isdir, isfile, join
+from os.path import abspath, dirname, isfile, join
from platform import python_version
-from tkinter import Tk, Toplevel, Frame, Text, Scrollbar, Menu, Menubutton
+
+from tkinter import Toplevel, Frame, Text, Menu
+from tkinter.ttk import Menubutton, Scrollbar
from tkinter import font as tkfont
-from idlelib.configHandler import idleConf
-use_ttk = False # until available to import
-if use_ttk:
- from tkinter.ttk import Menubutton
+from idlelib.config import idleConf
## About IDLE ##
@@ -197,15 +196,18 @@ class HelpFrame(Frame):
"Display html text, scrollbar, and toc."
def __init__(self, parent, filename):
Frame.__init__(self, parent)
- text = HelpText(self, filename)
+ # keep references to widgets for test access.
+ self.text = text = HelpText(self, filename)
self['background'] = text['background']
- scroll = Scrollbar(self, command=text.yview)
+ self.toc = toc = self.toc_menu(text)
+ self.scroll = scroll = Scrollbar(self, command=text.yview)
text['yscrollcommand'] = scroll.set
+
self.rowconfigure(0, weight=1)
self.columnconfigure(1, weight=1) # text
- self.toc_menu(text).grid(column=0, row=0, sticky='nw')
- text.grid(column=1, row=0, sticky='nsew')
- scroll.grid(column=2, row=0, sticky='ns')
+ toc.grid(row=0, column=0, sticky='nw')
+ text.grid(row=0, column=1, sticky='nsew')
+ scroll.grid(row=0, column=2, sticky='ns')
def toc_menu(self, text):
"Create table of contents as drop-down menu."
diff --git a/Lib/idlelib/help.txt b/Lib/idlelib/help.txt
deleted file mode 100644
index 89fbe0b41e..0000000000
--- a/Lib/idlelib/help.txt
+++ /dev/null
@@ -1,372 +0,0 @@
-This file, idlelib/help.txt is out-of-date and no longer used by Idle.
-It is deprecated and will be removed in the future, possibly in 3.6
-----------------------------------------------------------------------
-
-[See the end of this file for ** TIPS ** on using IDLE !!]
-
-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.
-
-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 (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
- Expand Word -- Expand the word you have typed to match another
- word in the same buffer; repeat to get a
- different expansion
- 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, open the
- configuration dialog by selecting Preferences
- in the application menu.
-
- ---
- 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.
-
-Window 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 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. 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,
- then delete the selection
- Copy -- Copy selection into system-wide clipboard
- Paste -- Insert system-wide clipboard into window
- ---
- Go to file/line -- Same as in Debug menu
-
-
-** TIPS **
-==========
-
-Additional Help Sources:
-
- Windows users can Google on zopeshelf.chm to access Zope help files in
- the Windows help format. The Additional Help Sources feature of the
- configuration GUI supports .chm, along with any other filetypes
- supported by your browser. Supply a Menu Item title, and enter the
- location in the Help File Path slot of the New Help Source dialog. Use
- http:// and/or www. to identify external URLs, or download the file and
- browse for its path on your machine using the Browse button.
-
- All users can access the extensive sources of help, including
- tutorials, available at 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 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. 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) 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.
- Alt-/ expand word is also useful to reduce typing.
-
- Command history:
-
- 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.
-
- Python default syntax colors:
-
- Keywords orange
- Builtins royal purple
- Strings green
- Comments red
- Definitions blue
-
- Shell default colors:
-
- Console output brown
- stdout blue
- stderr red
- stdin black
-
-Other preferences:
-
- The font preferences, 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.
-
- 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
diff --git a/Lib/idlelib/aboutDialog.py b/Lib/idlelib/help_about.py
index a8f75d2537..071bd3ec0f 100644
--- a/Lib/idlelib/aboutDialog.py
+++ b/Lib/idlelib/help_about.py
@@ -1,11 +1,13 @@
"""About Dialog for IDLE
"""
-
import os
from sys import version
+
from tkinter import *
-from idlelib import textView
+
+from idlelib import textview
+
class AboutDialog(Toplevel):
"""Modal about dialog for idle
@@ -135,17 +137,18 @@ class AboutDialog(Toplevel):
def display_printer_text(self, title, printer):
printer._Printer__setup()
text = '\n'.join(printer._Printer__lines)
- textView.view_text(self, title, text)
+ textview.view_text(self, title, text)
def display_file_text(self, title, filename, encoding=None):
fn = os.path.join(os.path.abspath(os.path.dirname(__file__)), filename)
- textView.view_file(self, title, fn, encoding)
+ textview.view_file(self, title, fn, encoding)
def Ok(self, event=None):
self.destroy()
+
if __name__ == '__main__':
import unittest
- unittest.main('idlelib.idle_test.test_helpabout', verbosity=2, exit=False)
+ unittest.main('idlelib.idle_test.test_help_about', verbosity=2, exit=False)
from idlelib.idle_test.htest import run
run(AboutDialog)
diff --git a/Lib/idlelib/IdleHistory.py b/Lib/idlelib/history.py
index 078af29053..56f53a0f2f 100644
--- a/Lib/idlelib/IdleHistory.py
+++ b/Lib/idlelib/history.py
@@ -1,11 +1,12 @@
"Implement Idle Shell history mechanism with History class"
-from idlelib.configHandler import idleConf
+from idlelib.config import idleConf
+
class History:
''' Implement Idle Shell history mechanism.
- store - Store source statement (called from PyShell.resetoutput).
+ store - Store source statement (called from pyshell.resetoutput).
fetch - Fetch stored statement matching prefix already entered.
history_next - Bound to <<history-next>> event (default Alt-N).
history_prev - Bound to <<history-prev>> event (default Alt-P).
@@ -99,6 +100,7 @@ class History:
self.pointer = None
self.prefix = None
+
if __name__ == "__main__":
from unittest import main
- main('idlelib.idle_test.test_idlehistory', verbosity=2, exit=False)
+ main('idlelib.idle_test.test_history', verbosity=2, exit=False)
diff --git a/Lib/idlelib/HyperParser.py b/Lib/idlelib/hyperparser.py
index 77cb057ce2..450a709c09 100644
--- a/Lib/idlelib/HyperParser.py
+++ b/Lib/idlelib/hyperparser.py
@@ -4,11 +4,10 @@ HyperParser uses PyParser. PyParser mostly gives information on the
proper indentation of code. HyperParser gives additional information on
the structure of code.
"""
-
-import string
from keyword import iskeyword
-from idlelib import PyParse
+import string
+from idlelib import pyparse
# all ASCII chars that may be in an identifier
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
@@ -30,7 +29,7 @@ class HyperParser:
self.editwin = editwin
self.text = text = editwin.text
- parser = PyParse.Parser(editwin.indentwidth, editwin.tabwidth)
+ parser = pyparse.Parser(editwin.indentwidth, editwin.tabwidth)
def index2line(index):
return int(float(index))
diff --git a/Lib/idlelib/idle.py b/Lib/idlelib/idle.py
index 141534dfe1..485d5a75a2 100644
--- a/Lib/idlelib/idle.py
+++ b/Lib/idlelib/idle.py
@@ -1,6 +1,7 @@
import os.path
import sys
+
# Enable running IDLE with idlelib in a non-standard location.
# This was once used to run development versions of IDLE.
# Because PEP 434 declared idle.py a public interface,
@@ -9,5 +10,5 @@ idlelib_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if idlelib_dir not in sys.path:
sys.path.insert(0, idlelib_dir)
-from idlelib.PyShell import main # This is subject to change
+from idlelib.pyshell import main # This is subject to change
main()
diff --git a/Lib/idlelib/idle.pyw b/Lib/idlelib/idle.pyw
index 142cb322ac..e73c049b70 100644
--- a/Lib/idlelib/idle.pyw
+++ b/Lib/idlelib/idle.pyw
@@ -1,10 +1,10 @@
try:
- import idlelib.PyShell
+ import idlelib.pyshell
except ImportError:
- # IDLE is not installed, but maybe PyShell is on sys.path:
- from . import PyShell
+ # IDLE is not installed, but maybe pyshell is on sys.path:
+ from . import pyshell
import os
- idledir = os.path.dirname(os.path.abspath(PyShell.__file__))
+ idledir = os.path.dirname(os.path.abspath(pyshell.__file__))
if idledir != os.getcwd():
# We're not in the IDLE directory, help the subprocess find run.py
pypath = os.environ.get('PYTHONPATH', '')
@@ -12,6 +12,6 @@ except ImportError:
os.environ['PYTHONPATH'] = pypath + ':' + idledir
else:
os.environ['PYTHONPATH'] = idledir
- PyShell.main()
+ pyshell.main()
else:
- idlelib.PyShell.main()
+ idlelib.pyshell.main()
diff --git a/Lib/idlelib/idle_test/__init__.py b/Lib/idlelib/idle_test/__init__.py
index 845c92d372..ad067b405c 100644
--- a/Lib/idlelib/idle_test/__init__.py
+++ b/Lib/idlelib/idle_test/__init__.py
@@ -1,6 +1,8 @@
'''idlelib.idle_test is a private implementation of test.test_idle,
which tests the IDLE application as part of the stdlib test suite.
Run IDLE tests alone with "python -m test.test_idle".
+Starting with Python 3.6, IDLE requires tcl/tk 8.5 or later.
+
This package and its contained modules are subject to change and
any direct use is at your own risk.
'''
diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py
index 58e62cb4e2..6f676ae865 100644
--- a/Lib/idlelib/idle_test/htest.py
+++ b/Lib/idlelib/idle_test/htest.py
@@ -59,19 +59,20 @@ msg: master window hints about testing the widget.
Modules and classes not being tested at the moment:
-PyShell.PyShellEditorWindow
-Debugger.Debugger
-AutoCompleteWindow.AutoCompleteWindow
-OutputWindow.OutputWindow (indirectly being tested with grep test)
+pyshell.PyShellEditorWindow
+debugger.Debugger
+autocomplete_w.AutoCompleteWindow
+outwin.OutputWindow (indirectly being tested with grep test)
'''
from importlib import import_module
-from idlelib.macosxSupport import _initializeTkVariantTests
import tkinter as tk
+from tkinter.ttk import Scrollbar
+tk.NoDefaultRoot()
AboutDialog_spec = {
- 'file': 'aboutDialog',
- 'kwds': {'title': 'aboutDialog test',
+ 'file': 'help_about',
+ 'kwds': {'title': 'help_about test',
'_htest': True,
},
'msg': "Test every button. Ensure Python, TK and IDLE versions "
@@ -79,14 +80,14 @@ AboutDialog_spec = {
}
_calltip_window_spec = {
- 'file': 'CallTipWindow',
+ 'file': 'calltip_w',
'kwds': {},
'msg': "Typing '(' should display a calltip.\n"
"Typing ') should hide the calltip.\n"
}
_class_browser_spec = {
- 'file': 'ClassBrowser',
+ 'file': 'browser',
'kwds': {},
'msg': "Inspect names of module, class(with superclass if "
"applicable), methods and functions.\nToggle nested items.\n"
@@ -95,7 +96,7 @@ _class_browser_spec = {
}
_color_delegator_spec = {
- 'file': 'ColorDelegator',
+ 'file': 'colorizer',
'kwds': {},
'msg': "The text is sample Python code.\n"
"Ensure components like comments, keywords, builtins,\n"
@@ -104,7 +105,7 @@ _color_delegator_spec = {
}
ConfigDialog_spec = {
- 'file': 'configDialog',
+ 'file': 'configdialog',
'kwds': {'title': 'ConfigDialogTest',
'_htest': True,},
'msg': "IDLE preferences dialog.\n"
@@ -121,7 +122,7 @@ ConfigDialog_spec = {
# TODO Improve message
_dyn_option_menu_spec = {
- 'file': 'dynOptionMenuWidget',
+ 'file': 'dynoption',
'kwds': {},
'msg': "Select one of the many options in the 'old option set'.\n"
"Click the button to change the option set.\n"
@@ -130,39 +131,15 @@ _dyn_option_menu_spec = {
# TODO edit wrapper
_editor_window_spec = {
- 'file': 'EditorWindow',
+ 'file': 'editor',
'kwds': {},
'msg': "Test editor functions of interest.\n"
"Best to close editor first."
}
-GetCfgSectionNameDialog_spec = {
- 'file': 'configSectionNameDialog',
- 'kwds': {'title':'Get Name',
- 'message':'Enter something',
- 'used_names': {'abc'},
- '_htest': True},
- 'msg': "After the text entered with [Ok] is stripped, <nothing>, "
- "'abc', or more that 30 chars are errors.\n"
- "Close 'Get Name' with a valid entry (printed to Shell), "
- "[Cancel], or [X]",
- }
-
-GetHelpSourceDialog_spec = {
- 'file': 'configHelpSourceEdit',
- 'kwds': {'title': 'Get helpsource',
- '_htest': True},
- 'msg': "Enter menu item name and help file path\n "
- "<nothing> and more than 30 chars are invalid menu item names.\n"
- "<nothing>, file does not exist are invalid path items.\n"
- "Test for incomplete web address for help file path.\n"
- "A valid entry will be printed to shell with [0k].\n"
- "[Cancel] will print None to shell",
- }
-
# Update once issue21519 is resolved.
GetKeysDialog_spec = {
- 'file': 'keybindingDialog',
+ 'file': 'config_key',
'kwds': {'title': 'Test keybindings',
'action': 'find-again',
'currentKeySequences': [''] ,
@@ -177,7 +154,7 @@ GetKeysDialog_spec = {
}
_grep_dialog_spec = {
- 'file': 'GrepDialog',
+ 'file': 'grep',
'kwds': {},
'msg': "Click the 'Show GrepDialog' button.\n"
"Test the various 'Find-in-files' functions.\n"
@@ -186,8 +163,24 @@ _grep_dialog_spec = {
"should open that file \nin a new EditorWindow."
}
+HelpSource_spec = {
+ 'file': 'query',
+ 'kwds': {'title': 'Help name and source',
+ 'menuitem': 'test',
+ 'filepath': __file__,
+ 'used_names': {'abc'},
+ '_htest': True},
+ 'msg': "Enter menu item name and help file path\n"
+ "'', > than 30 chars, and 'abc' are invalid menu item names.\n"
+ "'' and file does not exist are invalid path items.\n"
+ "Any url ('www...', 'http...') is accepted.\n"
+ "Test Browse with and without path, as cannot unittest.\n"
+ "[Ok] or <Return> prints valid entry to shell\n"
+ "[Cancel] or <Escape> prints None to shell"
+ }
+
_io_binding_spec = {
- 'file': 'IOBinding',
+ 'file': 'iomenu',
'kwds': {},
'msg': "Test the following bindings.\n"
"<Control-o> to open file from dialog.\n"
@@ -200,7 +193,7 @@ _io_binding_spec = {
}
_multi_call_spec = {
- 'file': 'MultiCall',
+ 'file': 'multicall',
'kwds': {},
'msg': "The following actions should trigger a print to console or IDLE"
" Shell.\nEntering and leaving the text area, key entry, "
@@ -210,14 +203,14 @@ _multi_call_spec = {
}
_multistatus_bar_spec = {
- 'file': 'MultiStatusBar',
+ 'file': 'statusbar',
'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',
+ 'file': 'debugobj',
'kwds': {},
'msg': "Double click on items upto the lowest level.\n"
"Attributes of the objects and related information "
@@ -225,7 +218,7 @@ _object_browser_spec = {
}
_path_browser_spec = {
- 'file': 'PathBrowser',
+ 'file': 'pathbrowser',
'kwds': {},
'msg': "Test for correct display of all paths in sys.path.\n"
"Toggle nested items upto the lowest level.\n"
@@ -234,7 +227,7 @@ _path_browser_spec = {
}
_percolator_spec = {
- 'file': 'Percolator',
+ '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"
@@ -244,8 +237,20 @@ _percolator_spec = {
"Test for actions like text entry, and removal."
}
+Query_spec = {
+ 'file': 'query',
+ 'kwds': {'title': 'Query',
+ 'message': 'Enter something',
+ 'text0': 'Go',
+ '_htest': True},
+ 'msg': "Enter with <Return> or [Ok]. Print valid entry to Shell\n"
+ "Blank line, after stripping, is ignored\n"
+ "Close dialog with valid entry, <Escape>, [Cancel], [X]"
+ }
+
+
_replace_dialog_spec = {
- 'file': 'ReplaceDialog',
+ 'file': 'replace',
'kwds': {},
'msg': "Click the 'Replace' button.\n"
"Test various replace options in the 'Replace dialog'.\n"
@@ -253,15 +258,22 @@ _replace_dialog_spec = {
}
_search_dialog_spec = {
- 'file': 'SearchDialog',
+ 'file': 'search',
'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'."
}
+_searchbase_spec = {
+ 'file': 'searchbase',
+ 'kwds': {},
+ 'msg': "Check the appearance of the base search dialog\n"
+ "Its only action is to close."
+ }
+
_scrolled_list_spec = {
- 'file': 'ScrolledList',
+ 'file': 'scrolledlist',
'kwds': {},
'msg': "You should see a scrollable list of items\n"
"Selecting (clicking) or double clicking an item "
@@ -277,7 +289,7 @@ show_idlehelp_spec = {
}
_stack_viewer_spec = {
- 'file': 'StackViewer',
+ 'file': 'stackviewer',
'kwds': {},
'msg': "A stacktrace for a NameError exception.\n"
"Expand 'idlelib ...' and '<locals>'.\n"
@@ -295,8 +307,8 @@ _tabbed_pages_spec = {
}
TextViewer_spec = {
- 'file': 'textView',
- 'kwds': {'title': 'Test textView',
+ '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"
@@ -304,21 +316,21 @@ TextViewer_spec = {
}
_tooltip_spec = {
- 'file': 'ToolTip',
+ 'file': 'tooltip',
'kwds': {},
'msg': "Place mouse cursor over both the buttons\n"
"A tooltip should appear with some text."
}
_tree_widget_spec = {
- 'file': 'TreeWidget',
+ 'file': 'tree',
'kwds': {},
'msg': "The canvas is scrollable.\n"
"Click on folders upto to the lowest level."
}
_undo_delegator_spec = {
- 'file': 'UndoDelegator',
+ 'file': 'undo',
'kwds': {},
'msg': "Click [Undo] to undo any action.\n"
"Click [Redo] to redo any action.\n"
@@ -327,7 +339,7 @@ _undo_delegator_spec = {
}
_widget_redirector_spec = {
- 'file': 'WidgetRedirector',
+ 'file': 'redirector',
'kwds': {},
'msg': "Every text insert should be printed to the console."
"or the IDLE shell."
@@ -337,14 +349,13 @@ 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)
+ scrollbar = 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)
@@ -365,11 +376,11 @@ def run(*tests):
test = getattr(mod, test_name)
test_list.append((test_spec, test))
- test_name = tk.StringVar('')
+ test_name = tk.StringVar(root)
callable_object = None
test_kwds = None
- def next():
+ def next_test():
nonlocal test_name, callable_object, test_kwds
if len(test_list) == 1:
@@ -384,20 +395,26 @@ def run(*tests):
text.insert("1.0",test_spec['msg'])
text.configure(state='disabled') # preserve read-only property
- def run_test():
+ def run_test(_=None):
widget = callable_object(**test_kwds)
try:
print(widget.result)
except AttributeError:
pass
- button = tk.Button(root, textvariable=test_name, command=run_test)
+ def close(_=None):
+ root.destroy()
+
+ button = tk.Button(root, textvariable=test_name,
+ default='active', command=run_test)
+ next_button = tk.Button(root, text="Next", command=next_test)
button.pack()
- next_button = tk.Button(root, text="Next", command=next)
next_button.pack()
+ next_button.focus_set()
+ root.bind('<Key-Return>', run_test)
+ root.bind('<Key-Escape>', close)
- next()
-
+ next_test()
root.mainloop()
if __name__ == '__main__':
diff --git a/Lib/idlelib/idle_test/mock_idle.py b/Lib/idlelib/idle_test/mock_idle.py
index 1672a3413e..c7b49ef02b 100644
--- a/Lib/idlelib/idle_test/mock_idle.py
+++ b/Lib/idlelib/idle_test/mock_idle.py
@@ -33,7 +33,7 @@ class Func:
class Editor:
- '''Minimally imitate EditorWindow.EditorWindow class.
+ '''Minimally imitate editor.EditorWindow class.
'''
def __init__(self, flist=None, filename=None, key=None, root=None):
self.text = Text()
@@ -46,7 +46,7 @@ class Editor:
class UndoDelegator:
- '''Minimally imitate UndoDelegator,UndoDelegator class.
+ '''Minimally imitate undo.UndoDelegator class.
'''
# A real undo block is only needed for user interaction.
def undo_block_start(*args):
diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py
index 5fc899dcd4..f3f2dea424 100644
--- a/Lib/idlelib/idle_test/test_autocomplete.py
+++ b/Lib/idlelib/idle_test/test_autocomplete.py
@@ -1,9 +1,13 @@
+''' Test autocomplete and autocomple_w
+
+Coverage of autocomple: 56%
+'''
import unittest
from test.support import requires
from tkinter import Tk, Text
-import idlelib.AutoComplete as ac
-import idlelib.AutoCompleteWindow as acw
+import idlelib.autocomplete as ac
+import idlelib.autocomplete_w as acw
from idlelib.idle_test.mock_idle import Func
from idlelib.idle_test.mock_tk import Event
@@ -91,6 +95,11 @@ class AutoCompleteTest(unittest.TestCase):
self.assertIsNone(autocomplete.autocomplete_event(ev))
del ev.mc_state
+ # Test that tab after whitespace is ignored.
+ self.text.insert('1.0', ' """Docstring.\n ')
+ self.assertIsNone(autocomplete.autocomplete_event(ev))
+ self.text.delete('1.0', 'end')
+
# If autocomplete window is open, complete() method is called
self.text.insert('1.0', 're.')
# This must call autocomplete._make_autocomplete_window()
diff --git a/Lib/idlelib/idle_test/test_autoexpand.py b/Lib/idlelib/idle_test/test_autoexpand.py
index d2a3156dca..ae8186cdc4 100644
--- a/Lib/idlelib/idle_test/test_autoexpand.py
+++ b/Lib/idlelib/idle_test/test_autoexpand.py
@@ -1,9 +1,9 @@
-"""Unit tests for idlelib.AutoExpand"""
+"""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
+from idlelib.autoexpand import AutoExpand
class Dummy_Editwin:
@@ -22,6 +22,7 @@ class AutoExpandTest(unittest.TestCase):
else:
cls.text = Text()
cls.auto_expand = AutoExpand(Dummy_Editwin(cls.text))
+ cls.auto_expand.bell = lambda: None
@classmethod
def tearDownClass(cls):
@@ -137,5 +138,6 @@ class AutoExpandTest(unittest.TestCase):
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 b2a733cc0d..0b11602ca9 100644
--- a/Lib/idlelib/idle_test/test_calltips.py
+++ b/Lib/idlelib/idle_test/test_calltips.py
@@ -1,5 +1,5 @@
import unittest
-import idlelib.CallTips as ct
+import idlelib.calltips as ct
import textwrap
import types
diff --git a/Lib/idlelib/idle_test/test_colorizer.py b/Lib/idlelib/idle_test/test_colorizer.py
new file mode 100644
index 0000000000..238bc3e114
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_colorizer.py
@@ -0,0 +1,56 @@
+'''Test idlelib/colorizer.py
+
+Perform minimal sanity checks that module imports and some things run.
+
+Coverage 22%.
+'''
+from idlelib import colorizer # always test import
+from test.support import requires
+from tkinter import Tk, Text
+import unittest
+
+
+class FunctionTest(unittest.TestCase):
+
+ def test_any(self):
+ self.assertTrue(colorizer.any('test', ('a', 'b')))
+
+ def test_make_pat(self):
+ self.assertTrue(colorizer.make_pat())
+
+
+class ColorConfigTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ cls.text = Text(cls.root)
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.text
+ cls.root.destroy()
+ del cls.root
+
+ def test_colorizer(self):
+ colorizer.color_config(self.text)
+
+class ColorDelegatorTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.root
+
+ def test_colorizer(self):
+ colorizer.ColorDelegator()
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py
new file mode 100644
index 0000000000..a3fa1a341a
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_config.py
@@ -0,0 +1,160 @@
+'''Test idlelib.config.
+
+Much is tested by opening config dialog live or in test_configdialog.
+Coverage: 27%
+'''
+from sys import modules
+from test.support import captured_stderr
+from tkinter import Tk
+import unittest
+from idlelib import config
+
+# Tests should not depend on fortuitous user configurations.
+# They must not affect actual user .cfg files.
+# Replace user parsers with empty parsers that cannot be saved.
+
+idleConf = config.idleConf
+usercfg = idleConf.userCfg
+testcfg = {}
+usermain = testcfg['main'] = config.IdleUserConfParser('') # filename
+userhigh = testcfg['highlight'] = config.IdleUserConfParser('')
+userkeys = testcfg['keys'] = config.IdleUserConfParser('')
+
+def setUpModule():
+ idleConf.userCfg = testcfg
+
+def tearDownModule():
+ idleConf.userCfg = usercfg
+
+
+class CurrentColorKeysTest(unittest.TestCase):
+ """ Test colorkeys function with user config [Theme] and [Keys] patterns.
+
+ colorkeys = config.IdleConf.current_colors_and_keys
+ Test all patterns written by IDLE and some errors
+ Item 'default' should really be 'builtin' (versus 'custom).
+ """
+ colorkeys = idleConf.current_colors_and_keys
+ default_theme = 'IDLE Classic'
+ default_keys = idleConf.default_keys()
+
+ def test_old_builtin_theme(self):
+ # On initial installation, user main is blank.
+ self.assertEqual(self.colorkeys('Theme'), self.default_theme)
+ # For old default, name2 must be blank.
+ usermain.read_string('''
+ [Theme]
+ default = True
+ ''')
+ # IDLE omits 'name' for default old builtin theme.
+ self.assertEqual(self.colorkeys('Theme'), self.default_theme)
+ # IDLE adds 'name' for non-default old builtin theme.
+ usermain['Theme']['name'] = 'IDLE New'
+ self.assertEqual(self.colorkeys('Theme'), 'IDLE New')
+ # Erroneous non-default old builtin reverts to default.
+ usermain['Theme']['name'] = 'non-existent'
+ self.assertEqual(self.colorkeys('Theme'), self.default_theme)
+ usermain.remove_section('Theme')
+
+ def test_new_builtin_theme(self):
+ # IDLE writes name2 for new builtins.
+ usermain.read_string('''
+ [Theme]
+ default = True
+ name2 = IDLE Dark
+ ''')
+ self.assertEqual(self.colorkeys('Theme'), 'IDLE Dark')
+ # Leftover 'name', not removed, is ignored.
+ usermain['Theme']['name'] = 'IDLE New'
+ self.assertEqual(self.colorkeys('Theme'), 'IDLE Dark')
+ # Erroneous non-default new builtin reverts to default.
+ usermain['Theme']['name2'] = 'non-existent'
+ self.assertEqual(self.colorkeys('Theme'), self.default_theme)
+ usermain.remove_section('Theme')
+
+ def test_user_override_theme(self):
+ # Erroneous custom name (no definition) reverts to default.
+ usermain.read_string('''
+ [Theme]
+ default = False
+ name = Custom Dark
+ ''')
+ self.assertEqual(self.colorkeys('Theme'), self.default_theme)
+ # Custom name is valid with matching Section name.
+ userhigh.read_string('[Custom Dark]\na=b')
+ self.assertEqual(self.colorkeys('Theme'), 'Custom Dark')
+ # Name2 is ignored.
+ usermain['Theme']['name2'] = 'non-existent'
+ self.assertEqual(self.colorkeys('Theme'), 'Custom Dark')
+ usermain.remove_section('Theme')
+ userhigh.remove_section('Custom Dark')
+
+ def test_old_builtin_keys(self):
+ # On initial installation, user main is blank.
+ self.assertEqual(self.colorkeys('Keys'), self.default_keys)
+ # For old default, name2 must be blank, name is always used.
+ usermain.read_string('''
+ [Keys]
+ default = True
+ name = IDLE Classic Unix
+ ''')
+ self.assertEqual(self.colorkeys('Keys'), 'IDLE Classic Unix')
+ # Erroneous non-default old builtin reverts to default.
+ usermain['Keys']['name'] = 'non-existent'
+ self.assertEqual(self.colorkeys('Keys'), self.default_keys)
+ usermain.remove_section('Keys')
+
+ def test_new_builtin_keys(self):
+ # IDLE writes name2 for new builtins.
+ usermain.read_string('''
+ [Keys]
+ default = True
+ name2 = IDLE Modern Unix
+ ''')
+ self.assertEqual(self.colorkeys('Keys'), 'IDLE Modern Unix')
+ # Leftover 'name', not removed, is ignored.
+ usermain['Keys']['name'] = 'IDLE Classic Unix'
+ self.assertEqual(self.colorkeys('Keys'), 'IDLE Modern Unix')
+ # Erroneous non-default new builtin reverts to default.
+ usermain['Keys']['name2'] = 'non-existent'
+ self.assertEqual(self.colorkeys('Keys'), self.default_keys)
+ usermain.remove_section('Keys')
+
+ def test_user_override_keys(self):
+ # Erroneous custom name (no definition) reverts to default.
+ usermain.read_string('''
+ [Keys]
+ default = False
+ name = Custom Keys
+ ''')
+ self.assertEqual(self.colorkeys('Keys'), self.default_keys)
+ # Custom name is valid with matching Section name.
+ userkeys.read_string('[Custom Keys]\na=b')
+ self.assertEqual(self.colorkeys('Keys'), 'Custom Keys')
+ # Name2 is ignored.
+ usermain['Keys']['name2'] = 'non-existent'
+ self.assertEqual(self.colorkeys('Keys'), 'Custom Keys')
+ usermain.remove_section('Keys')
+ userkeys.remove_section('Custom Keys')
+
+
+class WarningTest(unittest.TestCase):
+
+ def test_warn(self):
+ Equal = self.assertEqual
+ config._warned = set()
+ with captured_stderr() as stderr:
+ config._warn('warning', 'key')
+ Equal(config._warned, {('warning','key')})
+ Equal(stderr.getvalue(), 'warning'+'\n')
+ with captured_stderr() as stderr:
+ config._warn('warning', 'key')
+ Equal(stderr.getvalue(), '')
+ with captured_stderr() as stderr:
+ config._warn('warn2', 'yek')
+ Equal(config._warned, {('warning','key'), ('warn2','yek')})
+ Equal(stderr.getvalue(), 'warn2'+'\n')
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_config_help.py b/Lib/idlelib/idle_test/test_config_help.py
deleted file mode 100644
index 664f8edc62..0000000000
--- a/Lib/idlelib/idle_test/test_config_help.py
+++ /dev/null
@@ -1,106 +0,0 @@
-"""Unittests for idlelib.configHelpSourceEdit"""
-import unittest
-from idlelib.idle_test.mock_tk import Var, Mbox, Entry
-from idlelib import configHelpSourceEdit as help_dialog_module
-
-help_dialog = help_dialog_module.GetHelpSourceDialog
-
-
-class Dummy_help_dialog:
- # Mock for testing the following methods of help_dialog
- menu_ok = help_dialog.menu_ok
- path_ok = help_dialog.path_ok
- ok = help_dialog.ok
- cancel = help_dialog.cancel
- # Attributes, constant or variable, needed for tests
- menu = Var()
- entryMenu = Entry()
- path = Var()
- entryPath = Entry()
- result = None
- destroyed = False
-
- def destroy(self):
- self.destroyed = True
-
-
-# menu_ok and path_ok call Mbox.showerror if menu and path are not ok.
-orig_mbox = help_dialog_module.tkMessageBox
-showerror = Mbox.showerror
-
-
-class ConfigHelpTest(unittest.TestCase):
- dialog = Dummy_help_dialog()
-
- @classmethod
- def setUpClass(cls):
- help_dialog_module.tkMessageBox = Mbox
-
- @classmethod
- def tearDownClass(cls):
- help_dialog_module.tkMessageBox = orig_mbox
-
- def test_blank_menu(self):
- self.dialog.menu.set('')
- self.assertFalse(self.dialog.menu_ok())
- self.assertEqual(showerror.title, 'Menu Item Error')
- self.assertIn('No', showerror.message)
-
- def test_long_menu(self):
- self.dialog.menu.set('hello' * 10)
- self.assertFalse(self.dialog.menu_ok())
- self.assertEqual(showerror.title, 'Menu Item Error')
- self.assertIn('long', showerror.message)
-
- def test_good_menu(self):
- self.dialog.menu.set('help')
- showerror.title = 'No Error' # should not be called
- self.assertTrue(self.dialog.menu_ok())
- self.assertEqual(showerror.title, 'No Error')
-
- def test_blank_path(self):
- self.dialog.path.set('')
- self.assertFalse(self.dialog.path_ok())
- self.assertEqual(showerror.title, 'File Path Error')
- self.assertIn('No', showerror.message)
-
- def test_invalid_file_path(self):
- self.dialog.path.set('foobar' * 100)
- self.assertFalse(self.dialog.path_ok())
- self.assertEqual(showerror.title, 'File Path Error')
- self.assertIn('not exist', showerror.message)
-
- def test_invalid_url_path(self):
- self.dialog.path.set('ww.foobar.com')
- self.assertFalse(self.dialog.path_ok())
- self.assertEqual(showerror.title, 'File Path Error')
- self.assertIn('not exist', showerror.message)
-
- self.dialog.path.set('htt.foobar.com')
- self.assertFalse(self.dialog.path_ok())
- self.assertEqual(showerror.title, 'File Path Error')
- self.assertIn('not exist', showerror.message)
-
- def test_good_path(self):
- self.dialog.path.set('https://docs.python.org')
- showerror.title = 'No Error' # should not be called
- self.assertTrue(self.dialog.path_ok())
- self.assertEqual(showerror.title, 'No Error')
-
- def test_ok(self):
- self.dialog.destroyed = False
- self.dialog.menu.set('help')
- self.dialog.path.set('https://docs.python.org')
- self.dialog.ok()
- self.assertEqual(self.dialog.result, ('help',
- 'https://docs.python.org'))
- self.assertTrue(self.dialog.destroyed)
-
- def test_cancel(self):
- self.dialog.destroyed = False
- self.dialog.cancel()
- self.assertEqual(self.dialog.result, None)
- self.assertTrue(self.dialog.destroyed)
-
-if __name__ == '__main__':
- unittest.main(verbosity=2, exit=False)
diff --git a/Lib/idlelib/idle_test/test_config_key.py b/Lib/idlelib/idle_test/test_config_key.py
new file mode 100644
index 0000000000..ee3f2c835c
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_config_key.py
@@ -0,0 +1,33 @@
+''' Test idlelib.config_key.
+
+Coverage: 56% from creating and closing dialog.
+'''
+from idlelib import config_key
+from test.support import requires
+requires('gui')
+import unittest
+from tkinter import Tk, Text
+
+
+class GetKeysTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.root = Tk()
+ cls.root.withdraw()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.update() # Stop "can't run event command" warning.
+ cls.root.destroy()
+ del cls.root
+
+
+ def test_init(self):
+ dia = config_key.GetKeysDialog(
+ self.root, 'test', '<<Test>>', ['<Key-F12>'], _utest=True)
+ dia.Cancel()
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_config_name.py b/Lib/idlelib/idle_test/test_config_name.py
deleted file mode 100644
index 40e72b9551..0000000000
--- a/Lib/idlelib/idle_test/test_config_name.py
+++ /dev/null
@@ -1,75 +0,0 @@
-"""Unit tests for idlelib.configSectionNameDialog"""
-import unittest
-from idlelib.idle_test.mock_tk import Var, Mbox
-from idlelib import configSectionNameDialog as name_dialog_module
-
-name_dialog = name_dialog_module.GetCfgSectionNameDialog
-
-class Dummy_name_dialog:
- # Mock for testing the following methods of name_dialog
- name_ok = name_dialog.name_ok
- Ok = name_dialog.Ok
- Cancel = name_dialog.Cancel
- # Attributes, constant or variable, needed for tests
- used_names = ['used']
- name = Var()
- result = None
- destroyed = False
- def destroy(self):
- self.destroyed = True
-
-# name_ok calls Mbox.showerror if name is not ok
-orig_mbox = name_dialog_module.tkMessageBox
-showerror = Mbox.showerror
-
-class ConfigNameTest(unittest.TestCase):
- dialog = Dummy_name_dialog()
-
- @classmethod
- def setUpClass(cls):
- name_dialog_module.tkMessageBox = Mbox
-
- @classmethod
- def tearDownClass(cls):
- name_dialog_module.tkMessageBox = orig_mbox
-
- def test_blank_name(self):
- self.dialog.name.set(' ')
- self.assertEqual(self.dialog.name_ok(), '')
- self.assertEqual(showerror.title, 'Name Error')
- self.assertIn('No', showerror.message)
-
- def test_used_name(self):
- self.dialog.name.set('used')
- self.assertEqual(self.dialog.name_ok(), '')
- self.assertEqual(showerror.title, 'Name Error')
- self.assertIn('use', showerror.message)
-
- def test_long_name(self):
- self.dialog.name.set('good'*8)
- self.assertEqual(self.dialog.name_ok(), '')
- self.assertEqual(showerror.title, 'Name Error')
- self.assertIn('too long', showerror.message)
-
- def test_good_name(self):
- self.dialog.name.set(' good ')
- showerror.title = 'No Error' # should not be called
- self.assertEqual(self.dialog.name_ok(), 'good')
- self.assertEqual(showerror.title, 'No Error')
-
- def test_ok(self):
- self.dialog.destroyed = False
- self.dialog.name.set('good')
- self.dialog.Ok()
- self.assertEqual(self.dialog.result, 'good')
- self.assertTrue(self.dialog.destroyed)
-
- def test_cancel(self):
- self.dialog.destroyed = False
- self.dialog.Cancel()
- self.assertEqual(self.dialog.result, '')
- self.assertTrue(self.dialog.destroyed)
-
-
-if __name__ == '__main__':
- unittest.main(verbosity=2, exit=False)
diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py
index 5c09790c64..3f94493733 100644
--- a/Lib/idlelib/idle_test/test_configdialog.py
+++ b/Lib/idlelib/idle_test/test_configdialog.py
@@ -1,32 +1,127 @@
-'''Test idlelib.configDialog.
+"""Test idlelib.configdialog.
-Coverage: 46% just by creating dialog.
-The other half is code for working with user customizations.
-'''
-from idlelib.configDialog import ConfigDialog # always test import
+Half the class creates dialog, half works with user customizations.
+Coverage: 46% just by creating dialog, 56% with current tests.
+"""
+from idlelib.configdialog import ConfigDialog, idleConf # test import
from test.support import requires
requires('gui')
from tkinter import Tk
import unittest
-from idlelib import macosxSupport as macosx
+import idlelib.config as config
-class ConfigDialogTest(unittest.TestCase):
+# Tests should not depend on fortuitous user configurations.
+# They must not affect actual user .cfg files.
+# Use solution from test_config: empty parsers with no filename.
+usercfg = idleConf.userCfg
+testcfg = {
+ 'main': config.IdleUserConfParser(''),
+ 'highlight': config.IdleUserConfParser(''),
+ 'keys': config.IdleUserConfParser(''),
+ 'extensions': config.IdleUserConfParser(''),
+}
- @classmethod
- def setUpClass(cls):
- cls.root = Tk()
- cls.root.withdraw()
- macosx._initializeTkVariantTests(cls.root)
+# ConfigDialog.changedItems is a 3-level hierarchical dictionary of
+# pending changes that mirrors the multilevel user config dict.
+# For testing, record args in a list for comparison with expected.
+changes = []
+class TestDialog(ConfigDialog):
+ def AddChangedItem(self, *args):
+ changes.append(args)
- @classmethod
- def tearDownClass(cls):
- cls.root.update_idletasks()
- cls.root.destroy()
- del cls.root
+def setUpModule():
+ global root, configure
+ idleConf.userCfg = testcfg
+ root = Tk()
+ root.withdraw()
+ configure = TestDialog(root, 'Test', _utest=True)
- def test_dialog(self):
- d = ConfigDialog(self.root, 'Test', _utest=True)
- d.remove_var_callbacks()
+
+def tearDownModule():
+ global root, configure
+ idleConf.userCfg = testcfg
+ configure.remove_var_callbacks()
+ del configure
+ root.update_idletasks()
+ root.destroy()
+ del root
+
+
+class FontTabTest(unittest.TestCase):
+
+
+ def setUp(self):
+ changes.clear()
+
+ def test_font(self):
+ # Set values guaranteed not to be defaults.
+ dfont = idleConf.GetFont(root, 'main', 'EditorWindow')
+ dsize = str(dfont[1])
+ dbold = dfont[2] == 'bold'
+ configure.fontName.set('Test Font')
+ expected = [
+ ('main', 'EditorWindow', 'font', 'Test Font'),
+ ('main', 'EditorWindow', 'font-size', dsize),
+ ('main', 'EditorWindow', 'font-bold', dbold)]
+ self.assertEqual(changes, expected)
+ changes.clear()
+ configure.fontSize.set(20)
+ expected = [
+ ('main', 'EditorWindow', 'font', 'Test Font'),
+ ('main', 'EditorWindow', 'font-size', '20'),
+ ('main', 'EditorWindow', 'font-bold', dbold)]
+ self.assertEqual(changes, expected)
+ changes.clear()
+ configure.fontBold.set(not dbold)
+ expected = [
+ ('main', 'EditorWindow', 'font', 'Test Font'),
+ ('main', 'EditorWindow', 'font-size', '20'),
+ ('main', 'EditorWindow', 'font-bold', not dbold)]
+ self.assertEqual(changes, expected)
+
+ #def test_sample(self): pass # TODO
+
+ def test_tabspace(self):
+ configure.spaceNum.set(6)
+ self.assertEqual(changes, [('main', 'Indent', 'num-spaces', 6)])
+
+
+class HighlightTest(unittest.TestCase):
+
+ def setUp(self):
+ changes.clear()
+
+ #def test_colorchoose(self): pass # TODO
+
+
+class KeysTest(unittest.TestCase):
+
+ def setUp(self):
+ changes.clear()
+
+
+class GeneralTest(unittest.TestCase):
+
+ def setUp(self):
+ changes.clear()
+
+ def test_startup(self):
+ configure.radioStartupEdit.invoke()
+ self.assertEqual(changes,
+ [('main', 'General', 'editor-on-startup', 1)])
+
+ def test_autosave(self):
+ configure.radioSaveAuto.invoke()
+ self.assertEqual(changes, [('main', 'General', 'autosave', 1)])
+
+ def test_editor_size(self):
+ configure.entryWinHeight.insert(0, '1')
+ self.assertEqual(changes, [('main', 'EditorWindow', 'height', '140')])
+ changes.clear()
+ configure.entryWinWidth.insert(0, '1')
+ self.assertEqual(changes, [('main', 'EditorWindow', 'width', '180')])
+
+ #def test_help_sources(self): pass # TODO
if __name__ == '__main__':
diff --git a/Lib/idlelib/idle_test/test_debugger.py b/Lib/idlelib/idle_test/test_debugger.py
new file mode 100644
index 0000000000..bcba9a45c1
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_debugger.py
@@ -0,0 +1,29 @@
+''' Test idlelib.debugger.
+
+Coverage: 19%
+'''
+from idlelib import debugger
+from test.support import requires
+requires('gui')
+import unittest
+from tkinter import Tk
+
+
+class NameSpaceTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.root = Tk()
+ cls.root.withdraw()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.root
+
+ def test_init(self):
+ debugger.NamespaceViewer(self.root, 'Test')
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_delegator.py b/Lib/idlelib/idle_test/test_delegator.py
index 1f0baa9c0d..85624fbc12 100644
--- a/Lib/idlelib/idle_test/test_delegator.py
+++ b/Lib/idlelib/idle_test/test_delegator.py
@@ -1,5 +1,5 @@
import unittest
-from idlelib.Delegator import Delegator
+from idlelib.delegator import Delegator
class DelegatorTest(unittest.TestCase):
diff --git a/Lib/idlelib/idle_test/test_editmenu.py b/Lib/idlelib/idle_test/test_editmenu.py
index a258e29e0f..17eb25c4b4 100644
--- a/Lib/idlelib/idle_test/test_editmenu.py
+++ b/Lib/idlelib/idle_test/test_editmenu.py
@@ -5,8 +5,9 @@ Edit modules have their own test files files
from test.support import requires
requires('gui')
import tkinter as tk
+from tkinter import ttk
import unittest
-from idlelib import PyShell
+from idlelib import pyshell
class PasteTest(unittest.TestCase):
'''Test pasting into widgets that allow pasting.
@@ -16,17 +17,18 @@ class PasteTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.root = root = tk.Tk()
- root.withdraw()
- PyShell.fix_x11_paste(root)
+ cls.root.withdraw()
+ pyshell.fix_x11_paste(root)
cls.text = tk.Text(root)
cls.entry = tk.Entry(root)
+ cls.tentry = ttk.Entry(root)
cls.spin = tk.Spinbox(root)
root.clipboard_clear()
root.clipboard_append('two')
@classmethod
def tearDownClass(cls):
- del cls.text, cls.entry, cls.spin
+ del cls.text, cls.entry, cls.tentry
cls.root.clipboard_clear()
cls.root.update_idletasks()
cls.root.destroy()
@@ -44,16 +46,16 @@ class PasteTest(unittest.TestCase):
def test_paste_entry(self):
"Test pasting into an entry with and without a selection."
- # On 3.6, generated <<Paste>> fails without empty select range
- # for 'no selection'. Live widget works fine.
- entry = self.entry
- for end, ans in (0, 'onetwo'), ('end', 'two'):
- with self.subTest(entry=entry, end=end, ans=ans):
- entry.delete(0, 'end')
- entry.insert(0, 'one')
- entry.select_range(0, end) # see note
- entry.event_generate('<<Paste>>')
- self.assertEqual(entry.get(), ans)
+ # Generated <<Paste>> fails for tk entry without empty select
+ # range for 'no selection'. Live widget works fine.
+ for entry in self.entry, self.tentry:
+ for end, ans in (0, 'onetwo'), ('end', 'two'):
+ with self.subTest(entry=entry, end=end, ans=ans):
+ entry.delete(0, 'end')
+ entry.insert(0, 'one')
+ entry.select_range(0, end)
+ entry.event_generate('<<Paste>>')
+ self.assertEqual(entry.get(), ans)
def test_paste_spin(self):
"Test pasting into a spinbox with and without a selection."
diff --git a/Lib/idlelib/idle_test/test_editor.py b/Lib/idlelib/idle_test/test_editor.py
index a31d26d25d..e9d29d45b8 100644
--- a/Lib/idlelib/idle_test/test_editor.py
+++ b/Lib/idlelib/idle_test/test_editor.py
@@ -1,6 +1,6 @@
import unittest
from tkinter import Tk, Text
-from idlelib.EditorWindow import EditorWindow
+from idlelib.editor import EditorWindow
from test.support import requires
class Editor_func_test(unittest.TestCase):
diff --git a/Lib/idlelib/idle_test/test_grep.py b/Lib/idlelib/idle_test/test_grep.py
index 0d8ff0d9f1..6b54c13131 100644
--- a/Lib/idlelib/idle_test/test_grep.py
+++ b/Lib/idlelib/idle_test/test_grep.py
@@ -1,5 +1,5 @@
""" !Changing this line will break Test_findfile.test_found!
-Non-gui unit tests for idlelib.GrepDialog methods.
+Non-gui unit tests for grep.GrepDialog methods.
dummy_command calls grep_it calls findfiles.
An exception raised in one method will fail callers.
Otherwise, tests are mostly independent.
@@ -8,7 +8,7 @@ Otherwise, tests are mostly independent.
import unittest
from test.support import captured_stdout
from idlelib.idle_test.mock_tk import Var
-from idlelib.GrepDialog import GrepDialog
+from idlelib.grep import GrepDialog
import re
class Dummy_searchengine:
@@ -72,7 +72,7 @@ class Grep_itTest(unittest.TestCase):
self.assertTrue(lines[4].startswith('(Hint:'))
class Default_commandTest(unittest.TestCase):
- # To write this, mode OutputWindow import to top of GrepDialog
+ # To write this, move outwin import to top of GrepDialog
# so it can be replaced by captured_stdout in class setup/teardown.
pass
diff --git a/Lib/idlelib/idle_test/test_help.py b/Lib/idlelib/idle_test/test_help.py
new file mode 100644
index 0000000000..2c68e23b32
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_help.py
@@ -0,0 +1,34 @@
+'''Test idlelib.help.
+
+Coverage: 87%
+'''
+from idlelib import help
+from test.support import requires
+requires('gui')
+from os.path import abspath, dirname, join
+from tkinter import Tk
+import unittest
+
+class HelpFrameTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ "By itself, this tests that file parsed without exception."
+ cls.root = root = Tk()
+ root.withdraw()
+ helpfile = join(dirname(dirname(abspath(__file__))), 'help.html')
+ cls.frame = help.HelpFrame(root, helpfile)
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.frame
+ cls.root.update_idletasks()
+ cls.root.destroy()
+ del cls.root
+
+ def test_line1(self):
+ text = self.frame.text
+ self.assertEqual(text.get('1.0', '1.end'), ' IDLE ')
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_help_about.py b/Lib/idlelib/idle_test/test_help_about.py
index d0a012767a..843efb9ad2 100644
--- a/Lib/idlelib/idle_test/test_help_about.py
+++ b/Lib/idlelib/idle_test/test_help_about.py
@@ -2,10 +2,10 @@
Coverage:
'''
-from idlelib import aboutDialog as help_about
-from idlelib import textView as textview
+from idlelib import help_about
+from idlelib import textview
from idlelib.idle_test.mock_idle import Func
-from idlelib.idle_test.mock_tk import Mbox
+from idlelib.idle_test.mock_tk import Mbox_func
import unittest
About = help_about.AboutDialog
@@ -19,33 +19,33 @@ class Dummy_about_dialog():
class DisplayFileTest(unittest.TestCase):
- "Test that .txt files are found and properly decoded."
dialog = Dummy_about_dialog()
@classmethod
def setUpClass(cls):
- cls.orig_mbox = textview.tkMessageBox
+ cls.orig_error = textview.showerror
cls.orig_view = textview.view_text
- cls.mbox = Mbox()
+ cls.error = Mbox_func()
cls.view = Func()
- textview.tkMessageBox = cls.mbox
+ textview.showerror = cls.error
textview.view_text = cls.view
cls.About = Dummy_about_dialog()
@classmethod
def tearDownClass(cls):
- textview.tkMessageBox = cls.orig_mbox
+ textview.showerror = cls.orig_error
textview.view_text = cls.orig_view
def test_file_isplay(self):
for handler in (self.dialog.idle_credits,
self.dialog.idle_readme,
self.dialog.idle_news):
- self.mbox.showerror.message = ''
+ self.error.message = ''
self.view.called = False
- handler()
- self.assertEqual(self.mbox.showerror.message, '')
- self.assertEqual(self.view.called, True)
+ with self.subTest(handler=handler):
+ handler()
+ self.assertEqual(self.error.message, '')
+ self.assertEqual(self.view.called, True)
if __name__ == '__main__':
diff --git a/Lib/idlelib/idle_test/test_idlehistory.py b/Lib/idlelib/idle_test/test_history.py
index 6e7c6c3bff..b27801071b 100644
--- a/Lib/idlelib/idle_test/test_idlehistory.py
+++ b/Lib/idlelib/idle_test/test_history.py
@@ -4,8 +4,8 @@ from test.support import requires
import tkinter as tk
from tkinter import Text as tkText
from idlelib.idle_test.mock_tk import Text as mkText
-from idlelib.IdleHistory import History
-from idlelib.configHandler import idleConf
+from idlelib.history import History
+from idlelib.config import idleConf
line1 = 'a = 7'
line2 = 'b = a'
diff --git a/Lib/idlelib/idle_test/test_hyperparser.py b/Lib/idlelib/idle_test/test_hyperparser.py
index 9ce3f2c67f..73c8281e43 100644
--- a/Lib/idlelib/idle_test/test_hyperparser.py
+++ b/Lib/idlelib/idle_test/test_hyperparser.py
@@ -1,9 +1,9 @@
-"""Unittest for idlelib.HyperParser"""
+"""Unittest for idlelib.hyperparser.py."""
import unittest
from test.support import requires
from tkinter import Tk, Text
-from idlelib.EditorWindow import EditorWindow
-from idlelib.HyperParser import HyperParser
+from idlelib.editor import EditorWindow
+from idlelib.hyperparser import HyperParser
class DummyEditwin:
def __init__(self, text):
diff --git a/Lib/idlelib/idle_test/test_io.py b/Lib/idlelib/idle_test/test_iomenu.py
index e0e3b985e2..65bf593055 100644
--- a/Lib/idlelib/idle_test/test_io.py
+++ b/Lib/idlelib/idle_test/test_iomenu.py
@@ -1,6 +1,7 @@
import unittest
import io
-from idlelib.PyShell import PseudoInputFile, PseudoOutputFile
+
+from idlelib.run import PseudoInputFile, PseudoOutputFile
class S(str):
@@ -230,4 +231,4 @@ class PseudeInputFilesTest(unittest.TestCase):
if __name__ == '__main__':
- unittest.main()
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_macosx.py b/Lib/idlelib/idle_test/test_macosx.py
new file mode 100644
index 0000000000..fae75d8a49
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_macosx.py
@@ -0,0 +1,103 @@
+'''Test idlelib.macosx.py.
+
+Coverage: 71% on Windows.
+'''
+from idlelib import macosx
+from test.support import requires
+import sys
+import tkinter as tk
+import unittest
+import unittest.mock as mock
+from idlelib.filelist import FileList
+
+mactypes = {'carbon', 'cocoa', 'xquartz'}
+nontypes = {'other'}
+alltypes = mactypes | nontypes
+
+
+class InitTktypeTest(unittest.TestCase):
+ "Test _init_tk_type."
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = tk.Tk()
+ cls.root.withdraw()
+ cls.orig_platform = macosx.platform
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.update_idletasks()
+ cls.root.destroy()
+ del cls.root
+ macosx.platform = cls.orig_platform
+
+ def test_init_sets_tktype(self):
+ "Test that _init_tk_type sets _tk_type according to platform."
+ for platform, types in ('darwin', alltypes), ('other', nontypes):
+ with self.subTest(platform=platform):
+ macosx.platform = platform
+ macosx._tk_type == None
+ macosx._init_tk_type()
+ self.assertIn(macosx._tk_type, types)
+
+
+class IsTypeTkTest(unittest.TestCase):
+ "Test each of the four isTypeTk predecates."
+ isfuncs = ((macosx.isAquaTk, ('carbon', 'cocoa')),
+ (macosx.isCarbonTk, ('carbon')),
+ (macosx.isCocoaTk, ('cocoa')),
+ (macosx.isXQuartz, ('xquartz')),
+ )
+
+ @mock.patch('idlelib.macosx._init_tk_type')
+ def test_is_calls_init(self, mockinit):
+ "Test that each isTypeTk calls _init_tk_type when _tk_type is None."
+ macosx._tk_type = None
+ for func, whentrue in self.isfuncs:
+ with self.subTest(func=func):
+ func()
+ self.assertTrue(mockinit.called)
+ mockinit.reset_mock()
+
+ def test_isfuncs(self):
+ "Test that each isTypeTk return correct bool."
+ for func, whentrue in self.isfuncs:
+ for tktype in alltypes:
+ with self.subTest(func=func, whentrue=whentrue, tktype=tktype):
+ macosx._tk_type = tktype
+ (self.assertTrue if tktype in whentrue else self.assertFalse)\
+ (func())
+
+
+class SetupTest(unittest.TestCase):
+ "Test setupApp."
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = tk.Tk()
+ cls.root.withdraw()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.update_idletasks()
+ cls.root.destroy()
+ del cls.root
+
+ @mock.patch('idlelib.macosx.overrideRootMenu') #27312
+ def test_setupapp(self, overrideRootMenu):
+ "Call setupApp with each possible graphics type."
+ root = self.root
+ flist = FileList(root)
+ for tktype in alltypes:
+ with self.subTest(tktype=tktype):
+ macosx._tk_type = tktype
+ macosx.setupApp(root, flist)
+ if tktype in ('carbon', 'cocoa'):
+ self.assertTrue(overrideRootMenu.called)
+ overrideRootMenu.reset_mock()
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_formatparagraph.py b/Lib/idlelib/idle_test/test_paragraph.py
index b6eb2f31e4..ba350c9765 100644
--- a/Lib/idlelib/idle_test/test_formatparagraph.py
+++ b/Lib/idlelib/idle_test/test_paragraph.py
@@ -1,7 +1,7 @@
-# Test the functions and main class method of FormatParagraph.py
+# Test the functions and main class method of paragraph.py
import unittest
-from idlelib import FormatParagraph as fp
-from idlelib.EditorWindow import EditorWindow
+from idlelib import paragraph as fp
+from idlelib.editor import EditorWindow
from tkinter import Tk, Text
from test.support import requires
@@ -38,7 +38,7 @@ class Is_Get_Test(unittest.TestCase):
class FindTest(unittest.TestCase):
- """Test the find_paragraph function in FormatParagraph.
+ """Test the find_paragraph function in paragraph module.
Using the runcase() function, find_paragraph() is called with 'mark' set at
multiple indexes before and inside the test paragraph.
diff --git a/Lib/idlelib/idle_test/test_parenmatch.py b/Lib/idlelib/idle_test/test_parenmatch.py
index 95cc22cf68..051f7eac2d 100644
--- a/Lib/idlelib/idle_test/test_parenmatch.py
+++ b/Lib/idlelib/idle_test/test_parenmatch.py
@@ -1,4 +1,4 @@
-'''Test idlelib.ParenMatch.
+'''Test idlelib.parenmatch.
This must currently be a gui test because ParenMatch methods use
several text methods not defined on idlelib.idle_test.mock_tk.Text.
@@ -9,7 +9,7 @@ requires('gui')
import unittest
from unittest.mock import Mock
from tkinter import Tk, Text
-from idlelib.ParenMatch import ParenMatch
+from idlelib.parenmatch import ParenMatch
class DummyEditwin:
def __init__(self, text):
@@ -38,12 +38,17 @@ class ParenMatchTest(unittest.TestCase):
def tearDown(self):
self.text.delete('1.0', 'end')
+ def get_parenmatch(self):
+ pm = ParenMatch(self.editwin)
+ pm.bell = lambda: None
+ return pm
+
def test_paren_expression(self):
"""
Test ParenMatch with 'expression' style.
"""
text = self.text
- pm = ParenMatch(self.editwin)
+ pm = self.get_parenmatch()
pm.set_style('expression')
text.insert('insert', 'def foobar(a, b')
@@ -66,7 +71,7 @@ class ParenMatchTest(unittest.TestCase):
Test ParenMatch with 'default' style.
"""
text = self.text
- pm = ParenMatch(self.editwin)
+ pm = self.get_parenmatch()
pm.set_style('default')
text.insert('insert', 'def foobar(a, b')
@@ -86,7 +91,7 @@ class ParenMatchTest(unittest.TestCase):
These cases force conditional expression and alternate paths.
"""
text = self.text
- pm = ParenMatch(self.editwin)
+ pm = self.get_parenmatch()
text.insert('insert', '# this is a commen)')
self.assertIsNone(pm.paren_closed_event('event'))
@@ -99,7 +104,7 @@ class ParenMatchTest(unittest.TestCase):
self.assertIsNone(pm.paren_closed_event('event'))
def test_handle_restore_timer(self):
- pm = ParenMatch(self.editwin)
+ pm = self.get_parenmatch()
pm.restore_event = Mock()
pm.handle_restore_timer(0)
self.assertTrue(pm.restore_event.called)
diff --git a/Lib/idlelib/idle_test/test_pathbrowser.py b/Lib/idlelib/idle_test/test_pathbrowser.py
index afb886fa33..813cbcc631 100644
--- a/Lib/idlelib/idle_test/test_pathbrowser.py
+++ b/Lib/idlelib/idle_test/test_pathbrowser.py
@@ -2,13 +2,13 @@ import unittest
import os
import sys
import idlelib
-from idlelib import PathBrowser
+from idlelib import pathbrowser
class PathBrowserTest(unittest.TestCase):
def test_DirBrowserTreeItem(self):
# Issue16226 - make sure that getting a sublist works
- d = PathBrowser.DirBrowserTreeItem('')
+ d = pathbrowser.DirBrowserTreeItem('')
d.GetSubList()
self.assertEqual('', d.GetText())
@@ -17,11 +17,11 @@ class PathBrowserTest(unittest.TestCase):
self.assertEqual(d.ispackagedir(dir + '/Icons'), False)
def test_PathBrowserTreeItem(self):
- p = PathBrowser.PathBrowserTreeItem()
+ p = pathbrowser.PathBrowserTreeItem()
self.assertEqual(p.GetText(), 'sys.path')
sub = p.GetSubList()
self.assertEqual(len(sub), len(sys.path))
- self.assertEqual(type(sub[0]), PathBrowser.DirBrowserTreeItem)
+ self.assertEqual(type(sub[0]), pathbrowser.DirBrowserTreeItem)
if __name__ == '__main__':
unittest.main(verbosity=2, exit=False)
diff --git a/Lib/idlelib/idle_test/test_percolator.py b/Lib/idlelib/idle_test/test_percolator.py
index 4c0a7ad2bd..573b9a1e8e 100644
--- a/Lib/idlelib/idle_test/test_percolator.py
+++ b/Lib/idlelib/idle_test/test_percolator.py
@@ -1,10 +1,10 @@
-'''Test Percolator'''
+'''Test percolator.py.'''
from test.support import requires
requires('gui')
import unittest
from tkinter import Text, Tk, END
-from idlelib.Percolator import Percolator, Delegator
+from idlelib.percolator import Percolator, Delegator
class MyFilter(Delegator):
diff --git a/Lib/idlelib/idle_test/test_query.py b/Lib/idlelib/idle_test/test_query.py
new file mode 100644
index 0000000000..66af8eb85b
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_query.py
@@ -0,0 +1,353 @@
+"""Test idlelib.query.
+
+Non-gui tests for Query, SectionName, ModuleName, and HelpSource use
+dummy versions that extract the non-gui methods and add other needed
+attributes. GUI tests create an instance of each class and simulate
+entries and button clicks. Subclass tests only target the new code in
+the subclass definition.
+
+The appearance of the widgets is checked by the Query and
+HelpSource htests. These are run by running query.py.
+
+Coverage: 94% (100% for Query and SectionName).
+6 of 8 missing are ModuleName exceptions I don't know how to trigger.
+"""
+from test.support import requires
+import sys
+from tkinter import Tk
+import unittest
+from unittest import mock
+from idlelib.idle_test.mock_tk import Var
+from idlelib import query
+
+
+# NON-GUI TESTS
+
+class QueryTest(unittest.TestCase):
+ "Test Query base class."
+
+ class Dummy_Query:
+ # Test the following Query methods.
+ entry_ok = query.Query.entry_ok
+ ok = query.Query.ok
+ cancel = query.Query.cancel
+ # Add attributes and initialization needed for tests.
+ entry = Var()
+ entry_error = {}
+ def __init__(self, dummy_entry):
+ self.entry.set(dummy_entry)
+ self.entry_error['text'] = ''
+ self.result = None
+ self.destroyed = False
+ def showerror(self, message):
+ self.entry_error['text'] = message
+ def destroy(self):
+ self.destroyed = True
+
+ def test_entry_ok_blank(self):
+ dialog = self.Dummy_Query(' ')
+ self.assertEqual(dialog.entry_ok(), None)
+ self.assertEqual((dialog.result, dialog.destroyed), (None, False))
+ self.assertIn('blank line', dialog.entry_error['text'])
+
+ def test_entry_ok_good(self):
+ dialog = self.Dummy_Query(' good ')
+ Equal = self.assertEqual
+ Equal(dialog.entry_ok(), 'good')
+ Equal((dialog.result, dialog.destroyed), (None, False))
+ Equal(dialog.entry_error['text'], '')
+
+ def test_ok_blank(self):
+ dialog = self.Dummy_Query('')
+ dialog.entry.focus_set = mock.Mock()
+ self.assertEqual(dialog.ok(), None)
+ self.assertTrue(dialog.entry.focus_set.called)
+ del dialog.entry.focus_set
+ self.assertEqual((dialog.result, dialog.destroyed), (None, False))
+
+ def test_ok_good(self):
+ dialog = self.Dummy_Query('good')
+ self.assertEqual(dialog.ok(), None)
+ self.assertEqual((dialog.result, dialog.destroyed), ('good', True))
+
+ def test_cancel(self):
+ dialog = self.Dummy_Query('does not matter')
+ self.assertEqual(dialog.cancel(), None)
+ self.assertEqual((dialog.result, dialog.destroyed), (None, True))
+
+
+class SectionNameTest(unittest.TestCase):
+ "Test SectionName subclass of Query."
+
+ class Dummy_SectionName:
+ entry_ok = query.SectionName.entry_ok # Function being tested.
+ used_names = ['used']
+ entry = Var()
+ entry_error = {}
+ def __init__(self, dummy_entry):
+ self.entry.set(dummy_entry)
+ self.entry_error['text'] = ''
+ def showerror(self, message):
+ self.entry_error['text'] = message
+
+ def test_blank_section_name(self):
+ dialog = self.Dummy_SectionName(' ')
+ self.assertEqual(dialog.entry_ok(), None)
+ self.assertIn('no name', dialog.entry_error['text'])
+
+ def test_used_section_name(self):
+ dialog = self.Dummy_SectionName('used')
+ self.assertEqual(dialog.entry_ok(), None)
+ self.assertIn('use', dialog.entry_error['text'])
+
+ def test_long_section_name(self):
+ dialog = self.Dummy_SectionName('good'*8)
+ self.assertEqual(dialog.entry_ok(), None)
+ self.assertIn('longer than 30', dialog.entry_error['text'])
+
+ def test_good_section_name(self):
+ dialog = self.Dummy_SectionName(' good ')
+ self.assertEqual(dialog.entry_ok(), 'good')
+ self.assertEqual(dialog.entry_error['text'], '')
+
+
+class ModuleNameTest(unittest.TestCase):
+ "Test ModuleName subclass of Query."
+
+ class Dummy_ModuleName:
+ entry_ok = query.ModuleName.entry_ok # Function being tested.
+ text0 = ''
+ entry = Var()
+ entry_error = {}
+ def __init__(self, dummy_entry):
+ self.entry.set(dummy_entry)
+ self.entry_error['text'] = ''
+ def showerror(self, message):
+ self.entry_error['text'] = message
+
+ def test_blank_module_name(self):
+ dialog = self.Dummy_ModuleName(' ')
+ self.assertEqual(dialog.entry_ok(), None)
+ self.assertIn('no name', dialog.entry_error['text'])
+
+ def test_bogus_module_name(self):
+ dialog = self.Dummy_ModuleName('__name_xyz123_should_not_exist__')
+ self.assertEqual(dialog.entry_ok(), None)
+ self.assertIn('not found', dialog.entry_error['text'])
+
+ def test_c_source_name(self):
+ dialog = self.Dummy_ModuleName('itertools')
+ self.assertEqual(dialog.entry_ok(), None)
+ self.assertIn('source-based', dialog.entry_error['text'])
+
+ def test_good_module_name(self):
+ dialog = self.Dummy_ModuleName('idlelib')
+ self.assertTrue(dialog.entry_ok().endswith('__init__.py'))
+ self.assertEqual(dialog.entry_error['text'], '')
+
+
+# 3 HelpSource test classes each test one function.
+
+orig_platform = query.platform
+
+class HelpsourceBrowsefileTest(unittest.TestCase):
+ "Test browse_file method of ModuleName subclass of Query."
+
+ class Dummy_HelpSource:
+ browse_file = query.HelpSource.browse_file
+ pathvar = Var()
+
+ def test_file_replaces_path(self):
+ dialog = self.Dummy_HelpSource()
+ # Path is widget entry, either '' or something.
+ # Func return is file dialog return, either '' or something.
+ # Func return should override widget entry.
+ # We need all 4 combination to test all (most) code paths.
+ for path, func, result in (
+ ('', lambda a,b,c:'', ''),
+ ('', lambda a,b,c: __file__, __file__),
+ ('htest', lambda a,b,c:'', 'htest'),
+ ('htest', lambda a,b,c: __file__, __file__)):
+ with self.subTest():
+ dialog.pathvar.set(path)
+ dialog.askfilename = func
+ dialog.browse_file()
+ self.assertEqual(dialog.pathvar.get(), result)
+
+
+class HelpsourcePathokTest(unittest.TestCase):
+ "Test path_ok method of HelpSource subclass of Query."
+
+ class Dummy_HelpSource:
+ path_ok = query.HelpSource.path_ok
+ path = Var()
+ path_error = {}
+ def __init__(self, dummy_path):
+ self.path.set(dummy_path)
+ self.path_error['text'] = ''
+ def showerror(self, message, widget=None):
+ self.path_error['text'] = message
+
+ @classmethod
+ def tearDownClass(cls):
+ query.platform = orig_platform
+
+ def test_path_ok_blank(self):
+ dialog = self.Dummy_HelpSource(' ')
+ self.assertEqual(dialog.path_ok(), None)
+ self.assertIn('no help file', dialog.path_error['text'])
+
+ def test_path_ok_bad(self):
+ dialog = self.Dummy_HelpSource(__file__ + 'bad-bad-bad')
+ self.assertEqual(dialog.path_ok(), None)
+ self.assertIn('not exist', dialog.path_error['text'])
+
+ def test_path_ok_web(self):
+ dialog = self.Dummy_HelpSource('')
+ Equal = self.assertEqual
+ for url in 'www.py.org', 'http://py.org':
+ with self.subTest():
+ dialog.path.set(url)
+ self.assertEqual(dialog.path_ok(), url)
+ self.assertEqual(dialog.path_error['text'], '')
+
+ def test_path_ok_file(self):
+ dialog = self.Dummy_HelpSource('')
+ for platform, prefix in ('darwin', 'file://'), ('other', ''):
+ with self.subTest():
+ query.platform = platform
+ dialog.path.set(__file__)
+ self.assertEqual(dialog.path_ok(), prefix + __file__)
+ self.assertEqual(dialog.path_error['text'], '')
+
+
+class HelpsourceEntryokTest(unittest.TestCase):
+ "Test entry_ok method of HelpSource subclass of Query."
+
+ class Dummy_HelpSource:
+ entry_ok = query.HelpSource.entry_ok
+ entry_error = {}
+ path_error = {}
+ def item_ok(self):
+ return self.name
+ def path_ok(self):
+ return self.path
+
+ def test_entry_ok_helpsource(self):
+ dialog = self.Dummy_HelpSource()
+ for name, path, result in ((None, None, None),
+ (None, 'doc.txt', None),
+ ('doc', None, None),
+ ('doc', 'doc.txt', ('doc', 'doc.txt'))):
+ with self.subTest():
+ dialog.name, dialog.path = name, path
+ self.assertEqual(dialog.entry_ok(), result)
+
+
+# GUI TESTS
+
+class QueryGuiTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = root = Tk()
+ cls.root.withdraw()
+ cls.dialog = query.Query(root, 'TEST', 'test', _utest=True)
+ cls.dialog.destroy = mock.Mock()
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.dialog
+ cls.root.destroy()
+ del cls.root
+
+ def setUp(self):
+ self.dialog.entry.delete(0, 'end')
+ self.dialog.result = None
+ self.dialog.destroy.reset_mock()
+
+ def test_click_ok(self):
+ dialog = self.dialog
+ dialog.entry.insert(0, 'abc')
+ dialog.button_ok.invoke()
+ self.assertEqual(dialog.result, 'abc')
+ self.assertTrue(dialog.destroy.called)
+
+ def test_click_blank(self):
+ dialog = self.dialog
+ dialog.button_ok.invoke()
+ self.assertEqual(dialog.result, None)
+ self.assertFalse(dialog.destroy.called)
+
+ def test_click_cancel(self):
+ dialog = self.dialog
+ dialog.entry.insert(0, 'abc')
+ dialog.button_cancel.invoke()
+ self.assertEqual(dialog.result, None)
+ self.assertTrue(dialog.destroy.called)
+
+
+class SectionnameGuiTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+
+ def test_click_section_name(self):
+ root = Tk()
+ root.withdraw()
+ dialog = query.SectionName(root, 'T', 't', {'abc'}, _utest=True)
+ Equal = self.assertEqual
+ self.assertEqual(dialog.used_names, {'abc'})
+ dialog.entry.insert(0, 'okay')
+ dialog.button_ok.invoke()
+ self.assertEqual(dialog.result, 'okay')
+ del dialog
+ root.destroy()
+ del root
+
+
+class ModulenameGuiTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+
+ def test_click_module_name(self):
+ root = Tk()
+ root.withdraw()
+ dialog = query.ModuleName(root, 'T', 't', 'idlelib', _utest=True)
+ self.assertEqual(dialog.text0, 'idlelib')
+ self.assertEqual(dialog.entry.get(), 'idlelib')
+ dialog.button_ok.invoke()
+ self.assertTrue(dialog.result.endswith('__init__.py'))
+ del dialog
+ root.destroy()
+ del root
+
+
+class HelpsourceGuiTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+
+ def test_click_help_source(self):
+ root = Tk()
+ root.withdraw()
+ dialog = query.HelpSource(root, 'T', menuitem='__test__',
+ filepath=__file__, _utest=True)
+ Equal = self.assertEqual
+ Equal(dialog.entry.get(), '__test__')
+ Equal(dialog.path.get(), __file__)
+ dialog.button_ok.invoke()
+ prefix = "file://" if sys.platform == 'darwin' else ''
+ Equal(dialog.result, ('__test__', prefix + __file__))
+ del dialog
+ root.destroy()
+ del root
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=False)
diff --git a/Lib/idlelib/idle_test/test_widgetredir.py b/Lib/idlelib/idle_test/test_redirector.py
index baa975db35..b0385fa78c 100644
--- a/Lib/idlelib/idle_test/test_widgetredir.py
+++ b/Lib/idlelib/idle_test/test_redirector.py
@@ -1,4 +1,4 @@
-'''Test idlelib.WidgetRedirector.
+'''Test idlelib.redirector.
100% coverage
'''
@@ -6,7 +6,7 @@ from test.support import requires
import unittest
from idlelib.idle_test.mock_idle import Func
from tkinter import Tk, Text, TclError
-from idlelib.WidgetRedirector import WidgetRedirector
+from idlelib.redirector import WidgetRedirector
class InitCloseTest(unittest.TestCase):
@@ -120,6 +120,5 @@ class WidgetRedirectorTest(unittest.TestCase):
self.assertEqual(self.root.call(self.text._w, 'insert', 'boo'), '')
-
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_replacedialog.py b/Lib/idlelib/idle_test/test_replace.py
index ff44820809..9913ed2b7c 100644
--- a/Lib/idlelib/idle_test/test_replacedialog.py
+++ b/Lib/idlelib/idle_test/test_replace.py
@@ -1,4 +1,4 @@
-"""Unittest for idlelib.ReplaceDialog"""
+"""Unittest for idlelib.replace.py"""
from test.support import requires
requires('gui')
@@ -6,8 +6,8 @@ import unittest
from unittest.mock import Mock
from tkinter import Tk, Text
from idlelib.idle_test.mock_tk import Mbox
-import idlelib.SearchEngine as se
-import idlelib.ReplaceDialog as rd
+import idlelib.searchengine as se
+from idlelib.replace import ReplaceDialog
orig_mbox = se.tkMessageBox
showerror = Mbox.showerror
@@ -21,7 +21,8 @@ class ReplaceDialogTest(unittest.TestCase):
cls.root.withdraw()
se.tkMessageBox = Mbox
cls.engine = se.SearchEngine(cls.root)
- cls.dialog = rd.ReplaceDialog(cls.root, cls.engine)
+ cls.dialog = ReplaceDialog(cls.root, cls.engine)
+ cls.dialog.bell = lambda: None
cls.dialog.ok = Mock()
cls.text = Text(cls.root)
cls.text.undo_block_start = Mock()
@@ -70,7 +71,6 @@ class ReplaceDialogTest(unittest.TestCase):
# text found and replaced
pv.set('a')
rv.set('asdf')
- self.dialog.open(self.text)
replace()
equal(text.get('1.8', '1.12'), 'asdf')
@@ -91,7 +91,7 @@ class ReplaceDialogTest(unittest.TestCase):
text.mark_set('insert', 'end')
text.insert('insert', '\nline42:')
before_text = text.get('1.0', 'end')
- pv.set('[a-z][\d]+')
+ pv.set(r'[a-z][\d]+')
replace()
after_text = text.get('1.0', 'end')
equal(before_text, after_text)
@@ -192,7 +192,7 @@ class ReplaceDialogTest(unittest.TestCase):
self.engine.revar.set(True)
before_text = text.get('1.0', 'end')
- pv.set('[a-z][\d]+')
+ pv.set(r'[a-z][\d]+')
rv.set('hello')
replace()
after_text = text.get('1.0', 'end')
@@ -207,7 +207,7 @@ class ReplaceDialogTest(unittest.TestCase):
self.assertIn('error', showerror.title)
self.assertIn('Empty', showerror.message)
- pv.set('[\d')
+ pv.set(r'[\d')
replace()
self.assertIn('error', showerror.title)
self.assertIn('Pattern', showerror.message)
diff --git a/Lib/idlelib/idle_test/test_rstrip.py b/Lib/idlelib/idle_test/test_rstrip.py
index 1c90b93d21..130e6be257 100644
--- a/Lib/idlelib/idle_test/test_rstrip.py
+++ b/Lib/idlelib/idle_test/test_rstrip.py
@@ -1,5 +1,5 @@
import unittest
-import idlelib.RstripExtension as rs
+import idlelib.rstrip as rs
from idlelib.idle_test.mock_idle import Editor
class rstripTest(unittest.TestCase):
@@ -21,7 +21,7 @@ class rstripTest(unittest.TestCase):
def test_rstrip_multiple(self):
editor = Editor()
# Uncomment following to verify that test passes with real widgets.
-## from idlelib.EditorWindow import EditorWindow as Editor
+## from idlelib.editor import EditorWindow as Editor
## from tkinter import Tk
## editor = Editor(root=Tk())
text = editor.text
diff --git a/Lib/idlelib/idle_test/test_scrolledlist.py b/Lib/idlelib/idle_test/test_scrolledlist.py
new file mode 100644
index 0000000000..56aabfecf4
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_scrolledlist.py
@@ -0,0 +1,29 @@
+''' Test idlelib.scrolledlist.
+
+Coverage: 39%
+'''
+from idlelib import scrolledlist
+from test.support import requires
+requires('gui')
+import unittest
+from tkinter import Tk
+
+
+class ScrolledListTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.root = Tk()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.root
+
+
+ def test_init(self):
+ scrolledlist.ScrolledList(self.root)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_searchdialog.py b/Lib/idlelib/idle_test/test_search.py
index 190c866a18..80fa93adf5 100644
--- a/Lib/idlelib/idle_test/test_searchdialog.py
+++ b/Lib/idlelib/idle_test/test_search.py
@@ -1,4 +1,4 @@
-"""Test SearchDialog class in SearchDialogue.py"""
+"""Test SearchDialog class in idlelib.search.py"""
# Does not currently test the event handler wrappers.
# A usage test should simulate clicks and check hilighting.
@@ -11,8 +11,8 @@ requires('gui')
import unittest
import tkinter as tk
from tkinter import BooleanVar
-import idlelib.SearchEngine as se
-import idlelib.SearchDialog as sd
+import idlelib.searchengine as se
+import idlelib.search as sd
class SearchDialogTest(unittest.TestCase):
@@ -29,6 +29,7 @@ class SearchDialogTest(unittest.TestCase):
def setUp(self):
self.engine = se.SearchEngine(self.root)
self.dialog = sd.SearchDialog(self.root, self.engine)
+ self.dialog.bell = lambda: None
self.text = tk.Text(self.root)
self.text.insert('1.0', 'Hello World!')
@@ -38,6 +39,7 @@ class SearchDialogTest(unittest.TestCase):
self.engine.setpat('')
self.assertFalse(self.dialog.find_again(text))
+ self.dialog.bell = lambda: None
self.engine.setpat('Hello')
self.assertTrue(self.dialog.find_again(text))
diff --git a/Lib/idlelib/idle_test/test_searchdialogbase.py b/Lib/idlelib/idle_test/test_searchbase.py
index 8036b918c5..d769fa2fc2 100644
--- a/Lib/idlelib/idle_test/test_searchdialogbase.py
+++ b/Lib/idlelib/idle_test/test_searchbase.py
@@ -1,14 +1,13 @@
-'''Unittests for idlelib/SearchDialogBase.py
+'''tests idlelib.searchbase.
Coverage: 99%. The only thing not covered is inconsequential --
testing skipping of suite when self.needwrapbutton is false.
-
'''
import unittest
from test.support import requires
from tkinter import Tk, Toplevel, Frame ##, BooleanVar, StringVar
-from idlelib import SearchEngine as se
-from idlelib import SearchDialogBase as sdb
+from idlelib import searchengine as se
+from idlelib import searchbase as sdb
from idlelib.idle_test.mock_idle import Func
## from idlelib.idle_test.mock_tk import Var
@@ -74,7 +73,7 @@ class SearchDialogBaseTest(unittest.TestCase):
def test_make_entry(self):
equal = self.assertEqual
self.dialog.row = 0
- self.dialog.top = Toplevel(self.root)
+ self.dialog.top = self.root
entry, label = self.dialog.make_entry("Test:", 'hello')
equal(label['text'], 'Test:')
@@ -87,6 +86,7 @@ class SearchDialogBaseTest(unittest.TestCase):
equal(self.dialog.row, 1)
def test_create_entries(self):
+ self.dialog.top = self.root
self.dialog.row = 0
self.engine.setpat('hello')
self.dialog.create_entries()
@@ -94,7 +94,7 @@ class SearchDialogBaseTest(unittest.TestCase):
def test_make_frame(self):
self.dialog.row = 0
- self.dialog.top = Toplevel(self.root)
+ self.dialog.top = self.root
frame, label = self.dialog.make_frame()
self.assertEqual(label, '')
self.assertIsInstance(frame, Frame)
@@ -104,7 +104,7 @@ class SearchDialogBaseTest(unittest.TestCase):
self.assertIsInstance(frame, Frame)
def btn_test_setup(self, meth):
- self.dialog.top = Toplevel(self.root)
+ self.dialog.top = self.root
self.dialog.row = 0
return meth()
@@ -119,11 +119,6 @@ class SearchDialogBaseTest(unittest.TestCase):
var, label = spec
self.assertEqual(button['text'], label)
self.assertEqual(var.get(), state)
- if state == 1:
- button.deselect()
- else:
- button.select()
- self.assertEqual(var.get(), 1 - state)
def test_create_other_buttons(self):
for state in (False, True):
@@ -139,18 +134,15 @@ class SearchDialogBaseTest(unittest.TestCase):
# hit other button, then this one
# indexes depend on button order
self.assertEqual(var.get(), state)
- buttons[val].select()
- self.assertEqual(var.get(), 1 - state)
- buttons[1-val].select()
- self.assertEqual(var.get(), state)
def test_make_button(self):
- self.dialog.top = Toplevel(self.root)
+ self.dialog.top = self.root
self.dialog.buttonframe = Frame(self.dialog.top)
btn = self.dialog.make_button('Test', self.dialog.close)
self.assertEqual(btn['text'], 'Test')
def test_create_command_buttons(self):
+ self.dialog.top = self.root
self.dialog.create_command_buttons()
# Look for close button command in buttonframe
closebuttoncommand = ''
@@ -160,6 +152,5 @@ class SearchDialogBaseTest(unittest.TestCase):
self.assertIn('close', closebuttoncommand)
-
if __name__ == '__main__':
unittest.main(verbosity=2, exit=2)
diff --git a/Lib/idlelib/idle_test/test_searchengine.py b/Lib/idlelib/idle_test/test_searchengine.py
index edbd558133..b3aa8eb812 100644
--- a/Lib/idlelib/idle_test/test_searchengine.py
+++ b/Lib/idlelib/idle_test/test_searchengine.py
@@ -1,4 +1,4 @@
-'''Test functions and SearchEngine class in SearchEngine.py.'''
+'''Test functions and SearchEngine class in idlelib.searchengine.py.'''
# With mock replacements, the module does not use any gui widgets.
# The use of tk.Text is avoided (for now, until mock Text is improved)
@@ -10,7 +10,7 @@ import unittest
# from test.support import requires
from tkinter import BooleanVar, StringVar, TclError # ,Tk, Text
import tkinter.messagebox as tkMessageBox
-from idlelib import SearchEngine as se
+from idlelib import searchengine as se
from idlelib.idle_test.mock_tk import Var, Mbox
from idlelib.idle_test.mock_tk import Text as mockText
@@ -139,10 +139,10 @@ class SearchEngineTest(unittest.TestCase):
def test_setcookedpat(self):
engine = self.engine
- engine.setcookedpat('\s')
- self.assertEqual(engine.getpat(), '\s')
+ engine.setcookedpat(r'\s')
+ self.assertEqual(engine.getpat(), r'\s')
engine.revar.set(1)
- engine.setcookedpat('\s')
+ engine.setcookedpat(r'\s')
self.assertEqual(engine.getpat(), r'\\s')
def test_getcookedpat(self):
@@ -156,10 +156,10 @@ class SearchEngineTest(unittest.TestCase):
Equal(engine.getcookedpat(), r'\bhello\b')
engine.wordvar.set(False)
- engine.setpat('\s')
+ engine.setpat(r'\s')
Equal(engine.getcookedpat(), r'\\s')
engine.revar.set(True)
- Equal(engine.getcookedpat(), '\s')
+ Equal(engine.getcookedpat(), r'\s')
def test_getprog(self):
engine = self.engine
@@ -282,7 +282,7 @@ class ForwardBackwardTest(unittest.TestCase):
cls.pat = re.compile('target')
cls.res = (2, (10, 16)) # line, slice indexes of 'target'
cls.failpat = re.compile('xyz') # not in text
- cls.emptypat = re.compile('\w*') # empty match possible
+ cls.emptypat = re.compile(r'\w*') # empty match possible
def make_search(self, func):
def search(pat, line, col, wrap, ok=0):
diff --git a/Lib/idlelib/idle_test/test_text.py b/Lib/idlelib/idle_test/test_text.py
index 7e823df3db..a5ba7bb213 100644
--- a/Lib/idlelib/idle_test/test_text.py
+++ b/Lib/idlelib/idle_test/test_text.py
@@ -1,17 +1,19 @@
-# Test mock_tk.Text class against tkinter.Text class by running same tests with both.
+''' Test mock_tk.Text class against tkinter.Text class
+
+Run same tests with both by creating a mixin class.
+'''
import unittest
from test.support import requires
-
from _tkinter import TclError
class TextTest(object):
+ "Define items common to both sets of tests."
- hw = 'hello\nworld' # usual initial insert after initialization
+ hw = 'hello\nworld' # Several tests insert this after after initialization.
hwn = hw+'\n' # \n present at initialization, before insert
- Text = None
- def setUp(self):
- self.text = self.Text()
+ # setUpClass defines cls.Text and maybe cls.root.
+ # setUp defines self.text from Text and maybe root.
def test_init(self):
self.assertEqual(self.text.get('1.0'), '\n')
@@ -196,6 +198,10 @@ class MockTextTest(TextTest, unittest.TestCase):
from idlelib.idle_test.mock_tk import Text
cls.Text = Text
+ def setUp(self):
+ self.text = self.Text()
+
+
def test_decode(self):
# test endflags (-1, 0) not tested by test_index (which uses +1)
decode = self.text._decode
@@ -222,6 +228,9 @@ class TkTextTest(TextTest, unittest.TestCase):
cls.root.destroy()
del cls.root
+ def setUp(self):
+ self.text = self.Text(self.root)
+
if __name__ == '__main__':
unittest.main(verbosity=2, exit=False)
diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py
index 5d2e60019d..f018f5ef19 100644
--- a/Lib/idlelib/idle_test/test_textview.py
+++ b/Lib/idlelib/idle_test/test_textview.py
@@ -1,21 +1,21 @@
-'''Test idlelib.textView.
+'''Test idlelib.textview.
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%.
+Coverage: 94%.
'''
+from idlelib import textview as tv
from test.support import requires
requires('gui')
import unittest
import os
from tkinter import Tk
-from idlelib import textView as tv
from idlelib.idle_test.mock_idle import Func
-from idlelib.idle_test.mock_tk import Mbox
+from idlelib.idle_test.mock_tk import Mbox_func
def setUpModule():
global root
@@ -64,17 +64,17 @@ class TextViewTest(unittest.TestCase):
view.destroy()
-class textviewTest(unittest.TestCase):
+class ViewFunctionTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
- cls.orig_mbox = tv.tkMessageBox
- tv.tkMessageBox = Mbox
+ cls.orig_error = tv.showerror
+ tv.showerror = Mbox_func()
@classmethod
def tearDownClass(cls):
- tv.tkMessageBox = cls.orig_mbox
- del cls.orig_mbox
+ tv.showerror = cls.orig_error
+ del cls.orig_error
def test_view_text(self):
# If modal True, get tk error 'can't invoke "event" command'.
@@ -90,7 +90,7 @@ class textviewTest(unittest.TestCase):
self.assertIn('Test', view.textView.get('1.0', '1.end'))
view.Ok()
- # Mock messagebox will be used and view_file will not return anything
+ # Mock showerror will be used; view_file will return None.
testfile = os.path.join(test_dir, '../notthere.py')
view = tv.view_file(root, 'Title', testfile, modal=False)
self.assertIsNone(view)
diff --git a/Lib/idlelib/idle_test/test_tree.py b/Lib/idlelib/idle_test/test_tree.py
new file mode 100644
index 0000000000..09ba9641af
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_tree.py
@@ -0,0 +1,36 @@
+''' Test idlelib.tree.
+
+Coverage: 56%
+'''
+from idlelib import tree
+from test.support import requires
+requires('gui')
+import os
+import unittest
+from tkinter import Tk
+
+
+class TreeTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.root = Tk()
+ cls.root.withdraw()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.root
+
+ def test_init(self):
+ # Start with code slightly adapted from htest.
+ sc = tree.ScrolledCanvas(
+ self.root, bg="white", highlightthickness=0, takefocus=1)
+ sc.frame.pack(expand=1, fill="both", side='left')
+ item = tree.FileTreeItem(tree.ICONDIR)
+ node = tree.TreeNode(sc.canvas, None, item)
+ node.expand()
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_undodelegator.py b/Lib/idlelib/idle_test/test_undo.py
index 2b83c991e2..e872927a6c 100644
--- a/Lib/idlelib/idle_test/test_undodelegator.py
+++ b/Lib/idlelib/idle_test/test_undo.py
@@ -1,4 +1,4 @@
-"""Unittest for UndoDelegator in idlelib.UndoDelegator.
+"""Unittest for UndoDelegator in idlelib.undo.py.
Coverage about 80% (retest).
"""
@@ -8,8 +8,8 @@ requires('gui')
import unittest
from unittest.mock import Mock
from tkinter import Text, Tk
-from idlelib.UndoDelegator import UndoDelegator
-from idlelib.Percolator import Percolator
+from idlelib.undo import UndoDelegator
+from idlelib.percolator import Percolator
class UndoDelegatorTest(unittest.TestCase):
@@ -29,8 +29,8 @@ class UndoDelegatorTest(unittest.TestCase):
def setUp(self):
self.delegator = UndoDelegator()
+ self.delegator.bell = Mock()
self.percolator.insertfilter(self.delegator)
- self.delegator.bell = Mock(wraps=self.delegator.bell)
def tearDown(self):
self.percolator.removefilter(self.delegator)
diff --git a/Lib/idlelib/idle_test/test_warning.py b/Lib/idlelib/idle_test/test_warning.py
index 18627ddd23..f3269f195a 100644
--- a/Lib/idlelib/idle_test/test_warning.py
+++ b/Lib/idlelib/idle_test/test_warning.py
@@ -1,4 +1,4 @@
-'''Test warnings replacement in PyShell.py and run.py.
+'''Test warnings replacement in pyshell.py and run.py.
This file could be expanded to include traceback overrides
(in same two modules). If so, change name.
@@ -17,9 +17,9 @@ showwarning = warnings.showwarning
running_in_idle = 'idle' in showwarning.__name__
from idlelib import run
-from idlelib import PyShell as shell
+from idlelib import pyshell as shell
-# The following was generated from PyShell.idle_formatwarning
+# The following was generated from pyshell.idle_formatwarning
# and checked as matching expectation.
idlemsg = '''
Warning (from warnings module):
diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py
deleted file mode 100644
index 3e9f69a3e3..0000000000
--- a/Lib/idlelib/idlever.py
+++ /dev/null
@@ -1,12 +0,0 @@
-"""
-The separate Idle version was eliminated years ago;
-idlelib.idlever is no longer used by Idle
-and will be removed in 3.6 or later. Use
- from sys import version
- IDLE_VERSION = version[:version.index(' ')]
-"""
-# Kept for now only for possible existing extension use
-import warnings as w
-w.warn(__doc__, DeprecationWarning, stacklevel=2)
-from sys import version
-IDLE_VERSION = version[:version.index(' ')]
diff --git a/Lib/idlelib/IOBinding.py b/Lib/idlelib/iomenu.py
index 84f39a2fee..3414c7b3af 100644
--- a/Lib/idlelib/IOBinding.py
+++ b/Lib/idlelib/iomenu.py
@@ -10,57 +10,60 @@ import tkinter.filedialog as tkFileDialog
import tkinter.messagebox as tkMessageBox
from tkinter.simpledialog import askstring
-from idlelib.configHandler import idleConf
+import idlelib
+from idlelib.config import idleConf
-
-# Try setting the locale, so that we can find out
-# what encoding to use
-try:
- import locale
- locale.setlocale(locale.LC_CTYPE, "")
-except (ImportError, locale.Error):
- pass
-
-# Encoding for file names
-filesystemencoding = sys.getfilesystemencoding() ### currently unused
-
-locale_encoding = 'ascii'
-if sys.platform == 'win32':
- # On Windows, we could use "mbcs". However, to give the user
- # a portable encoding name, we need to find the code page
- try:
- locale_encoding = locale.getdefaultlocale()[1]
- codecs.lookup(locale_encoding)
- except LookupError:
- pass
+if idlelib.testing: # Set True by test.test_idle to avoid setlocale.
+ encoding = 'utf-8'
else:
+ # Try setting the locale, so that we can find out
+ # what encoding to use
try:
- # Different things can fail here: the locale module may not be
- # loaded, it may not offer nl_langinfo, or CODESET, or the
- # resulting codeset may be unknown to Python. We ignore all
- # these problems, falling back to ASCII
- locale_encoding = locale.nl_langinfo(locale.CODESET)
- if locale_encoding is None or locale_encoding is '':
- # situation occurs on Mac OS X
- locale_encoding = 'ascii'
- codecs.lookup(locale_encoding)
- except (NameError, AttributeError, LookupError):
- # Try getdefaultlocale: it parses environment variables,
- # which may give a clue. Unfortunately, getdefaultlocale has
- # bugs that can cause ValueError.
+ import locale
+ locale.setlocale(locale.LC_CTYPE, "")
+ except (ImportError, locale.Error):
+ pass
+
+ locale_decode = 'ascii'
+ if sys.platform == 'win32':
+ # On Windows, we could use "mbcs". However, to give the user
+ # a portable encoding name, we need to find the code page
try:
locale_encoding = locale.getdefaultlocale()[1]
+ codecs.lookup(locale_encoding)
+ except LookupError:
+ pass
+ else:
+ try:
+ # Different things can fail here: the locale module may not be
+ # loaded, it may not offer nl_langinfo, or CODESET, or the
+ # resulting codeset may be unknown to Python. We ignore all
+ # these problems, falling back to ASCII
+ locale_encoding = locale.nl_langinfo(locale.CODESET)
if locale_encoding is None or locale_encoding is '':
# situation occurs on Mac OS X
locale_encoding = 'ascii'
codecs.lookup(locale_encoding)
- except (ValueError, LookupError):
- pass
+ except (NameError, AttributeError, LookupError):
+ # Try getdefaultlocale: it parses environment variables,
+ # which may give a clue. Unfortunately, getdefaultlocale has
+ # bugs that can cause ValueError.
+ try:
+ locale_encoding = locale.getdefaultlocale()[1]
+ if locale_encoding is None or locale_encoding is '':
+ # situation occurs on Mac OS X
+ locale_encoding = 'ascii'
+ codecs.lookup(locale_encoding)
+ except (ValueError, LookupError):
+ pass
-locale_encoding = locale_encoding.lower()
+ locale_encoding = locale_encoding.lower()
-encoding = locale_encoding ### KBK 07Sep07 This is used all over IDLE, check!
- ### 'encoding' is used below in encode(), check!
+ encoding = locale_encoding
+ # Encoding is used in multiple files; locale_encoding nowhere.
+ # The only use of 'encoding' below is in _decode as initial value
+ # of deprecated block asking user for encoding.
+ # Perhaps use elsewhere should be reviewed.
coding_re = re.compile(r'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)', re.ASCII)
blank_re = re.compile(r'^[ \t\f]*(?:[#\r\n]|$)', re.ASCII)
@@ -107,6 +110,9 @@ def coding_spec(data):
class IOBinding:
+# One instance per editor Window so methods know which to save, close.
+# Open returns focus to self.editwin if aborted.
+# EditorWindow.open_module, others, belong here.
def __init__(self, editwin):
self.editwin = editwin
@@ -301,7 +307,7 @@ class IOBinding:
"The file's encoding is invalid for Python 3.x.\n"
"IDLE will convert it to UTF-8.\n"
"What is the current encoding of the file?",
- initialvalue = locale_encoding,
+ initialvalue = encoding,
parent = self.editwin.text)
if enc:
@@ -529,8 +535,8 @@ def _io_binding(parent): # htest #
root = Toplevel(parent)
root.title("Test IOBinding")
- width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
- root.geometry("+%d+%d"%(x, y + 150))
+ x, y = map(int, parent.geometry().split('+')[1:])
+ root.geometry("+%d+%d" % (x, y + 175))
class MyEditWin:
def __init__(self, text):
self.text = text
@@ -561,5 +567,8 @@ def _io_binding(parent): # htest #
IOBinding(editwin)
if __name__ == "__main__":
+ import unittest
+ unittest.main('idlelib.idle_test.test_iomenu', verbosity=2, exit=False)
+
from idlelib.idle_test.htest import run
run(_io_binding)
diff --git a/Lib/idlelib/macosxSupport.py b/Lib/idlelib/macosx.py
index b96bae1d55..c225dd9e1a 100644
--- a/Lib/idlelib/macosxSupport.py
+++ b/Lib/idlelib/macosx.py
@@ -1,30 +1,25 @@
"""
A number of functions that enhance IDLE on Mac OSX.
"""
-import sys
-import tkinter
-from os import path
+from sys import platform # Used in _init_tk_type, changed by test.
import warnings
-def runningAsOSXApp():
- warnings.warn("runningAsOSXApp() is deprecated, use isAquaTk()",
- DeprecationWarning, stacklevel=2)
- return isAquaTk()
+import tkinter
+
-def isCarbonAquaTk(root):
- warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()",
- DeprecationWarning, stacklevel=2)
- return isCarbonTk()
+## Define functions that query the Mac graphics type.
+## _tk_type and its initializer are private to this section.
_tk_type = None
-def _initializeTkVariantTests(root):
+def _init_tk_type():
"""
Initializes OS X Tk variant values for
isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
"""
global _tk_type
- if sys.platform == 'darwin':
+ if platform == 'darwin':
+ root = tkinter.Tk()
ws = root.tk.call('tk', 'windowingsystem')
if 'x11' in ws:
_tk_type = "xquartz"
@@ -34,6 +29,7 @@ def _initializeTkVariantTests(root):
_tk_type = "cocoa"
else:
_tk_type = "carbon"
+ root.destroy()
else:
_tk_type = "other"
@@ -41,7 +37,8 @@ def isAquaTk():
"""
Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
"""
- assert _tk_type is not None
+ if not _tk_type:
+ _init_tk_type()
return _tk_type == "cocoa" or _tk_type == "carbon"
def isCarbonTk():
@@ -49,23 +46,27 @@ def isCarbonTk():
Returns True if IDLE is using a Carbon Aqua Tk (instead of the
newer Cocoa Aqua Tk).
"""
- assert _tk_type is not None
+ if not _tk_type:
+ _init_tk_type()
return _tk_type == "carbon"
def isCocoaTk():
"""
Returns True if IDLE is using a Cocoa Aqua Tk.
"""
- assert _tk_type is not None
+ if not _tk_type:
+ _init_tk_type()
return _tk_type == "cocoa"
def isXQuartz():
"""
Returns True if IDLE is using an OS X X11 Tk.
"""
- assert _tk_type is not None
+ if not _tk_type:
+ _init_tk_type()
return _tk_type == "xquartz"
+
def tkVersionWarning(root):
"""
Returns a string warning message if the Tk version in use appears to
@@ -86,6 +87,9 @@ def tkVersionWarning(root):
else:
return False
+
+## Fix the menu and related functions.
+
def addOpenEventSupport(root, flist):
"""
This ensures that the application will respond to open AppleEvents, which
@@ -124,23 +128,23 @@ def overrideRootMenu(root, flist):
# Due to a (mis-)feature of TkAqua the user will also see an empty Help
# menu.
from tkinter import Menu
- from idlelib import Bindings
- from idlelib import WindowList
+ from idlelib import mainmenu
+ from idlelib import windows
- closeItem = Bindings.menudefs[0][1][-2]
+ closeItem = mainmenu.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)
+ del mainmenu.menudefs[0][1][-3:]
+ mainmenu.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]
+ del mainmenu.menudefs[-1][1][0:2]
# Remove the 'Configure Idle' entry from the options menu, it is in the
# application menu as 'Preferences'
- del Bindings.menudefs[-2][1][0]
+ del mainmenu.menudefs[-2][1][0]
menubar = Menu(root)
root.configure(menu=menubar)
menudict = {}
@@ -155,30 +159,30 @@ def overrideRootMenu(root, flist):
if end > 0:
menu.delete(0, end)
- WindowList.add_windows_to_menu(menu)
- WindowList.register_callback(postwindowsmenu)
+ windows.add_windows_to_menu(menu)
+ windows.register_callback(postwindowsmenu)
def about_dialog(event=None):
"Handle Help 'About IDLE' event."
- # Synchronize with EditorWindow.EditorWindow.about_dialog.
- from idlelib import aboutDialog
- aboutDialog.AboutDialog(root, 'About IDLE')
+ # Synchronize with editor.EditorWindow.about_dialog.
+ from idlelib import help_about
+ help_about.AboutDialog(root, 'About IDLE')
def config_dialog(event=None):
"Handle Options 'Configure IDLE' event."
- # Synchronize with EditorWindow.EditorWindow.config_dialog.
- from idlelib import configDialog
+ # Synchronize with editor.EditorWindow.config_dialog.
+ from idlelib import configdialog
# Ensure that the root object has an instance_dict attribute,
# mirrors code in EditorWindow (although that sets the attribute
# on an EditorWindow instance that is then passed as the first
# argument to ConfigDialog)
root.instance_dict = flist.inversedict
- configDialog.ConfigDialog(root, 'Settings')
+ configdialog.ConfigDialog(root, 'Settings')
def help_dialog(event=None):
"Handle Help 'IDLE Help' event."
- # Synchronize with EditorWindow.EditorWindow.help_dialog.
+ # Synchronize with editor.EditorWindow.help_dialog.
from idlelib import help
help.show_idlehelp(root)
@@ -198,29 +202,33 @@ def overrideRootMenu(root, flist):
menudict['application'] = menu = Menu(menubar, name='apple',
tearoff=0)
menubar.add_cascade(label='IDLE', menu=menu)
- Bindings.menudefs.insert(0,
+ mainmenu.menudefs.insert(0,
('application', [
('About IDLE', '<<about-idle>>'),
None,
]))
- tkversion = root.tk.eval('info patchlevel')
- if tuple(map(int, tkversion.split('.'))) < (8, 4, 14):
- # for earlier AquaTk versions, supply a Preferences menu item
- Bindings.menudefs[0][1].append(
- ('_Preferences....', '<<open-config-dialog>>'),
- )
if isCocoaTk():
# replace default About dialog with About IDLE one
root.createcommand('tkAboutDialog', about_dialog)
# replace default "Help" item in Help menu
root.createcommand('::tk::mac::ShowHelp', help_dialog)
# remove redundant "IDLE Help" from menu
- del Bindings.menudefs[-1][1][0]
+ del mainmenu.menudefs[-1][1][0]
+
+def fixb2context(root):
+ '''Removed bad AquaTk Button-2 (right) and Paste bindings.
+
+ They prevent context menu access and seem to be gone in AquaTk8.6.
+ See issue #24801.
+ '''
+ root.unbind_class('Text', '<B2>')
+ root.unbind_class('Text', '<B2-Motion>')
+ root.unbind_class('Text', '<<PasteSelection>>')
def setupApp(root, flist):
"""
Perform initial OS X customizations if needed.
- Called from PyShell.main() after initial calls to Tk()
+ 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)
@@ -233,8 +241,13 @@ def setupApp(root, flist):
isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
are initialized here as well.
"""
- _initializeTkVariantTests(root)
if isAquaTk():
hideTkConsole(root)
overrideRootMenu(root, flist)
addOpenEventSupport(root, flist)
+ fixb2context(root)
+
+
+if __name__ == '__main__':
+ from unittest import main
+ main('idlelib.idle_test.test_macosx', verbosity=2)
diff --git a/Lib/idlelib/Bindings.py b/Lib/idlelib/mainmenu.py
index e19a279b1b..65345cd1f1 100644
--- a/Lib/idlelib/Bindings.py
+++ b/Lib/idlelib/mainmenu.py
@@ -10,9 +10,9 @@ windows.
"""
from importlib.util import find_spec
-from idlelib.configHandler import idleConf
+from idlelib.config import idleConf
-# Warning: menudefs is altered in macosxSupport.overrideRootMenu()
+# Warning: menudefs is altered in macosx.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
diff --git a/Lib/idlelib/MultiCall.py b/Lib/idlelib/multicall.py
index 8462854921..b74fed4c0c 100644
--- a/Lib/idlelib/MultiCall.py
+++ b/Lib/idlelib/multicall.py
@@ -28,9 +28,9 @@ The order by which events are called is defined by these rules:
unless this conflicts with the first rule.
Each function will be called at most once for each event.
"""
-
-import sys
import re
+import sys
+
import tkinter
# the event type constants, which define the meaning of mc_type
@@ -414,12 +414,12 @@ def MultiCallCreator(widget):
return MultiCall
-def _multi_call(parent):
- root = tkinter.Tk()
- root.title("Test MultiCall")
- width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
- root.geometry("+%d+%d"%(x, y + 150))
- text = MultiCallCreator(tkinter.Text)(root)
+def _multi_call(parent): # htest #
+ top = tkinter.Toplevel(parent)
+ top.title("Test MultiCall")
+ x, y = map(int, parent.geometry().split('+')[1:])
+ top.geometry("+%d+%d" % (x, y + 175))
+ text = MultiCallCreator(tkinter.Text)(top)
text.pack()
def bindseq(seq, n=[0]):
def handler(event):
@@ -439,7 +439,6 @@ def _multi_call(parent):
bindseq("<FocusOut>")
bindseq("<Enter>")
bindseq("<Leave>")
- root.mainloop()
if __name__ == "__main__":
from idlelib.idle_test.htest import run
diff --git a/Lib/idlelib/OutputWindow.py b/Lib/idlelib/outwin.py
index e614f9b2bb..f6d2915c62 100644
--- a/Lib/idlelib/OutputWindow.py
+++ b/Lib/idlelib/outwin.py
@@ -1,8 +1,11 @@
-from tkinter import *
-from idlelib.EditorWindow import EditorWindow
import re
+
+from tkinter import *
import tkinter.messagebox as tkMessageBox
-from idlelib import IOBinding
+
+from idlelib.editor import EditorWindow
+from idlelib import iomenu
+
class OutputWindow(EditorWindow):
@@ -36,7 +39,7 @@ class OutputWindow(EditorWindow):
def write(self, s, tags=(), mark="insert"):
if isinstance(s, (bytes, bytes)):
- s = s.decode(IOBinding.encoding, "replace")
+ s = s.decode(iomenu.encoding, "replace")
self.text.insert(mark, s, tags)
self.text.see(mark)
self.text.update()
diff --git a/Lib/idlelib/FormatParagraph.py b/Lib/idlelib/paragraph.py
index 7a9d185042..f11bdaeb77 100644
--- a/Lib/idlelib/FormatParagraph.py
+++ b/Lib/idlelib/paragraph.py
@@ -14,9 +14,10 @@ Known problems with comment reformatting:
spaces, they will not be considered part of the same block.
* Fancy comments, like this bulleted list, aren't handled :-)
"""
-
import re
-from idlelib.configHandler import idleConf
+
+from idlelib.config import idleConf
+
class FormatParagraph:
@@ -129,7 +130,7 @@ def reformat_paragraph(data, limit):
partial = indent1
while i < n and not is_all_white(lines[i]):
# XXX Should take double space after period (etc.) into account
- words = re.split("(\s+)", lines[i])
+ words = re.split(r"(\s+)", lines[i])
for j in range(0, len(words), 2):
word = words[j]
if not word:
@@ -189,7 +190,8 @@ def get_comment_header(line):
if m is None: return ""
return m.group(1)
+
if __name__ == "__main__":
import unittest
- unittest.main('idlelib.idle_test.test_formatparagraph',
+ unittest.main('idlelib.idle_test.test_paragraph',
verbosity=2, exit=False)
diff --git a/Lib/idlelib/ParenMatch.py b/Lib/idlelib/parenmatch.py
index 47e10f3517..ccec708f31 100644
--- a/Lib/idlelib/ParenMatch.py
+++ b/Lib/idlelib/parenmatch.py
@@ -4,9 +4,8 @@ When you hit a right paren, the cursor should move briefly to the left
paren. Paren here is used generically; the matching applies to
parentheses, square brackets, and curly braces.
"""
-
-from idlelib.HyperParser import HyperParser
-from idlelib.configHandler import idleConf
+from idlelib.hyperparser import HyperParser
+from idlelib.config import idleConf
_openers = {')':'(',']':'[','}':'{'}
CHECK_DELAY = 100 # milliseconds
@@ -64,6 +63,7 @@ class ParenMatch:
# and deactivate_restore (which calls event_delete).
editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME,
self.restore_event)
+ self.bell = self.text.bell if self.BELL else lambda: None
self.counter = 0
self.is_restore_active = 0
self.set_style(self.STYLE)
@@ -93,7 +93,7 @@ class ParenMatch:
indices = (HyperParser(self.editwin, "insert")
.get_surrounding_brackets())
if indices is None:
- self.warn_mismatched()
+ self.bell()
return
self.activate_restore()
self.create_tag(indices)
@@ -109,7 +109,7 @@ class ParenMatch:
return
indices = hp.get_surrounding_brackets(_openers[closer], True)
if indices is None:
- self.warn_mismatched()
+ self.bell()
return
self.activate_restore()
self.create_tag(indices)
@@ -124,10 +124,6 @@ class ParenMatch:
if timer_count == self.counter:
self.restore_event()
- def warn_mismatched(self):
- if self.BELL:
- self.text.bell()
-
# any one of the create_tag_XXX methods can be used depending on
# the style
diff --git a/Lib/idlelib/PathBrowser.py b/Lib/idlelib/pathbrowser.py
index 9ab7632f4d..6c19508d31 100644
--- a/Lib/idlelib/PathBrowser.py
+++ b/Lib/idlelib/pathbrowser.py
@@ -1,10 +1,10 @@
+import importlib.machinery
import os
import sys
-import importlib.machinery
-from idlelib.TreeWidget import TreeItem
-from idlelib.ClassBrowser import ClassBrowser, ModuleBrowserTreeItem
-from idlelib.PyShell import PyShellFileList
+from idlelib.browser import ClassBrowser, ModuleBrowserTreeItem
+from idlelib.pyshell import PyShellFileList
+from idlelib.tree import TreeItem
class PathBrowser(ClassBrowser):
@@ -24,6 +24,7 @@ class PathBrowser(ClassBrowser):
def rootnode(self):
return PathBrowserTreeItem()
+
class PathBrowserTreeItem(TreeItem):
def GetText(self):
@@ -36,6 +37,7 @@ class PathBrowserTreeItem(TreeItem):
sublist.append(item)
return sublist
+
class DirBrowserTreeItem(TreeItem):
def __init__(self, dir, packages=[]):
@@ -95,6 +97,7 @@ class DirBrowserTreeItem(TreeItem):
sorted.sort()
return sorted
+
def _path_browser(parent): # htest #
flist = PyShellFileList(parent)
PathBrowser(flist, _htest=True)
diff --git a/Lib/idlelib/Percolator.py b/Lib/idlelib/percolator.py
index b8be2aa746..d18daf0586 100644
--- a/Lib/idlelib/Percolator.py
+++ b/Lib/idlelib/percolator.py
@@ -1,5 +1,5 @@
-from idlelib.WidgetRedirector import WidgetRedirector
-from idlelib.Delegator import Delegator
+from idlelib.delegator import Delegator
+from idlelib.redirector import WidgetRedirector
class Percolator:
@@ -57,7 +57,6 @@ class Percolator:
def _percolator(parent): # htest #
import tkinter as tk
- import re
class Tracer(Delegator):
def __init__(self, name):
@@ -74,8 +73,8 @@ def _percolator(parent): # htest #
box = tk.Toplevel(parent)
box.title("Test Percolator")
- width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
- box.geometry("+%d+%d" % (x, y + 150))
+ x, y = map(int, parent.geometry().split('+')[1:])
+ box.geometry("+%d+%d" % (x, y + 175))
text = tk.Text(box)
p = Percolator(text)
pin = p.insertfilter
@@ -89,10 +88,10 @@ def _percolator(parent): # htest #
(pin if var2.get() else pout)(t2)
text.pack()
- var1 = tk.IntVar()
+ var1 = tk.IntVar(parent)
cb1 = tk.Checkbutton(box, text="Tracer1", command=toggle1, variable=var1)
cb1.pack()
- var2 = tk.IntVar()
+ var2 = tk.IntVar(parent)
cb2 = tk.Checkbutton(box, text="Tracer2", command=toggle2, variable=var2)
cb2.pack()
diff --git a/Lib/idlelib/PyParse.py b/Lib/idlelib/pyparse.py
index 9ccbb25076..6739dfd1a0 100644
--- a/Lib/idlelib/PyParse.py
+++ b/Lib/idlelib/pyparse.py
@@ -1,6 +1,6 @@
+from collections import Mapping
import re
import sys
-from collections import Mapping
# Reason last stmt is continued (or C_NONE if it's not).
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE,
diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/pyshell.py
index 5dec68e580..f3ee391ca0 100755
--- a/Lib/idlelib/PyShell.py
+++ b/Lib/idlelib/pyshell.py
@@ -1,8 +1,27 @@
#! /usr/bin/env python3
+try:
+ from tkinter import *
+except ImportError:
+ print("** IDLE can't import Tkinter.\n"
+ "Your Python may not be configured for Tk. **", file=sys.__stderr__)
+ raise SystemExit(1)
+import tkinter.messagebox as tkMessageBox
+if TkVersion < 8.5:
+ root = Tk() # otherwise create root in main
+ root.withdraw()
+ tkMessageBox.showerror("Idle Cannot Start",
+ "Idle requires tcl/tk 8.5+, not %s." % TkVersion,
+ parent=root)
+ raise SystemExit(1)
+
+from code import InteractiveInterpreter
import getopt
+import io
+import linecache
import os
import os.path
+from platform import python_version, system
import re
import socket
import subprocess
@@ -10,30 +29,20 @@ import sys
import threading
import time
import tokenize
-import io
-
-import linecache
-from code import InteractiveInterpreter
-from platform import python_version, system
-
-try:
- from tkinter import *
-except ImportError:
- print("** IDLE can't import Tkinter.\n"
- "Your Python may not be configured for Tk. **", file=sys.__stderr__)
- sys.exit(1)
-import tkinter.messagebox as tkMessageBox
+import warnings
-from idlelib.EditorWindow import EditorWindow, fixwordbreaks
-from idlelib.FileList import FileList
-from idlelib.ColorDelegator import ColorDelegator
-from idlelib.UndoDelegator import UndoDelegator
-from idlelib.OutputWindow import OutputWindow
-from idlelib.configHandler import idleConf
+from idlelib import testing # bool value
+from idlelib.colorizer import ColorDelegator
+from idlelib.config import idleConf
+from idlelib import debugger
+from idlelib import debugger_r
+from idlelib.editor import EditorWindow, fixwordbreaks
+from idlelib.filelist import FileList
+from idlelib import macosx
+from idlelib.outwin import OutputWindow
from idlelib import rpc
-from idlelib import Debugger
-from idlelib import RemoteDebugger
-from idlelib import macosxSupport
+from idlelib.run import idle_formatwarning, PseudoInputFile, PseudoOutputFile
+from idlelib.undo import UndoDelegator
HOST = '127.0.0.1' # python execution server on localhost loopback
PORT = 0 # someday pass in host, port for remote debug capability
@@ -43,20 +52,6 @@ PORT = 0 # someday pass in host, port for remote debug capability
# temporarily redirect the stream to the shell window to display warnings when
# checking user's code.
warning_stream = sys.__stderr__ # None, at least on Windows, if no console.
-import warnings
-
-def idle_formatwarning(message, category, filename, lineno, line=None):
- """Format warnings the IDLE way."""
-
- s = "\nWarning (from warnings module):\n"
- s += ' File \"%s\", line %s\n' % (filename, lineno)
- if line is None:
- line = linecache.getline(filename, lineno)
- line = line.strip()
- if line:
- s += " %s\n" % line
- s += "%s: %s\n" % (category.__name__, message)
- return s
def idle_showwarning(
message, category, filename, lineno, file=None, line=None):
@@ -410,7 +405,7 @@ class ModifiedInterpreter(InteractiveInterpreter):
# run from the IDLE source directory.
del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc',
default=False, type='bool')
- if __name__ == 'idlelib.PyShell':
+ if __name__ == 'idlelib.pyshell':
command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,)
else:
command = "__import__('run').main(%r)" % (del_exitf,)
@@ -468,7 +463,7 @@ class ModifiedInterpreter(InteractiveInterpreter):
if debug:
try:
# Only close subprocess debugger, don't unregister gui_adap!
- RemoteDebugger.close_subprocess_debugger(self.rpcclt)
+ debugger_r.close_subprocess_debugger(self.rpcclt)
except:
pass
# Kill subprocess, spawn a new one, accept connection.
@@ -497,7 +492,7 @@ class ModifiedInterpreter(InteractiveInterpreter):
# restart subprocess debugger
if debug:
# Restarted debugger connects to current instance of debug GUI
- RemoteDebugger.restart_subprocess_debugger(self.rpcclt)
+ debugger_r.restart_subprocess_debugger(self.rpcclt)
# reload remote debugger breakpoints for all PyShellEditWindows
debug.load_breakpoints()
self.compile.compiler.flags = self.original_compiler_flags
@@ -578,7 +573,7 @@ class ModifiedInterpreter(InteractiveInterpreter):
if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
self.remote_stack_viewer()
elif how == "ERROR":
- errmsg = "PyShell.ModifiedInterpreter: Subprocess ERROR:\n"
+ errmsg = "pyshell.ModifiedInterpreter: Subprocess ERROR:\n"
print(errmsg, what, file=sys.__stderr__)
print(errmsg, what, file=console)
# we received a response to the currently active seq number:
@@ -613,13 +608,13 @@ class ModifiedInterpreter(InteractiveInterpreter):
return
def remote_stack_viewer(self):
- from idlelib import RemoteObjectBrowser
+ from idlelib import debugobj_r
oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {})
if oid is None:
self.tkconsole.root.bell()
return
- item = RemoteObjectBrowser.StubObjectTreeItem(self.rpcclt, oid)
- from idlelib.TreeWidget import ScrolledCanvas, TreeNode
+ item = debugobj_r.StubObjectTreeItem(self.rpcclt, oid)
+ from idlelib.tree import ScrolledCanvas, TreeNode
top = Toplevel(self.tkconsole.root)
theme = idleConf.CurrentTheme()
background = idleConf.GetHighlight(theme, 'normal')['background']
@@ -662,9 +657,9 @@ class ModifiedInterpreter(InteractiveInterpreter):
# at the moment, InteractiveInterpreter expects str
assert isinstance(source, str)
#if isinstance(source, str):
- # from idlelib import IOBinding
+ # from idlelib import iomenu
# try:
- # source = source.encode(IOBinding.encoding)
+ # source = source.encode(iomenu.encoding)
# except UnicodeError:
# self.tkconsole.resetoutput()
# self.write("Unsupported characters in input\n")
@@ -850,7 +845,7 @@ class PyShell(OutputWindow):
# New classes
- from idlelib.IdleHistory import History
+ from idlelib.history import History
def __init__(self, flist=None):
if use_subprocess:
@@ -888,11 +883,11 @@ class PyShell(OutputWindow):
self.save_stdout = sys.stdout
self.save_stderr = sys.stderr
self.save_stdin = sys.stdin
- from idlelib import IOBinding
- self.stdin = PseudoInputFile(self, "stdin", IOBinding.encoding)
- self.stdout = PseudoOutputFile(self, "stdout", IOBinding.encoding)
- self.stderr = PseudoOutputFile(self, "stderr", IOBinding.encoding)
- self.console = PseudoOutputFile(self, "console", IOBinding.encoding)
+ from idlelib import iomenu
+ self.stdin = PseudoInputFile(self, "stdin", iomenu.encoding)
+ self.stdout = PseudoOutputFile(self, "stdout", iomenu.encoding)
+ self.stderr = PseudoOutputFile(self, "stderr", iomenu.encoding)
+ self.console = PseudoOutputFile(self, "console", iomenu.encoding)
if not use_subprocess:
sys.stdout = self.stdout
sys.stderr = self.stderr
@@ -900,7 +895,7 @@ class PyShell(OutputWindow):
try:
# page help() text to shell.
import pydoc # import must be done here to capture i/o rebinding.
- # XXX KBK 27Dec07 use a textView someday, but must work w/o subproc
+ # XXX KBK 27Dec07 use TextViewer someday, but must work w/o subproc
pydoc.pager = pydoc.plainpager
except:
sys.stderr = sys.__stderr__
@@ -954,7 +949,7 @@ class PyShell(OutputWindow):
self.interp.setdebugger(None)
db.close()
if self.interp.rpcclt:
- RemoteDebugger.close_remote_debugger(self.interp.rpcclt)
+ debugger_r.close_remote_debugger(self.interp.rpcclt)
self.resetoutput()
self.console.write("[DEBUG OFF]\n")
sys.ps1 = ">>> "
@@ -963,10 +958,10 @@ class PyShell(OutputWindow):
def open_debugger(self):
if self.interp.rpcclt:
- dbg_gui = RemoteDebugger.start_remote_debugger(self.interp.rpcclt,
+ dbg_gui = debugger_r.start_remote_debugger(self.interp.rpcclt,
self)
else:
- dbg_gui = Debugger.Debugger(self)
+ dbg_gui = debugger.Debugger(self)
self.interp.setdebugger(dbg_gui)
dbg_gui.load_breakpoints()
sys.ps1 = "[DEBUG ON]\n>>> "
@@ -1241,7 +1236,7 @@ class PyShell(OutputWindow):
"(sys.last_traceback is not defined)",
parent=self.text)
return
- from idlelib.StackViewer import StackBrowser
+ from idlelib.stackviewer import StackBrowser
StackBrowser(self.root, self.flist)
def view_restart_mark(self, event=None):
@@ -1309,92 +1304,6 @@ class PyShell(OutputWindow):
return 'disabled'
return super().rmenu_check_paste()
-class PseudoFile(io.TextIOBase):
-
- def __init__(self, shell, tags, encoding=None):
- self.shell = shell
- self.tags = tags
- self._encoding = encoding
-
- @property
- def encoding(self):
- return self._encoding
-
- @property
- def name(self):
- return '<%s>' % self.tags
-
- def isatty(self):
- return True
-
-
-class PseudoOutputFile(PseudoFile):
-
- def writable(self):
- return True
-
- def write(self, s):
- if self.closed:
- raise ValueError("write to closed file")
- if type(s) is not str:
- if not isinstance(s, str):
- raise TypeError('must be str, not ' + type(s).__name__)
- # See issue #19481
- s = str.__str__(s)
- return self.shell.write(s, self.tags)
-
-
-class PseudoInputFile(PseudoFile):
-
- def __init__(self, shell, tags, encoding=None):
- PseudoFile.__init__(self, shell, tags, encoding)
- self._line_buffer = ''
-
- def readable(self):
- return True
-
- def read(self, size=-1):
- if self.closed:
- raise ValueError("read from closed file")
- if size is None:
- size = -1
- elif not isinstance(size, int):
- raise TypeError('must be int, not ' + type(size).__name__)
- result = self._line_buffer
- self._line_buffer = ''
- if size < 0:
- while True:
- line = self.shell.readline()
- if not line: break
- result += line
- else:
- while len(result) < size:
- line = self.shell.readline()
- if not line: break
- result += line
- self._line_buffer = result[size:]
- result = result[:size]
- return result
-
- def readline(self, size=-1):
- if self.closed:
- raise ValueError("read from closed file")
- if size is None:
- size = -1
- elif not isinstance(size, int):
- raise TypeError('must be int, not ' + type(size).__name__)
- line = self._line_buffer or self.shell.readline()
- if size < 0:
- size = len(line)
- eol = line.find('\n', 0, size)
- if eol >= 0:
- size = eol + 1
- self._line_buffer = line[size:]
- return line[:size]
-
- def close(self):
- self.shell.close()
-
def fix_x11_paste(root):
"Make paste replace selection on x11. See issue #5124."
@@ -1540,7 +1449,10 @@ def main():
enable_edit = enable_edit or edit_start
enable_shell = enable_shell or not enable_edit
- # start editor and/or shell windows:
+ # Setup root. Don't break user code run in IDLE process.
+ # Don't change environment when testing.
+ if use_subprocess and not testing:
+ NoDefaultRoot()
root = Tk(className="Idle")
root.withdraw()
@@ -1549,25 +1461,19 @@ def main():
if system() == 'Windows':
iconfile = os.path.join(icondir, 'idle.ico')
root.wm_iconbitmap(default=iconfile)
- elif TkVersion >= 8.5:
+ else:
ext = '.png' if TkVersion >= 8.6 else '.gif'
iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext))
for size in (16, 32, 48)]
- icons = [PhotoImage(file=iconfile) for iconfile in iconfiles]
+ icons = [PhotoImage(master=root, file=iconfile)
+ for iconfile in iconfiles]
root.wm_iconphoto(True, *icons)
+ # start editor and/or shell windows:
fixwordbreaks(root)
fix_x11_paste(root)
flist = PyShellFileList(root)
- macosxSupport.setupApp(root, flist)
-
- if macosxSupport.isAquaTk():
- # There are some screwed up <2> class bindings for text
- # widgets defined in Tk which we need to do away with.
- # See issue #24801.
- root.unbind_class('Text', '<B2>')
- root.unbind_class('Text', '<B2-Motion>')
- root.unbind_class('Text', '<<PasteSelection>>')
+ macosx.setupApp(root, flist)
if enable_edit:
if not (cmd or script):
@@ -1582,7 +1488,7 @@ def main():
shell = flist.open_shell()
if not shell:
return # couldn't open shell
- if macosxSupport.isAquaTk() and flist.dict:
+ if macosx.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
@@ -1616,7 +1522,7 @@ def main():
# check for problematic OS X Tk versions and print a warning
# message in the IDLE shell window; this is less intrusive
# than always opening a separate window.
- tkversionwarning = macosxSupport.tkVersionWarning(root)
+ tkversionwarning = macosx.tkVersionWarning(root)
if tkversionwarning:
shell.interp.runcommand("print('%s')" % tkversionwarning)
@@ -1626,7 +1532,7 @@ def main():
capture_warnings(False)
if __name__ == "__main__":
- sys.modules['PyShell'] = sys.modules['__main__']
+ sys.modules['pyshell'] = sys.modules['__main__']
main()
capture_warnings(False) # Make sure turned off; see issue 18081
diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py
new file mode 100644
index 0000000000..3b1f1e25be
--- /dev/null
+++ b/Lib/idlelib/query.py
@@ -0,0 +1,308 @@
+"""
+Dialogs that query users and verify the answer before accepting.
+Use ttk widgets, limiting use to tcl/tk 8.5+, as in IDLE 3.6+.
+
+Query is the generic base class for a popup dialog.
+The user must either enter a valid answer or close the dialog.
+Entries are validated when <Return> is entered or [Ok] is clicked.
+Entries are ignored when [Cancel] or [X] are clicked.
+The 'return value' is .result set to either a valid answer or None.
+
+Subclass SectionName gets a name for a new config file section.
+Configdialog uses it for new highlight theme and keybinding set names.
+Subclass ModuleName gets a name for File => Open Module.
+Subclass HelpSource gets menu item and path for additions to Help menu.
+"""
+# Query and Section name result from splitting GetCfgSectionNameDialog
+# of configSectionNameDialog.py (temporarily config_sec.py) into
+# generic and specific parts. 3.6 only, July 2016.
+# ModuleName.entry_ok came from editor.EditorWindow.load_module.
+# HelpSource was extracted from configHelpSourceEdit.py (temporarily
+# config_help.py), with darwin code moved from ok to path_ok.
+
+import importlib
+import os
+from sys import executable, platform # Platform is set for one test.
+
+from tkinter import Toplevel, StringVar, W, E, N, S
+from tkinter.ttk import Frame, Button, Entry, Label
+from tkinter import filedialog
+from tkinter.font import Font
+
+class Query(Toplevel):
+ """Base class for getting verified answer from a user.
+
+ For this base class, accept any non-blank string.
+ """
+ def __init__(self, parent, title, message, *, text0='', used_names={},
+ _htest=False, _utest=False):
+ """Create popup, do not return until tk widget destroyed.
+
+ Additional subclass init must be done before calling this
+ unless _utest=True is passed to suppress wait_window().
+
+ title - string, title of popup dialog
+ message - string, informational message to display
+ text0 - initial value for entry
+ used_names - names already in use
+ _htest - bool, change box location when running htest
+ _utest - bool, leave window hidden and not modal
+ """
+ Toplevel.__init__(self, parent)
+ self.withdraw() # Hide while configuring, especially geometry.
+ self.parent = parent
+ self.title(title)
+ self.message = message
+ self.text0 = text0
+ self.used_names = used_names
+ self.transient(parent)
+ self.grab_set()
+ windowingsystem = self.tk.call('tk', 'windowingsystem')
+ if windowingsystem == 'aqua':
+ try:
+ self.tk.call('::tk::unsupported::MacWindowStyle', 'style',
+ self._w, 'moveableModal', '')
+ except:
+ pass
+ self.bind("<Command-.>", self.cancel)
+ self.bind('<Key-Escape>', self.cancel)
+ self.protocol("WM_DELETE_WINDOW", self.cancel)
+ self.bind('<Key-Return>', self.ok)
+ self.bind("<KP_Enter>", self.ok)
+ self.resizable(height=False, width=False)
+ self.create_widgets()
+ self.update_idletasks() # Needed here for winfo_reqwidth below.
+ self.geometry( # Center dialog over parent (or below htest box).
+ "+%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)
+ ) )
+ if not _utest:
+ self.deiconify() # Unhide now that geometry set.
+ self.wait_window()
+
+ def create_widgets(self): # Call from override, if any.
+ # Bind to self widgets needed for entry_ok or unittest.
+ self.frame = frame = Frame(self, padding=10)
+ frame.grid(column=0, row=0, sticky='news')
+ frame.grid_columnconfigure(0, weight=1)
+
+ entrylabel = Label(frame, anchor='w', justify='left',
+ text=self.message)
+ self.entryvar = StringVar(self, self.text0)
+ self.entry = Entry(frame, width=30, textvariable=self.entryvar)
+ self.entry.focus_set()
+ self.error_font = Font(name='TkCaptionFont',
+ exists=True, root=self.parent)
+ self.entry_error = Label(frame, text=' ', foreground='red',
+ font=self.error_font)
+ self.button_ok = Button(
+ frame, text='OK', default='active', command=self.ok)
+ self.button_cancel = Button(
+ frame, text='Cancel', command=self.cancel)
+
+ entrylabel.grid(column=0, row=0, columnspan=3, padx=5, sticky=W)
+ self.entry.grid(column=0, row=1, columnspan=3, padx=5, sticky=W+E,
+ pady=[10,0])
+ self.entry_error.grid(column=0, row=2, columnspan=3, padx=5,
+ sticky=W+E)
+ self.button_ok.grid(column=1, row=99, padx=5)
+ self.button_cancel.grid(column=2, row=99, padx=5)
+
+ def showerror(self, message, widget=None):
+ #self.bell(displayof=self)
+ (widget or self.entry_error)['text'] = 'ERROR: ' + message
+
+ def entry_ok(self): # Example: usually replace.
+ "Return non-blank entry or None."
+ self.entry_error['text'] = ''
+ entry = self.entry.get().strip()
+ if not entry:
+ self.showerror('blank line.')
+ return None
+ return entry
+
+ def ok(self, event=None): # Do not replace.
+ '''If entry is valid, bind it to 'result' and destroy tk widget.
+
+ Otherwise leave dialog open for user to correct entry or cancel.
+ '''
+ entry = self.entry_ok()
+ if entry is not None:
+ self.result = entry
+ self.destroy()
+ else:
+ # [Ok] moves focus. (<Return> does not.) Move it back.
+ self.entry.focus_set()
+
+ def cancel(self, event=None): # Do not replace.
+ "Set dialog result to None and destroy tk widget."
+ self.result = None
+ self.destroy()
+
+
+class SectionName(Query):
+ "Get a name for a config file section name."
+ # Used in ConfigDialog.GetNewKeysName, .GetNewThemeName (837)
+
+ def __init__(self, parent, title, message, used_names,
+ *, _htest=False, _utest=False):
+ super().__init__(parent, title, message, used_names=used_names,
+ _htest=_htest, _utest=_utest)
+
+ def entry_ok(self):
+ "Return sensible ConfigParser section name or None."
+ self.entry_error['text'] = ''
+ name = self.entry.get().strip()
+ if not name:
+ self.showerror('no name specified.')
+ return None
+ elif len(name)>30:
+ self.showerror('name is longer than 30 characters.')
+ return None
+ elif name in self.used_names:
+ self.showerror('name is already in use.')
+ return None
+ return name
+
+
+class ModuleName(Query):
+ "Get a module name for Open Module menu entry."
+ # Used in open_module (editor.EditorWindow until move to iobinding).
+
+ def __init__(self, parent, title, message, text0,
+ *, _htest=False, _utest=False):
+ super().__init__(parent, title, message, text0=text0,
+ _htest=_htest, _utest=_utest)
+
+ def entry_ok(self):
+ "Return entered module name as file path or None."
+ self.entry_error['text'] = ''
+ name = self.entry.get().strip()
+ if not name:
+ self.showerror('no name specified.')
+ return None
+ # XXX Ought to insert current file's directory in front of path.
+ try:
+ spec = importlib.util.find_spec(name)
+ except (ValueError, ImportError) as msg:
+ self.showerror(str(msg))
+ return None
+ if spec is None:
+ self.showerror("module not found")
+ return None
+ if not isinstance(spec.loader, importlib.abc.SourceLoader):
+ self.showerror("not a source-based module")
+ return None
+ try:
+ file_path = spec.loader.get_filename(name)
+ except AttributeError:
+ self.showerror("loader does not support get_filename",
+ parent=self)
+ return None
+ return file_path
+
+
+class HelpSource(Query):
+ "Get menu name and help source for Help menu."
+ # Used in ConfigDialog.HelpListItemAdd/Edit, (941/9)
+
+ def __init__(self, parent, title, *, menuitem='', filepath='',
+ used_names={}, _htest=False, _utest=False):
+ """Get menu entry and url/local file for Additional Help.
+
+ User enters a name for the Help resource and a web url or file
+ name. The user can browse for the file.
+ """
+ self.filepath = filepath
+ message = 'Name for item on Help menu:'
+ super().__init__(
+ parent, title, message, text0=menuitem,
+ used_names=used_names, _htest=_htest, _utest=_utest)
+
+ def create_widgets(self):
+ super().create_widgets()
+ frame = self.frame
+ pathlabel = Label(frame, anchor='w', justify='left',
+ text='Help File Path: Enter URL or browse for file')
+ self.pathvar = StringVar(self, self.filepath)
+ self.path = Entry(frame, textvariable=self.pathvar, width=40)
+ browse = Button(frame, text='Browse', width=8,
+ command=self.browse_file)
+ self.path_error = Label(frame, text=' ', foreground='red',
+ font=self.error_font)
+
+ pathlabel.grid(column=0, row=10, columnspan=3, padx=5, pady=[10,0],
+ sticky=W)
+ self.path.grid(column=0, row=11, columnspan=2, padx=5, sticky=W+E,
+ pady=[10,0])
+ browse.grid(column=2, row=11, padx=5, sticky=W+S)
+ self.path_error.grid(column=0, row=12, columnspan=3, padx=5,
+ sticky=W+E)
+
+ def askfilename(self, filetypes, initdir, initfile): # htest #
+ # Extracted from browse_file so can mock for unittests.
+ # Cannot unittest as cannot simulate button clicks.
+ # Test by running htest, such as by running this file.
+ return filedialog.Open(parent=self, filetypes=filetypes)\
+ .show(initialdir=initdir, initialfile=initfile)
+
+ def browse_file(self):
+ filetypes = [
+ ("HTML Files", "*.htm *.html", "TEXT"),
+ ("PDF Files", "*.pdf", "TEXT"),
+ ("Windows Help Files", "*.chm"),
+ ("Text Files", "*.txt", "TEXT"),
+ ("All Files", "*")]
+ path = self.pathvar.get()
+ if path:
+ dir, base = os.path.split(path)
+ else:
+ base = None
+ if platform[:3] == 'win':
+ dir = os.path.join(os.path.dirname(executable), 'Doc')
+ if not os.path.isdir(dir):
+ dir = os.getcwd()
+ else:
+ dir = os.getcwd()
+ file = self.askfilename(filetypes, dir, base)
+ if file:
+ self.pathvar.set(file)
+
+ item_ok = SectionName.entry_ok # localize for test override
+
+ def path_ok(self):
+ "Simple validity check for menu file path"
+ path = self.path.get().strip()
+ if not path: #no path specified
+ self.showerror('no help file path specified.', self.path_error)
+ return None
+ elif not path.startswith(('www.', 'http')):
+ if path[:5] == 'file:':
+ path = path[5:]
+ if not os.path.exists(path):
+ self.showerror('help file path does not exist.',
+ self.path_error)
+ return None
+ if platform == 'darwin': # for Mac Safari
+ path = "file://" + path
+ return path
+
+ def entry_ok(self):
+ "Return apparently valid (name, path) or None"
+ self.entry_error['text'] = ''
+ self.path_error['text'] = ''
+ name = self.item_ok()
+ path = self.path_ok()
+ return None if name is None or path is None else (name, path)
+
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main('idlelib.idle_test.test_query', verbosity=2, exit=False)
+
+ from idlelib.idle_test.htest import run
+ run(Query, HelpSource)
diff --git a/Lib/idlelib/WidgetRedirector.py b/Lib/idlelib/redirector.py
index b66be9e721..ec681de38d 100644
--- a/Lib/idlelib/WidgetRedirector.py
+++ b/Lib/idlelib/redirector.py
@@ -104,7 +104,7 @@ class WidgetRedirector:
Note that if a registered function is called, the operation is not
passed through to Tk. Apply the function returned by self.register()
- to *args to accomplish that. For an example, see ColorDelegator.py.
+ to *args to accomplish that. For an example, see colorizer.py.
'''
m = self._operations.get(operation)
@@ -151,14 +151,13 @@ class OriginalCommand:
def _widget_redirector(parent): # htest #
- from tkinter import Tk, Text
- import re
-
- root = Tk()
- root.title("Test WidgetRedirector")
- width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
- root.geometry("+%d+%d"%(x, y + 150))
- text = Text(root)
+ from tkinter import Toplevel, Text
+
+ top = Toplevel(parent)
+ top.title("Test WidgetRedirector")
+ x, y = map(int, parent.geometry().split('+')[1:])
+ top.geometry("+%d+%d" % (x, y + 175))
+ text = Text(top)
text.pack()
text.focus_set()
redir = WidgetRedirector(text)
@@ -166,11 +165,11 @@ def _widget_redirector(parent): # htest #
print("insert", args)
original_insert(*args)
original_insert = redir.register("insert", my_insert)
- root.mainloop()
if __name__ == "__main__":
import unittest
- unittest.main('idlelib.idle_test.test_widgetredir',
+ unittest.main('idlelib.idle_test.test_redirector',
verbosity=2, exit=False)
+
from idlelib.idle_test.htest import run
run(_widget_redirector)
diff --git a/Lib/idlelib/ReplaceDialog.py b/Lib/idlelib/replace.py
index f2ea22e7f7..abd9e59f4e 100644
--- a/Lib/idlelib/ReplaceDialog.py
+++ b/Lib/idlelib/replace.py
@@ -3,18 +3,18 @@ Uses idlelib.SearchEngine for search capability.
Defines various replace related functions like replace, replace all,
replace+find.
"""
-from tkinter import *
-
-from idlelib import SearchEngine
-from idlelib.SearchDialogBase import SearchDialogBase
import re
+from tkinter import StringVar, TclError
+
+from idlelib.searchbase import SearchDialogBase
+from idlelib import searchengine
def replace(text):
"""Returns a singleton ReplaceDialog instance.The single dialog
saves user entries and preferences across instances."""
root = text._root()
- engine = SearchEngine.get(root)
+ engine = searchengine.get(root)
if not hasattr(engine, "_replacedialog"):
engine._replacedialog = ReplaceDialog(root, engine)
dialog = engine._replacedialog
@@ -95,7 +95,7 @@ class ReplaceDialog(SearchDialogBase):
text = self.text
res = self.engine.search_text(text, prog)
if not res:
- text.bell()
+ self.bell()
return
text.tag_remove("sel", "1.0", "end")
text.tag_remove("hit", "1.0", "end")
@@ -142,7 +142,7 @@ class ReplaceDialog(SearchDialogBase):
text = self.text
res = self.engine.search_text(text, None, ok)
if not res:
- text.bell()
+ self.bell()
return False
line, m = res
i, j = m.span()
@@ -164,7 +164,7 @@ class ReplaceDialog(SearchDialogBase):
pos = None
if not pos:
first = last = pos = text.index("insert")
- line, col = SearchEngine.get_line_col(pos)
+ line, col = searchengine.get_line_col(pos)
chars = text.get("%d.0" % line, "%d.0" % (line+1))
m = prog.match(chars, col)
if not prog:
@@ -204,11 +204,13 @@ class ReplaceDialog(SearchDialogBase):
def _replace_dialog(parent): # htest #
- """htest wrapper function"""
+ from tkinter import Toplevel, Text, END, SEL
+ from tkinter.ttk import Button
+
box = Toplevel(parent)
box.title("Test ReplaceDialog")
- width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
- box.geometry("+%d+%d"%(x, y + 150))
+ x, y = map(int, parent.geometry().split('+')[1:])
+ box.geometry("+%d+%d" % (x, y + 175))
# mock undo delegator methods
def undo_block_start():
@@ -234,7 +236,7 @@ def _replace_dialog(parent): # htest #
if __name__ == '__main__':
import unittest
- unittest.main('idlelib.idle_test.test_replacedialog',
+ unittest.main('idlelib.idle_test.test_replace',
verbosity=2, exit=False)
from idlelib.idle_test.htest import run
diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py
index 48105f2aa1..8f57edb836 100644
--- a/Lib/idlelib/rpc.py
+++ b/Lib/idlelib/rpc.py
@@ -26,23 +26,21 @@ See the Idle run.main() docstring for further information on how this was
accomplished in Idle.
"""
-
-import sys
-import os
+import builtins
+import copyreg
import io
-import socket
+import marshal
+import os
+import pickle
+import queue
import select
+import socket
import socketserver
import struct
-import pickle
+import sys
import threading
-import queue
import traceback
-import copyreg
import types
-import marshal
-import builtins
-
def unpickle_code(ms):
co = marshal.loads(ms)
@@ -60,10 +58,12 @@ def dumps(obj, protocol=None):
p.dump(obj)
return f.getvalue()
+
class CodePickler(pickle.Pickler):
dispatch_table = {types.CodeType: pickle_code}
dispatch_table.update(copyreg.dispatch_table)
+
BUFSIZE = 8*1024
LOCALHOST = '127.0.0.1'
@@ -487,16 +487,19 @@ class RemoteObject(object):
# Token mix-in class
pass
+
def remoteref(obj):
oid = id(obj)
objecttable[oid] = obj
return RemoteProxy(oid)
+
class RemoteProxy(object):
def __init__(self, oid):
self.oid = oid
+
class RPCHandler(socketserver.BaseRequestHandler, SocketIO):
debugging = False
@@ -514,6 +517,7 @@ class RPCHandler(socketserver.BaseRequestHandler, SocketIO):
def get_remote_proxy(self, oid):
return RPCProxy(self, oid)
+
class RPCClient(SocketIO):
debugging = False
@@ -539,6 +543,7 @@ class RPCClient(SocketIO):
def get_remote_proxy(self, oid):
return RPCProxy(self, oid)
+
class RPCProxy(object):
__methods = None
@@ -587,6 +592,7 @@ def _getattributes(obj, attributes):
if not callable(attr):
attributes[name] = 1
+
class MethodProxy(object):
def __init__(self, sockio, oid, name):
diff --git a/Lib/idlelib/RstripExtension.py b/Lib/idlelib/rstrip.py
index 2ce3c7eafe..2ce3c7eafe 100644
--- a/Lib/idlelib/RstripExtension.py
+++ b/Lib/idlelib/rstrip.py
diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py
index 28ce4200a9..afa9744a34 100644
--- a/Lib/idlelib/run.py
+++ b/Lib/idlelib/run.py
@@ -1,33 +1,45 @@
-import sys
+import io
import linecache
+import queue
+import sys
import time
import traceback
import _thread as thread
import threading
-import queue
-import tkinter
-
-from idlelib import CallTips
-from idlelib import AutoComplete
+import warnings
-from idlelib import RemoteDebugger
-from idlelib import RemoteObjectBrowser
-from idlelib import StackViewer
-from idlelib import rpc
-from idlelib import PyShell
-from idlelib import IOBinding
+import tkinter # Tcl, deletions, messagebox if startup fails
+from idlelib import autocomplete # AutoComplete, fetch_encodings
+from idlelib import calltips # CallTips
+from idlelib import debugger_r # start_debugger
+from idlelib import debugobj_r # remote_object_tree_item
+from idlelib import iomenu # encoding
+from idlelib import rpc # multiple objects
+from idlelib import stackviewer # StackTreeItem
import __main__
for mod in ('simpledialog', 'messagebox', 'font',
'dialog', 'filedialog', 'commondialog',
- 'colorchooser'):
+ 'ttk'):
delattr(tkinter, mod)
del sys.modules['tkinter.' + mod]
LOCALHOST = '127.0.0.1'
-import warnings
+
+def idle_formatwarning(message, category, filename, lineno, line=None):
+ """Format warnings the IDLE way."""
+
+ s = "\nWarning (from warnings module):\n"
+ s += ' File \"%s\", line %s\n' % (filename, lineno)
+ if line is None:
+ line = linecache.getline(filename, lineno)
+ line = line.strip()
+ if line:
+ s += " %s\n" % line
+ s += "%s: %s\n" % (category.__name__, message)
+ return s
def idle_showwarning_subproc(
message, category, filename, lineno, file=None, line=None):
@@ -38,7 +50,7 @@ def idle_showwarning_subproc(
if file is None:
file = sys.stderr
try:
- file.write(PyShell.idle_formatwarning(
+ file.write(idle_formatwarning(
message, category, filename, lineno, line))
except IOError:
pass # the file (probably stderr) is invalid - this warning gets lost.
@@ -88,7 +100,7 @@ def main(del_exitfunc=False):
MyHandler object. That reference is saved as attribute rpchandler of the
Executive instance. The Executive methods have access to the reference and
can pass it on to entities that they command
- (e.g. RemoteDebugger.Debugger.start_debugger()). The latter, in turn, can
+ (e.g. debugger_r.Debugger.start_debugger()). The latter, in turn, can
call MyHandler(SocketIO) register/unregister methods via the reference to
register and unregister themselves.
@@ -210,7 +222,7 @@ def print_exception():
tbe = traceback.extract_tb(tb)
print('Traceback (most recent call last):', file=efile)
exclude = ("run.py", "rpc.py", "threading.py", "queue.py",
- "RemoteDebugger.py", "bdb.py")
+ "debugger_r.py", "bdb.py")
cleanup_traceback(tbe, exclude)
traceback.print_list(tbe, file=efile)
lines = traceback.format_exception_only(typ, exc)
@@ -267,6 +279,7 @@ def exit():
capture_warnings(False)
sys.exit(0)
+
class MyRPCServer(rpc.RPCServer):
def handle_error(self, request, client_address):
@@ -297,6 +310,96 @@ class MyRPCServer(rpc.RPCServer):
quitting = True
thread.interrupt_main()
+
+# Pseudofiles for shell-remote communication (also used in pyshell)
+
+class PseudoFile(io.TextIOBase):
+
+ def __init__(self, shell, tags, encoding=None):
+ self.shell = shell
+ self.tags = tags
+ self._encoding = encoding
+
+ @property
+ def encoding(self):
+ return self._encoding
+
+ @property
+ def name(self):
+ return '<%s>' % self.tags
+
+ def isatty(self):
+ return True
+
+
+class PseudoOutputFile(PseudoFile):
+
+ def writable(self):
+ return True
+
+ def write(self, s):
+ if self.closed:
+ raise ValueError("write to closed file")
+ if type(s) is not str:
+ if not isinstance(s, str):
+ raise TypeError('must be str, not ' + type(s).__name__)
+ # See issue #19481
+ s = str.__str__(s)
+ return self.shell.write(s, self.tags)
+
+
+class PseudoInputFile(PseudoFile):
+
+ def __init__(self, shell, tags, encoding=None):
+ PseudoFile.__init__(self, shell, tags, encoding)
+ self._line_buffer = ''
+
+ def readable(self):
+ return True
+
+ def read(self, size=-1):
+ if self.closed:
+ raise ValueError("read from closed file")
+ if size is None:
+ size = -1
+ elif not isinstance(size, int):
+ raise TypeError('must be int, not ' + type(size).__name__)
+ result = self._line_buffer
+ self._line_buffer = ''
+ if size < 0:
+ while True:
+ line = self.shell.readline()
+ if not line: break
+ result += line
+ else:
+ while len(result) < size:
+ line = self.shell.readline()
+ if not line: break
+ result += line
+ self._line_buffer = result[size:]
+ result = result[:size]
+ return result
+
+ def readline(self, size=-1):
+ if self.closed:
+ raise ValueError("read from closed file")
+ if size is None:
+ size = -1
+ elif not isinstance(size, int):
+ raise TypeError('must be int, not ' + type(size).__name__)
+ line = self._line_buffer or self.shell.readline()
+ if size < 0:
+ size = len(line)
+ eol = line.find('\n', 0, size)
+ if eol >= 0:
+ size = eol + 1
+ self._line_buffer = line[size:]
+ return line[:size]
+
+ def close(self):
+ self.shell.close()
+
+
class MyHandler(rpc.RPCHandler):
def handle(self):
@@ -304,12 +407,12 @@ class MyHandler(rpc.RPCHandler):
executive = Executive(self)
self.register("exec", executive)
self.console = self.get_remote_proxy("console")
- sys.stdin = PyShell.PseudoInputFile(self.console, "stdin",
- IOBinding.encoding)
- sys.stdout = PyShell.PseudoOutputFile(self.console, "stdout",
- IOBinding.encoding)
- sys.stderr = PyShell.PseudoOutputFile(self.console, "stderr",
- IOBinding.encoding)
+ sys.stdin = PseudoInputFile(self.console, "stdin",
+ iomenu.encoding)
+ sys.stdout = PseudoOutputFile(self.console, "stdout",
+ iomenu.encoding)
+ sys.stderr = PseudoOutputFile(self.console, "stderr",
+ iomenu.encoding)
sys.displayhook = rpc.displayhook
# page help() text to shell.
@@ -345,8 +448,8 @@ class Executive(object):
def __init__(self, rpchandler):
self.rpchandler = rpchandler
self.locals = __main__.__dict__
- self.calltip = CallTips.CallTips()
- self.autocomplete = AutoComplete.AutoComplete()
+ self.calltip = calltips.CallTips()
+ self.autocomplete = autocomplete.AutoComplete()
def runcode(self, code):
global interruptable
@@ -378,7 +481,7 @@ class Executive(object):
thread.interrupt_main()
def start_the_debugger(self, gui_adap_oid):
- return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
+ return debugger_r.start_debugger(self.rpchandler, gui_adap_oid)
def stop_the_debugger(self, idb_adap_oid):
"Unregister the Idb Adapter. Link objects and Idb then subject to GC"
@@ -402,7 +505,7 @@ class Executive(object):
tb = tb.tb_next
sys.last_type = typ
sys.last_value = val
- item = StackViewer.StackTreeItem(flist, tb)
- return RemoteObjectBrowser.remote_object_tree_item(item)
+ item = stackviewer.StackTreeItem(flist, tb)
+ return debugobj_r.remote_object_tree_item(item)
capture_warnings(False) # Make sure turned off; see issue 18081
diff --git a/Lib/idlelib/ScriptBinding.py b/Lib/idlelib/runscript.py
index 5cb818d833..79d86adabc 100644
--- a/Lib/idlelib/ScriptBinding.py
+++ b/Lib/idlelib/runscript.py
@@ -20,11 +20,12 @@ XXX GvR Redesign this interface (yet again) as follows:
import os
import tabnanny
import tokenize
+
import tkinter.messagebox as tkMessageBox
-from idlelib import PyShell
-from idlelib.configHandler import idleConf
-from idlelib import macosxSupport
+from idlelib.config import idleConf
+from idlelib import macosx
+from idlelib import pyshell
indent_message = """Error: Inconsistent indentation detected!
@@ -46,12 +47,12 @@ class ScriptBinding:
def __init__(self, editwin):
self.editwin = editwin
- # Provide instance variables referenced by Debugger
+ # Provide instance variables referenced by debugger
# XXX This should be done differently
self.flist = self.editwin.flist
self.root = self.editwin.root
- if macosxSupport.isCocoaTk():
+ if macosx.isCocoaTk():
self.editwin.text_frame.bind('<<run-module-event-2>>', self._run_module_event)
def check_module_event(self, event):
@@ -112,7 +113,7 @@ class ScriptBinding:
shell.set_warning_stream(saved_stream)
def run_module_event(self, event):
- if macosxSupport.isCocoaTk():
+ if macosx.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
@@ -142,7 +143,7 @@ class ScriptBinding:
if not self.tabnanny(filename):
return 'break'
interp = self.shell.interp
- if PyShell.use_subprocess:
+ if pyshell.use_subprocess:
interp.restart_subprocess(with_cwd=False, filename=
self.editwin._filename_to_unicode(filename))
dirname = os.path.dirname(filename)
@@ -161,7 +162,7 @@ class ScriptBinding:
interp.prepend_syspath(filename)
# XXX KBK 03Jul04 When run w/o subprocess, runtime warnings still
# go to __stderr__. With subprocess, they go to the shell.
- # Need to change streams in PyShell.ModifiedInterpreter.
+ # Need to change streams in pyshell.ModifiedInterpreter.
interp.runcode(code)
return 'break'
diff --git a/Lib/idlelib/ScrolledList.py b/Lib/idlelib/scrolledlist.py
index 53576b5f82..cc08c26e5e 100644
--- a/Lib/idlelib/ScrolledList.py
+++ b/Lib/idlelib/scrolledlist.py
@@ -1,5 +1,8 @@
from tkinter import *
-from idlelib import macosxSupport
+from tkinter.ttk import Scrollbar
+
+from idlelib import macosx
+
class ScrolledList:
@@ -23,7 +26,7 @@ class ScrolledList:
# Bind events to the list box
listbox.bind("<ButtonRelease-1>", self.click_event)
listbox.bind("<Double-ButtonRelease-1>", self.double_click_event)
- if macosxSupport.isAquaTk():
+ if macosx.isAquaTk():
listbox.bind("<ButtonPress-2>", self.popup_event)
listbox.bind("<Control-Button-1>", self.popup_event)
else:
@@ -124,22 +127,20 @@ class ScrolledList:
pass
-def _scrolled_list(parent):
- root = Tk()
- root.title("Test ScrolledList")
- width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
- root.geometry("+%d+%d"%(x, y + 150))
+def _scrolled_list(parent): # htest #
+ top = Toplevel(parent)
+ x, y = map(int, parent.geometry().split('+')[1:])
+ top.geometry("+%d+%d" % (x+200, y + 175))
class MyScrolledList(ScrolledList):
def fill_menu(self): self.menu.add_command(label="right click")
def on_select(self, index): print("select", self.get(index))
def on_double(self, index): print("double", self.get(index))
- scrolled_list = MyScrolledList(root)
+ scrolled_list = MyScrolledList(top)
for i in range(30):
scrolled_list.append("Item %02d" % i)
- root.mainloop()
-
if __name__ == '__main__':
+ # At the moment, test_scrolledlist merely creates instance, like htest.
from idlelib.idle_test.htest import run
run(_scrolled_list)
diff --git a/Lib/idlelib/SearchDialog.py b/Lib/idlelib/search.py
index 765d53fe92..4b90659308 100644
--- a/Lib/idlelib/SearchDialog.py
+++ b/Lib/idlelib/search.py
@@ -1,12 +1,12 @@
-from tkinter import *
+from tkinter import TclError
-from idlelib import SearchEngine
-from idlelib.SearchDialogBase import SearchDialogBase
+from idlelib import searchengine
+from idlelib.searchbase import SearchDialogBase
def _setup(text):
"Create or find the singleton SearchDialog instance."
root = text._root()
- engine = SearchEngine.get(root)
+ engine = searchengine.get(root)
if not hasattr(engine, "_searchdialog"):
engine._searchdialog = SearchDialog(root, engine)
return engine._searchdialog
@@ -24,6 +24,7 @@ def find_selection(text):
"Handle the editor edit menu item and corresponding event."
return _setup(text).find_selection(text)
+
class SearchDialog(SearchDialogBase):
def create_widgets(self):
@@ -51,7 +52,7 @@ class SearchDialog(SearchDialogBase):
selfirst = text.index("sel.first")
sellast = text.index("sel.last")
if selfirst == first and sellast == last:
- text.bell()
+ self.bell()
return False
except TclError:
pass
@@ -61,7 +62,7 @@ class SearchDialog(SearchDialogBase):
text.see("insert")
return True
else:
- text.bell()
+ self.bell()
return False
def find_selection(self, text):
@@ -72,26 +73,30 @@ class SearchDialog(SearchDialogBase):
def _search_dialog(parent): # htest #
- '''Display search test box.'''
+ "Display search test box."
+ from tkinter import Toplevel, Text
+ from tkinter.ttk import Button
+
box = Toplevel(parent)
box.title("Test SearchDialog")
- width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
- box.geometry("+%d+%d"%(x, y + 150))
+ x, y = map(int, parent.geometry().split('+')[1:])
+ box.geometry("+%d+%d" % (x, y + 175))
text = Text(box, inactiveselectbackground='gray')
text.pack()
text.insert("insert","This is a sample string.\n"*5)
def show_find():
- text.tag_add(SEL, "1.0", END)
+ text.tag_add('sel', '1.0', 'end')
_setup(text).open(text)
- text.tag_remove(SEL, "1.0", END)
+ text.tag_remove('sel', '1.0', 'end')
button = Button(box, text="Search (selection ignored)", command=show_find)
button.pack()
if __name__ == '__main__':
import unittest
- unittest.main('idlelib.idle_test.test_searchdialog',
+ unittest.main('idlelib.idle_test.test_search',
verbosity=2, exit=False)
+
from idlelib.idle_test.htest import run
run(_search_dialog)
diff --git a/Lib/idlelib/SearchDialogBase.py b/Lib/idlelib/searchbase.py
index 5fa84e238b..5f81785b71 100644
--- a/Lib/idlelib/SearchDialogBase.py
+++ b/Lib/idlelib/searchbase.py
@@ -1,7 +1,8 @@
'''Define SearchDialogBase used by Search, Replace, and Grep dialogs.'''
-from tkinter import (Toplevel, Frame, Entry, Label, Button,
- Checkbutton, Radiobutton)
+from tkinter import Toplevel, Frame
+from tkinter.ttk import Entry, Label, Button, Checkbutton, Radiobutton
+
class SearchDialogBase:
'''Create most of a 3 or 4 row, 3 column search dialog.
@@ -79,6 +80,7 @@ class SearchDialogBase:
top.wm_title(self.title)
top.wm_iconname(self.icon)
self.top = top
+ self.bell = top.bell
self.row = 0
self.top.grid_columnconfigure(0, pad=2, weight=0)
@@ -125,7 +127,7 @@ class SearchDialogBase:
def create_option_buttons(self):
'''Return (filled frame, options) for testing.
- Options is a list of SearchEngine booleanvar, label pairs.
+ Options is a list of searchengine booleanvar, label pairs.
A gridded frame from make_frame is filled with a Checkbutton
for each pair, bound to the var, with the corresponding label.
'''
@@ -137,10 +139,8 @@ class SearchDialogBase:
if self.needwrapbutton:
options.append((engine.wrapvar, "Wrap around"))
for var, label in options:
- btn = Checkbutton(frame, anchor="w", variable=var, text=label)
+ btn = Checkbutton(frame, variable=var, text=label)
btn.pack(side="left", fill="both")
- if var.get():
- btn.select()
return frame, options
def create_other_buttons(self):
@@ -153,11 +153,8 @@ class SearchDialogBase:
var = self.engine.backvar
others = [(1, 'Up'), (0, 'Down')]
for val, label in others:
- btn = Radiobutton(frame, anchor="w",
- variable=var, value=val, text=label)
+ btn = Radiobutton(frame, variable=var, value=val, text=label)
btn.pack(side="left", fill="both")
- if var.get() == val:
- btn.select()
return frame, others
def make_button(self, label, command, isdef=0):
@@ -178,7 +175,26 @@ class SearchDialogBase:
b = self.make_button("close", self.close)
b.lower()
+
+class _searchbase(SearchDialogBase): # htest #
+ "Create auto-opening dialog with no text connection."
+
+ def __init__(self, parent):
+ import re
+ from idlelib import searchengine
+
+ self.root = parent
+ self.engine = searchengine.get(parent)
+ self.create_widgets()
+ print(parent.geometry())
+ width,height, x,y = list(map(int, re.split('[x+]', parent.geometry())))
+ self.top.geometry("+%d+%d" % (x + 40, y + 175))
+
+ def default_command(self, dummy): pass
+
if __name__ == '__main__':
import unittest
- unittest.main(
- 'idlelib.idle_test.test_searchdialogbase', verbosity=2)
+ unittest.main('idlelib.idle_test.test_searchbase', verbosity=2, exit=False)
+
+ from idlelib.idle_test.htest import run
+ run(_searchbase)
diff --git a/Lib/idlelib/SearchEngine.py b/Lib/idlelib/searchengine.py
index 37883bf687..253f1b0831 100644
--- a/Lib/idlelib/SearchEngine.py
+++ b/Lib/idlelib/searchengine.py
@@ -1,5 +1,6 @@
'''Define SearchEngine for search dialogs.'''
import re
+
from tkinter import StringVar, BooleanVar, TclError
import tkinter.messagebox as tkMessageBox
@@ -14,6 +15,7 @@ def get(root):
# This creates a cycle that persists until root is deleted.
return root._searchengine
+
class SearchEngine:
"""Handles searching a text widget for Find, Replace, and Grep."""
@@ -57,7 +59,7 @@ class SearchEngine:
def setcookedpat(self, pat):
"Set pattern after escaping if re."
- # called only in SearchDialog.py: 66
+ # called only in search.py: 66
if self.isre():
pat = re.escape(pat)
self.setpat(pat)
@@ -186,6 +188,7 @@ class SearchEngine:
col = len(chars) - 1
return None
+
def search_reverse(prog, chars, col):
'''Search backwards and return an re match object or None.
diff --git a/Lib/idlelib/StackViewer.py b/Lib/idlelib/stackviewer.py
index ccc755ce31..0698def5d4 100644
--- a/Lib/idlelib/StackViewer.py
+++ b/Lib/idlelib/stackviewer.py
@@ -1,12 +1,12 @@
-import os
-import sys
import linecache
+import os
import re
+import sys
+
import tkinter as tk
-from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas
-from idlelib.ObjectBrowser import ObjectTreeItem, make_objecttreeitem
-from idlelib.PyShell import PyShellFileList
+from idlelib.debugobj import ObjectTreeItem, make_objecttreeitem
+from idlelib.tree import TreeNode, TreeItem, ScrolledCanvas
def StackBrowser(root, flist=None, tb=None, top=None):
if top is None:
@@ -17,6 +17,7 @@ def StackBrowser(root, flist=None, tb=None, top=None):
node = TreeNode(sc.canvas, None, item)
node.expand()
+
class StackTreeItem(TreeItem):
def __init__(self, flist=None, tb=None):
@@ -55,6 +56,7 @@ class StackTreeItem(TreeItem):
sublist.append(item)
return sublist
+
class FrameTreeItem(TreeItem):
def __init__(self, info, flist):
@@ -96,6 +98,7 @@ class FrameTreeItem(TreeItem):
if os.path.isfile(filename):
self.flist.gotofileline(filename, lineno)
+
class VariablesTreeItem(ObjectTreeItem):
def GetText(self):
@@ -120,15 +123,14 @@ class VariablesTreeItem(ObjectTreeItem):
sublist.append(item)
return sublist
- def keys(self): # unused, left for possible 3rd party use
- return list(self.object.keys())
-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)
+def _stack_viewer(parent): # htest #
+ from idlelib.pyshell import PyShellFileList
+ top = tk.Toplevel(parent)
+ top.title("Test StackViewer")
+ x, y = map(int, parent.geometry().split('+')[1:])
+ top.geometry("+%d+%d" % (x + 50, y + 175))
+ flist = PyShellFileList(top)
try: # to obtain a traceback object
intentional_name_error
except NameError:
@@ -139,7 +141,7 @@ def _stack_viewer(parent):
sys.last_value = exc_value
sys.last_traceback = exc_tb
- StackBrowser(root, flist=flist, top=root, tb=exc_tb)
+ StackBrowser(top, flist=flist, top=top, tb=exc_tb)
# restore sys to original state
del sys.last_type
diff --git a/Lib/idlelib/MultiStatusBar.py b/Lib/idlelib/statusbar.py
index e82ba9ab2f..8618528d82 100644
--- a/Lib/idlelib/MultiStatusBar.py
+++ b/Lib/idlelib/statusbar.py
@@ -1,16 +1,15 @@
-from tkinter import *
+from tkinter import Frame, Label
+
class MultiStatusBar(Frame):
- def __init__(self, master=None, **kw):
- if master is None:
- master = Tk()
+ def __init__(self, master, **kw):
Frame.__init__(self, master, **kw)
self.labels = {}
- def set_label(self, name, text='', side=LEFT, width=0):
+ def set_label(self, name, text='', side='left', width=0):
if name not in self.labels:
- label = Label(self, borderwidth=0, anchor=W)
+ label = Label(self, borderwidth=0, anchor='w')
label.pack(side=side, pady=0, padx=4)
self.labels[name] = label
else:
@@ -19,28 +18,28 @@ class MultiStatusBar(Frame):
label.config(width=width)
label.config(text=text)
-def _multistatus_bar(parent):
- root = Tk()
- width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
- root.geometry("+%d+%d" %(x, y + 150))
- root.title("Test multistatus bar")
- frame = Frame(root)
- text = Text(frame)
+
+def _multistatus_bar(parent): # htest #
+ from tkinter import Toplevel, Frame, Text, Button
+ top = Toplevel(parent)
+ x, y = map(int, parent.geometry().split('+')[1:])
+ top.geometry("+%d+%d" %(x, y + 175))
+ top.title("Test multistatus bar")
+ frame = Frame(top)
+ text = Text(frame, height=5, width=40)
text.pack()
msb = MultiStatusBar(frame)
msb.set_label("one", "hello")
msb.set_label("two", "world")
- msb.pack(side=BOTTOM, fill=X)
+ 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)
+ button = Button(top, text="Update status", command=change)
+ button.pack(side='bottom')
frame.pack()
- frame.mainloop()
- root.mainloop()
if __name__ == '__main__':
from idlelib.idle_test.htest import run
diff --git a/Lib/idlelib/tabbedpages.py b/Lib/idlelib/tabbedpages.py
index 965f9f8593..4186fa2013 100644
--- a/Lib/idlelib/tabbedpages.py
+++ b/Lib/idlelib/tabbedpages.py
@@ -285,6 +285,7 @@ class TabSet(Frame):
# placed hide it
self.tab_set.lower()
+
class TabbedPageSet(Frame):
"""A Tkinter tabbed-pane widget.
@@ -302,6 +303,7 @@ class TabbedPageSet(Frame):
remove_page() methods.
"""
+
class Page(object):
"""Abstract base class for TabbedPageSet's pages.
@@ -467,31 +469,29 @@ class TabbedPageSet(Frame):
self._tab_set.set_selected_tab(page_name)
-def _tabbed_pages(parent):
- # test dialog
- root=Tk()
- width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
- root.geometry("+%d+%d"%(x, y + 175))
- root.title("Test tabbed pages")
- tabPage=TabbedPageSet(root, page_names=['Foobar','Baz'], n_rows=0,
+
+def _tabbed_pages(parent): # htest #
+ top=Toplevel(parent)
+ x, y = map(int, parent.geometry().split('+')[1:])
+ top.geometry("+%d+%d" % (x, y + 175))
+ top.title("Test tabbed pages")
+ tabPage=TabbedPageSet(top, page_names=['Foobar','Baz'], n_rows=0,
expand_tabs=False,
)
tabPage.pack(side=TOP, expand=TRUE, fill=BOTH)
Label(tabPage.pages['Foobar'].frame, text='Foo', pady=20).pack()
Label(tabPage.pages['Foobar'].frame, text='Bar', pady=20).pack()
Label(tabPage.pages['Baz'].frame, text='Baz').pack()
- entryPgName=Entry(root)
- buttonAdd=Button(root, text='Add Page',
+ entryPgName=Entry(top)
+ buttonAdd=Button(top, text='Add Page',
command=lambda:tabPage.add_page(entryPgName.get()))
- buttonRemove=Button(root, text='Remove Page',
+ buttonRemove=Button(top, text='Remove Page',
command=lambda:tabPage.remove_page(entryPgName.get()))
- labelPgName=Label(root, text='name of page to add/remove:')
+ labelPgName=Label(top, text='name of page to add/remove:')
buttonAdd.pack(padx=5, pady=5)
buttonRemove.pack(padx=5, pady=5)
labelPgName.pack(padx=5)
entryPgName.pack(padx=5)
- root.mainloop()
-
if __name__ == '__main__':
from idlelib.idle_test.htest import run
diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textview.py
index 12ac31962d..adee326e1d 100644
--- a/Lib/idlelib/textView.py
+++ b/Lib/idlelib/textview.py
@@ -1,14 +1,14 @@
"""Simple text browser for IDLE
"""
-
from tkinter import *
-import tkinter.messagebox as tkMessageBox
+from tkinter.ttk import Scrollbar
+from tkinter.messagebox import showerror
+
class TextViewer(Toplevel):
- """A simple text viewer dialog for IDLE
+ "A simple text viewer dialog for IDLE."
- """
def __init__(self, parent, title, text, modal=True, _htest=False):
"""Show the given text in a scrollable window with a 'close' button
@@ -20,11 +20,11 @@ class TextViewer(Toplevel):
"""
Toplevel.__init__(self, parent)
self.configure(borderwidth=5)
- # place dialog below parent if running htest
+ # Place dialog below parent if running htest.
self.geometry("=%dx%d+%d+%d" % (750, 500,
parent.winfo_rootx() + 10,
parent.winfo_rooty() + (10 if not _htest else 100)))
- #elguavas - config placeholders til config stuff completed
+ # TODO: get fg/bg from theme.
self.bg = '#ffffff'
self.fg = '#000000'
@@ -33,9 +33,9 @@ class TextViewer(Toplevel):
self.protocol("WM_DELETE_WINDOW", self.Ok)
self.parent = parent
self.textView.focus_set()
- #key bindings for this dialog
- self.bind('<Return>',self.Ok) #dismiss dialog
- self.bind('<Escape>',self.Ok) #dismiss dialog
+ # Bind keys for closing this dialog.
+ self.bind('<Return>',self.Ok)
+ self.bind('<Escape>',self.Ok)
self.textView.insert(0.0, text)
self.textView.config(state=DISABLED)
@@ -50,7 +50,7 @@ class TextViewer(Toplevel):
self.buttonOk = Button(frameButtons, text='Close',
command=self.Ok, takefocus=FALSE)
self.scrollbarView = Scrollbar(frameText, orient=VERTICAL,
- takefocus=FALSE, highlightthickness=0)
+ takefocus=FALSE)
self.textView = Text(frameText, wrap=WORD, highlightthickness=0,
fg=self.fg, bg=self.bg)
self.scrollbarView.config(command=self.textView.yview)
@@ -72,14 +72,14 @@ def view_file(parent, title, filename, encoding=None, modal=True):
try:
with open(filename, 'r', encoding=encoding) as file:
contents = file.read()
- except IOError:
- tkMessageBox.showerror(title='File Load Error',
- message='Unable to load file %r .' % filename,
- parent=parent)
+ except OSError:
+ showerror(title='File Load Error',
+ message='Unable to load file %r .' % filename,
+ parent=parent)
except UnicodeDecodeError as err:
- tkMessageBox.showerror(title='Unicode Decode Error',
- message=str(err),
- parent=parent)
+ showerror(title='Unicode Decode Error',
+ message=str(err),
+ parent=parent)
else:
return view_text(parent, title, contents, modal)
diff --git a/Lib/idlelib/ToolTip.py b/Lib/idlelib/tooltip.py
index 964107e117..843fb4a7d0 100644
--- a/Lib/idlelib/ToolTip.py
+++ b/Lib/idlelib/tooltip.py
@@ -1,4 +1,4 @@
-# general purpose 'tooltip' routines - currently unused in idlefork
+# general purpose 'tooltip' routines - currently unused in idlelib
# (although the 'calltips' extension is partly based on this code)
# may be useful for some purposes in (or almost in ;) the current project scope
# Ideas gleaned from PySol
@@ -76,21 +76,20 @@ class ListboxToolTip(ToolTipBase):
for item in self.items:
listbox.insert(END, item)
-def _tooltip(parent):
- root = Tk()
- root.title("Test tooltip")
- width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
- root.geometry("+%d+%d"%(x, y + 150))
- label = Label(root, text="Place your mouse over buttons")
+def _tooltip(parent): # htest #
+ top = Toplevel(parent)
+ top.title("Test tooltip")
+ x, y = map(int, parent.geometry().split('+')[1:])
+ top.geometry("+%d+%d" % (x, y + 150))
+ label = Label(top, text="Place your mouse over buttons")
label.pack()
- button1 = Button(root, text="Button 1")
- button2 = Button(root, text="Button 2")
+ button1 = Button(top, text="Button 1")
+ button2 = Button(top, text="Button 2")
button1.pack()
button2.pack()
ToolTip(button1, "This is tooltip text for button1.")
ListboxToolTip(button2, ["This is","multiple line",
"tooltip text","for button2"])
- root.mainloop()
if __name__ == '__main__':
from idlelib.idle_test.htest import run
diff --git a/Lib/idlelib/TreeWidget.py b/Lib/idlelib/tree.py
index a19578fdcb..292ce36184 100644
--- a/Lib/idlelib/TreeWidget.py
+++ b/Lib/idlelib/tree.py
@@ -15,10 +15,12 @@
# - optimize tree redraw after expand of subnode
import os
+
from tkinter import *
+from tkinter.ttk import Scrollbar
-from idlelib import ZoomHeight
-from idlelib.configHandler import idleConf
+from idlelib.config import idleConf
+from idlelib import zoomheight
ICONDIR = "Icons"
@@ -445,22 +447,21 @@ class ScrolledCanvas:
self.canvas.yview_scroll(1, "unit")
return "break"
def zoom_height(self, event):
- ZoomHeight.zoom_height(self.master)
+ zoomheight.zoom_height(self.master)
return "break"
-def _tree_widget(parent):
- root = Tk()
- root.title("Test TreeWidget")
- width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
- root.geometry("+%d+%d"%(x, y + 150))
- sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1)
+def _tree_widget(parent): # htest #
+ top = Toplevel(parent)
+ x, y = map(int, parent.geometry().split('+')[1:])
+ top.geometry("+%d+%d" % (x+50, y+175))
+ sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1)
sc.frame.pack(expand=1, fill="both", side=LEFT)
- item = FileTreeItem(os.getcwd())
+ item = FileTreeItem(ICONDIR)
node = TreeNode(sc.canvas, None, item)
node.expand()
- root.mainloop()
if __name__ == '__main__':
+ # test_tree is currently a copy of this
from idlelib.idle_test.htest import run
run(_tree_widget)
diff --git a/Lib/idlelib/UndoDelegator.py b/Lib/idlelib/undo.py
index 1c2502d818..4332f10993 100644
--- a/Lib/idlelib/UndoDelegator.py
+++ b/Lib/idlelib/undo.py
@@ -1,7 +1,9 @@
import string
-from tkinter import *
-from idlelib.Delegator import Delegator
+from idlelib.delegator import Delegator
+
+# tkintter import not needed because module does not create widgets,
+# although many methods operate on text widget arguments.
#$ event <<redo>>
#$ win <Control-y>
@@ -158,7 +160,6 @@ class UndoDelegator(Delegator):
class Command:
-
# Base class for Undoable commands
tags = None
@@ -204,7 +205,6 @@ class Command:
class InsertCommand(Command):
-
# Undoable insert command
def __init__(self, index1, chars, tags=None):
@@ -262,7 +262,6 @@ class InsertCommand(Command):
class DeleteCommand(Command):
-
# Undoable delete command
def __init__(self, index1, index2=None):
@@ -297,8 +296,8 @@ class DeleteCommand(Command):
text.see('insert')
##sys.__stderr__.write("undo: %s\n" % self)
-class CommandSequence(Command):
+class CommandSequence(Command):
# Wrapper for a sequence of undoable cmds to be undone/redone
# as a unit
@@ -338,13 +337,12 @@ class CommandSequence(Command):
def _undo_delegator(parent): # htest #
- import re
- import tkinter as tk
- from idlelib.Percolator import Percolator
- undowin = tk.Toplevel()
+ from tkinter import Toplevel, Text, Button
+ from idlelib.percolator import Percolator
+ undowin = Toplevel(parent)
undowin.title("Test UndoDelegator")
- width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
- undowin.geometry("+%d+%d"%(x, y + 150))
+ x, y = map(int, parent.geometry().split('+')[1:])
+ undowin.geometry("+%d+%d" % (x, y + 175))
text = Text(undowin, height=10)
text.pack()
@@ -362,7 +360,7 @@ def _undo_delegator(parent): # htest #
if __name__ == "__main__":
import unittest
- unittest.main('idlelib.idle_test.test_undodelegator', verbosity=2,
- exit=False)
+ unittest.main('idlelib.idle_test.test_undo', verbosity=2, exit=False)
+
from idlelib.idle_test.htest import run
run(_undo_delegator)
diff --git a/Lib/idlelib/WindowList.py b/Lib/idlelib/windows.py
index bc74348f56..a3f858aa73 100644
--- a/Lib/idlelib/WindowList.py
+++ b/Lib/idlelib/windows.py
@@ -1,5 +1,6 @@
from tkinter import *
+
class WindowList:
def __init__(self):
@@ -48,6 +49,7 @@ class WindowList:
t, v, tb = sys.exc_info()
print("warning: callback failed in WindowList", t, ":", v)
+
registry = WindowList()
add_windows_to_menu = registry.add_windows_to_menu
diff --git a/Lib/idlelib/ZoomHeight.py b/Lib/idlelib/zoomheight.py
index a5d679e499..aa4a427eab 100644
--- a/Lib/idlelib/ZoomHeight.py
+++ b/Lib/idlelib/zoomheight.py
@@ -3,7 +3,8 @@
import re
import sys
-from idlelib import macosxSupport
+from idlelib import macosx
+
class ZoomHeight:
@@ -20,6 +21,7 @@ class ZoomHeight:
top = self.editwin.top
zoom_height(top)
+
def zoom_height(top):
geom = top.wm_geometry()
m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
@@ -32,7 +34,7 @@ def zoom_height(top):
newy = 0
newheight = newheight - 72
- elif macosxSupport.isAquaTk():
+ elif macosx.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.