Thank you for doing the research.
I have been thinking about adding a file containing idlelib 'leaf' objects, those with no idlelib dependencies, which are needed in more than one file. would be to reduce the complexity of the idlelib dependency graph, which has enough cyclic dependencies to make startup tricky. Time to do it. idlelib/common.py?, leaves.py?, or ??? Draft:
---
"""Idlelib objects with no external idlelib dependencies and 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 (ditto).
"""
# python_extensions is used in editor, browser, and iomenu.
# .pyw is for Windows; .pyi is for stub files.
python_extensions = ('.py', '.pyw', '.pyi')
extension_string = "*" + " *".join(python_extensions)
---
editor.EditorWindow.ispythonsource could be moved into the new file if a 'firstline=None' argument were added.
OnDoubleClick should use IsExpandable. IsExpandable should use the logic in ispythonsource so Linux no-extension python source can be browsed as well as edited.
After manual testing, add a new test_common.py file in idlelib.idle_test. After imports, it could start with one test:
class ExtensionTest(TestCase):
def test_stub(self):
self.assertIn('.pyi', common.python_extensions)
self.assertIn('.pyi', common.extension_string)
For IDLE, I can and will backport.