diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index e8055a054640b4..424e971c8bb316 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -4,6 +4,9 @@ Released on 2022-05-16 ========================= +bpo-28950: Apply IDLE syntax highlighting to `.pyi` files. Add util.py +for common components. Patch by Alex Waygood and Terry Jan Reedy. + bpo-46630: Make query dialogs on Windows start with a cursor in the entry box. diff --git a/Lib/idlelib/README.txt b/Lib/idlelib/README.txt index bc3d978f43f1ad..8870fda315e392 100644 --- a/Lib/idlelib/README.txt +++ b/Lib/idlelib/README.txt @@ -82,6 +82,7 @@ tabbedpages.py # Define tabbed pages widget (nim). textview.py # Define read-only text widget (nim). tree.py # Define tree widget, used in browsers (nim). undo.py # Manage undo stack. +util.py # Define objects imported elsewhere with no dependencies (nim) windows.py # Manage window list and define listed top level. zoomheight.py # Zoom window to full height of screen. diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index fcc8a3f08ccfe3..6c52efd655e121 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -27,6 +27,7 @@ from idlelib import replace from idlelib import search from idlelib.tree import wheel_event +from idlelib.util import py_extensions from idlelib import window # The default tab setting for a Text widget, in average-width characters. @@ -757,7 +758,7 @@ def ispythonsource(self, filename): if not filename or os.path.isdir(filename): return True base, ext = os.path.splitext(os.path.basename(filename)) - if os.path.normcase(ext) in (".py", ".pyw"): + if os.path.normcase(ext) in py_extensions: return True line = self.text.get('1.0', '1.0 lineend') return line.startswith('#!') and 'python' in line diff --git a/Lib/idlelib/idle_test/example_noext b/Lib/idlelib/idle_test/example_noext new file mode 100644 index 00000000000000..7d2510e30bc0ae --- /dev/null +++ b/Lib/idlelib/idle_test/example_noext @@ -0,0 +1,4 @@ +#!usr/bin/env python + +def example_function(some_argument): + pass diff --git a/Lib/idlelib/idle_test/example_stub.pyi b/Lib/idlelib/idle_test/example_stub.pyi new file mode 100644 index 00000000000000..a9811a78d10a6e --- /dev/null +++ b/Lib/idlelib/idle_test/example_stub.pyi @@ -0,0 +1,2 @@ +class Example: + def method(self, argument1: str, argument2: list[int]) -> None: ... diff --git a/Lib/idlelib/idle_test/test_iomenu.py b/Lib/idlelib/idle_test/test_iomenu.py index 99f40487967124..e338893c09e6a1 100644 --- a/Lib/idlelib/idle_test/test_iomenu.py +++ b/Lib/idlelib/idle_test/test_iomenu.py @@ -1,6 +1,6 @@ "Test , coverage 17%." -from idlelib import iomenu +from idlelib import iomenu, util import unittest from test.support import requires from tkinter import Tk @@ -45,5 +45,27 @@ def test_fixnewlines_end(self): eq(fix(), 'a'+io.eol_convention) +def _extension_in_filetypes(extension): + return any( + f'*{extension}' in filetype_tuple[1] + for filetype_tuple in iomenu.IOBinding.filetypes + ) + + +class FiletypesTest(unittest.TestCase): + def test_python_source_files(self): + for extension in util.py_extensions: + with self.subTest(extension=extension): + self.assertTrue( + _extension_in_filetypes(extension) + ) + + def test_text_files(self): + self.assertTrue(_extension_in_filetypes('.txt')) + + def test_all_files(self): + self.assertTrue(_extension_in_filetypes('')) + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/idle_test/test_util.py b/Lib/idlelib/idle_test/test_util.py new file mode 100644 index 00000000000000..20721fe980c784 --- /dev/null +++ b/Lib/idlelib/idle_test/test_util.py @@ -0,0 +1,14 @@ +"""Test util, coverage 100%""" + +import unittest +from idlelib import util + + +class UtilTest(unittest.TestCase): + def test_extensions(self): + for extension in {'.pyi', '.py', '.pyw'}: + self.assertIn(extension, util.py_extensions) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/iomenu.py b/Lib/idlelib/iomenu.py index 5ebf7089fb9abe..ad3109df84096a 100644 --- a/Lib/idlelib/iomenu.py +++ b/Lib/idlelib/iomenu.py @@ -11,6 +11,9 @@ import idlelib from idlelib.config import idleConf +from idlelib.util import py_extensions + +py_extensions = ' '.join("*"+ext for ext in py_extensions) encoding = 'utf-8' if sys.platform == 'win32': @@ -348,7 +351,7 @@ def print_window(self, event): savedialog = None filetypes = ( - ("Python files", "*.py *.pyw", "TEXT"), + ("Python files", py_extensions, "TEXT"), ("Text files", "*.txt", "TEXT"), ("All files", "*"), ) diff --git a/Lib/idlelib/util.py b/Lib/idlelib/util.py new file mode 100644 index 00000000000000..5480219786bca6 --- /dev/null +++ b/Lib/idlelib/util.py @@ -0,0 +1,22 @@ +""" +Idlelib objects with no external idlelib dependencies +which are needed in more than one idlelib module. + +They are included here because + a) they don't particularly belong elsewhere; or + b) because inclusion here simplifies the idlelib dependency graph. + +TODO: + * Python versions (editor and help_about), + * tk version and patchlevel (pyshell, help_about, maxos?, editor?), + * std streams (pyshell, run), + * warning stuff (pyshell, run). +""" +from os import path + +# .pyw is for Windows; .pyi is for stub files. +py_extensions = ('.py', '.pyw', '.pyi') # Order needed for open/save dialogs. + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_util', verbosity=2) diff --git a/Misc/NEWS.d/next/IDLE/2021-10-14-16-55-03.bpo-45447.FhiH5P.rst b/Misc/NEWS.d/next/IDLE/2021-10-14-16-55-03.bpo-45447.FhiH5P.rst new file mode 100644 index 00000000000000..2b5170c7631d24 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2021-10-14-16-55-03.bpo-45447.FhiH5P.rst @@ -0,0 +1,2 @@ +Apply IDLE syntax highlighting to `.pyi` files. Patch by Alex Waygood +and Terry Jan Reedy.