From 47be2bb276ca8c53b55b49aa8fc11b052eb8bf7c Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Sat, 30 Jul 2022 06:42:13 +0200 Subject: [PATCH] gh-95411: IDLE - Enable using the module browser with .pyw files (GH-95397) Co-authored-by: Terry Jan Reedy (cherry picked from commit 7e19e417b5df765dabab8d6550ec0e9d897c573e) Co-authored-by: Erlend Egeberg Aasland --- Lib/idlelib/NEWS.txt | 2 ++ Lib/idlelib/browser.py | 27 ++++++++++++------- Lib/idlelib/idle_test/test_browser.py | 10 +++++++ ...2-07-29-11-08-52.gh-issue-95411.dazlqH.rst | 1 + 4 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/IDLE/2022-07-29-11-08-52.gh-issue-95411.dazlqH.rst diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 39e76f95226a6f..6d7355355982df 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,8 @@ after 3.10.0 until 3.10.10? Released 2023-04-03? ========================= +gh-95411: Enable using IDLE's module browser with .pyw files. + gh-89610: Add .pyi as a recognized extension for IDLE on macOS. This allows opening stub files by double clicking on them in the Finder. diff --git a/Lib/idlelib/browser.py b/Lib/idlelib/browser.py index 3c3a53a6599a79..10d9a7261113e7 100644 --- a/Lib/idlelib/browser.py +++ b/Lib/idlelib/browser.py @@ -6,7 +6,6 @@ (or recheck on window popup) - add popup menu with more options (e.g. doc strings, base classes, imports) - add base classes to class browser tree -- finish removing limitation to x.py files (ModuleBrowserTreeItem) """ import os @@ -16,12 +15,22 @@ from idlelib.config import idleConf from idlelib import pyshell from idlelib.tree import TreeNode, TreeItem, ScrolledCanvas +from idlelib.util import py_extensions from idlelib.window import ListedToplevel file_open = None # Method...Item and Class...Item use this. # Normally pyshell.flist.open, but there is no pyshell.flist for htest. +# The browser depends on pyclbr and importlib which do not support .pyi files. +browseable_extension_blocklist = ('.pyi',) + + +def is_browseable_extension(path): + _, ext = os.path.splitext(path) + ext = os.path.normcase(ext) + return ext in py_extensions and ext not in browseable_extension_blocklist + def transform_children(child_dict, modname=None): """Transform a child dictionary to an ordered sequence of objects. @@ -76,8 +85,8 @@ def __init__(self, master, path, *, _htest=False, _utest=False): Instance variables: name: Module name. - file: Full path and module with .py extension. Used in - creating ModuleBrowserTreeItem as the rootnode for + file: Full path and module with supported extension. + Used in creating ModuleBrowserTreeItem as the rootnode for the tree and subsequently in the children. """ self.master = master @@ -161,22 +170,22 @@ def GetSubList(self): def OnDoubleClick(self): "Open a module in an editor window when double clicked." - if os.path.normcase(self.file[-3:]) != ".py": + if not is_browseable_extension(self.file): return if not os.path.exists(self.file): return file_open(self.file) def IsExpandable(self): - "Return True if Python (.py) file." - return os.path.normcase(self.file[-3:]) == ".py" + "Return True if Python file." + return is_browseable_extension(self.file) def listchildren(self): "Return sequenced classes and functions in the module." - dir, base = os.path.split(self.file) - name, ext = os.path.splitext(base) - if os.path.normcase(ext) != ".py": + if not is_browseable_extension(self.file): return [] + dir, base = os.path.split(self.file) + name, _ = os.path.splitext(base) try: tree = pyclbr.readmodule_ex(name, [dir] + sys.path) except ImportError: diff --git a/Lib/idlelib/idle_test/test_browser.py b/Lib/idlelib/idle_test/test_browser.py index 03a50f22ca1e82..343d50a6e37bd6 100644 --- a/Lib/idlelib/idle_test/test_browser.py +++ b/Lib/idlelib/idle_test/test_browser.py @@ -5,6 +5,7 @@ import unittest from unittest import mock from idlelib.idle_test.mock_idle import Func +from idlelib.util import py_extensions from collections import deque import os.path @@ -57,6 +58,15 @@ def test_close(self): self.assertTrue(mb.node.destroy.called) del mb.top.destroy, mb.node.destroy + def test_is_browseable_extension(self): + path = "/path/to/file" + for ext in py_extensions: + with self.subTest(ext=ext): + filename = f'{path}{ext}' + actual = browser.is_browseable_extension(filename) + expected = ext not in browser.browseable_extension_blocklist + self.assertEqual(actual, expected) + # Nested tree same as in test_pyclbr.py except for supers on C0. C1. mb = pyclbr diff --git a/Misc/NEWS.d/next/IDLE/2022-07-29-11-08-52.gh-issue-95411.dazlqH.rst b/Misc/NEWS.d/next/IDLE/2022-07-29-11-08-52.gh-issue-95411.dazlqH.rst new file mode 100644 index 00000000000000..94ca8b2c2ea959 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2022-07-29-11-08-52.gh-issue-95411.dazlqH.rst @@ -0,0 +1 @@ +Enable using IDLE's module browser with .pyw files.