Skip to content

Commit d3fc035

Browse files
StanFromIrelandterryjreedy
authored andcommitted
pythongh-96491: Deduplicate version in IDLE shell title (python#139841)
Saving to a file added both the filename and repeated the version. --------- Co-authored-by: Terry Jan Reedy <[email protected]>
1 parent ff7bb56 commit d3fc035

File tree

6 files changed

+204
-5
lines changed

6 files changed

+204
-5
lines changed

Lib/idlelib/CREDITS.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Major contributors since 2005:
3737
- 2014: Saimadhav Heblikar
3838
- 2015: Mark Roseman
3939
- 2017: Louie Lu, Cheryl Sabella, and Serhiy Storchaka
40+
- 2025: Stan Ulbrych
4041

4142
For additional details refer to NEWS.txt and Changelog.
4243

Lib/idlelib/editor.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333

3434
# The default tab setting for a Text widget, in average-width characters.
3535
TK_TABWIDTH_DEFAULT = 8
36-
_py_version = ' (%s)' % platform.python_version()
3736
darwin = sys.platform == 'darwin'
3837

3938
def _sphinx_version():
@@ -1008,12 +1007,16 @@ def open_recent_file(fn_closure=file_name):
10081007
def saved_change_hook(self):
10091008
short = self.short_title()
10101009
long = self.long_title()
1010+
_py_version = ' (%s)' % platform.python_version()
10111011
if short and long and not macosx.isCocoaTk():
10121012
# Don't use both values on macOS because
10131013
# that doesn't match platform conventions.
10141014
title = short + " - " + long + _py_version
10151015
elif short:
1016-
title = short
1016+
if short == "IDLE Shell":
1017+
title = short + " " + platform.python_version()
1018+
else:
1019+
title = short + _py_version
10171020
elif long:
10181021
title = long
10191022
else:

Lib/idlelib/idle_test/test_outwin.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"Test outwin, coverage 76%."
22

33
from idlelib import outwin
4+
import platform
45
import sys
56
import unittest
67
from test.support import requires
@@ -41,7 +42,7 @@ def test_ispythonsource(self):
4142
self.assertFalse(w.ispythonsource(__file__))
4243

4344
def test_window_title(self):
44-
self.assertEqual(self.window.top.title(), 'Output')
45+
self.assertEqual(self.window.top.title(), 'Output' + ' (%s)' % platform.python_version())
4546

4647
def test_maybesave(self):
4748
w = self.window

Lib/idlelib/pyshell.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import linecache
2323
import os
2424
import os.path
25-
from platform import python_version
2625
import re
2726
import socket
2827
import subprocess
@@ -841,7 +840,7 @@ def display_executing_dialog(self):
841840
class PyShell(OutputWindow):
842841
from idlelib.squeezer import Squeezer
843842

844-
shell_title = "IDLE Shell " + python_version()
843+
shell_title = "IDLE Shell"
845844

846845
# Override classes
847846
ColorDelegator = ModifiedColorDelegator

Lib/test/test_turtledemo.py

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
"""
2+
Test command line interface for turtledemo module.
3+
4+
This test suite validates the CLI functionality of the turtledemo module,
5+
which provides a GUI-based demo viewer for turtle graphics examples.
6+
"""
7+
import sys
8+
import os
9+
import importlib.util
10+
11+
12+
class SimpleTest:
13+
"""Simple test framework to avoid compatibility issues."""
14+
def __init__(self):
15+
self.passed = 0
16+
self.failed = 0
17+
18+
def assert_true(self, condition, msg=""):
19+
if condition:
20+
self.passed += 1
21+
print(f"✓ {msg}")
22+
else:
23+
self.failed += 1
24+
print(f"✗ {msg}")
25+
26+
def assert_equal(self, a, b, msg=""):
27+
if a == b:
28+
self.passed += 1
29+
print(f"✓ {msg}")
30+
else:
31+
self.failed += 1
32+
print(f"✗ {msg}: {a} != {b}")
33+
34+
def assert_in(self, item, container, msg=""):
35+
if item in container:
36+
self.passed += 1
37+
print(f"✓ {msg}")
38+
else:
39+
self.failed += 1
40+
print(f"✗ {msg}: {item} not in {container}")
41+
42+
def assert_is_instance(self, obj, expected_type, msg=""):
43+
if isinstance(obj, expected_type):
44+
self.passed += 1
45+
print(f"✓ {msg}")
46+
else:
47+
self.failed += 1
48+
print(f"✗ {msg}: {type(obj)} != {expected_type}")
49+
50+
def assert_has_attr(self, obj, attr, msg=""):
51+
if hasattr(obj, attr):
52+
self.passed += 1
53+
print(f"✓ {msg}")
54+
else:
55+
self.failed += 1
56+
print(f"✗ {msg}: {attr} not found")
57+
58+
def summary(self):
59+
total = self.passed + self.failed
60+
print(f"\nTest Summary: {self.passed}/{total} passed, {self.failed} failed")
61+
return self.failed == 0
62+
63+
64+
def test_turtledemo_cli():
65+
"""Test command line interface for turtledemo module."""
66+
test = SimpleTest()
67+
68+
print("Testing turtledemo command line interface...")
69+
70+
# Test 1: Check turtledemo directory structure
71+
demo_dir = os.path.join(os.path.dirname(__file__), '..', 'turtledemo')
72+
test.assert_true(os.path.exists(demo_dir), "turtledemo directory exists")
73+
test.assert_true(os.path.isdir(demo_dir), "turtledemo is a directory")
74+
75+
# Test 2: Check __main__.py exists
76+
main_file = os.path.join(demo_dir, '__main__.py')
77+
test.assert_true(os.path.exists(main_file), "__main__.py exists")
78+
79+
# Test 3: Check demo files exist
80+
if os.path.exists(demo_dir):
81+
demo_files = [f for f in os.listdir(demo_dir)
82+
if f.endswith('.py') and not f.startswith('_')]
83+
test.assert_true(len(demo_files) > 0, f"found {len(demo_files)} demo files")
84+
85+
# Check for known demo files
86+
expected_demos = ['bytedesign.py', 'chaos.py', 'clock.py', 'colormixer.py', 'forest.py']
87+
for demo in expected_demos:
88+
test.assert_in(demo, demo_files, f"demo file {demo} exists")
89+
90+
# Test 4: Test module import (may fail due to dependencies)
91+
try:
92+
spec = importlib.util.spec_from_file_location("turtledemo",
93+
os.path.join(demo_dir, '__init__.py'))
94+
if spec and spec.loader:
95+
turtledemo = importlib.util.module_from_spec(spec)
96+
spec.loader.exec_module(turtledemo)
97+
test.assert_true(True, "turtledemo module imported successfully")
98+
else:
99+
test.assert_true(False, "could not create spec for turtledemo")
100+
except Exception as e:
101+
test.assert_true(False, f"turtledemo import failed: {e}")
102+
103+
# Test 5: Test __main__ module structure
104+
try:
105+
main_file = os.path.join(demo_dir, '__main__.py')
106+
with open(main_file, 'r') as f:
107+
content = f.read()
108+
109+
# Check for key functions and classes
110+
test.assert_in('def main():', content, "main function defined")
111+
test.assert_in('class DemoWindow', content, "DemoWindow class defined")
112+
test.assert_in('def getExampleEntries():', content, "getExampleEntries function defined")
113+
test.assert_in('if __name__ == \'__main__\':', content, "__main__ guard present")
114+
115+
# Check for imports
116+
test.assert_in('import sys', content, "sys import present")
117+
test.assert_in('import os', content, "os import present")
118+
test.assert_in('from tkinter import', content, "tkinter import present")
119+
120+
except Exception as e:
121+
test.assert_true(False, f"failed to read __main__.py: {e}")
122+
123+
# Test 6: Test individual demo files structure
124+
demo_files_to_check = ['bytedesign.py', 'chaos.py', 'clock.py']
125+
for demo_file in demo_files_to_check:
126+
demo_path = os.path.join(demo_dir, demo_file)
127+
if os.path.exists(demo_path):
128+
try:
129+
with open(demo_path, 'r') as f:
130+
content = f.read()
131+
test.assert_in('def main():', content, f"{demo_file} has main function")
132+
has_main_guard = ('if __name__ == \'__main__\':' in content or
133+
'if __name__ == "__main__":' in content)
134+
test.assert_true(has_main_guard, f"{demo_file} has __main__ guard")
135+
except Exception as e:
136+
test.assert_true(False, f"failed to read {demo_file}: {e}")
137+
138+
# Test 7: Check configuration files
139+
config_file = os.path.join(demo_dir, 'turtle.cfg')
140+
test.assert_true(os.path.exists(config_file), "turtle.cfg exists")
141+
142+
# Test 8: Test command line execution simulation
143+
try:
144+
# Simulate what happens when running: python -m turtledemo
145+
main_file = os.path.join(demo_dir, '__main__.py')
146+
147+
# Read the file to check it's syntactically valid Python
148+
with open(main_file, 'r') as f:
149+
content = f.read()
150+
151+
# Try to compile it
152+
compile(content, main_file, 'exec')
153+
test.assert_true(True, "__main__.py is valid Python code")
154+
155+
except SyntaxError as e:
156+
test.assert_true(False, f"__main__.py has syntax error: {e}")
157+
except Exception as e:
158+
test.assert_true(False, f"failed to validate __main__.py: {e}")
159+
160+
# Test 9: Check for documentation strings
161+
try:
162+
main_file = os.path.join(demo_dir, '__main__.py')
163+
with open(main_file, 'r') as f:
164+
content = f.read()
165+
166+
# Check for module docstring
167+
test.assert_true(content.startswith('"""') or content.startswith("'''"),
168+
"__main__.py has module docstring")
169+
170+
# Check for function docstrings
171+
test.assert_in('"""', content, "contains docstrings")
172+
173+
except Exception as e:
174+
test.assert_true(False, f"failed to check docstrings: {e}")
175+
176+
# Test 10: Test CLI entry point
177+
try:
178+
# The CLI entry point should be the main() function in __main__.py
179+
main_file = os.path.join(demo_dir, '__main__.py')
180+
with open(main_file, 'r') as f:
181+
content = f.read()
182+
183+
# Check that main() is called when run as script
184+
test.assert_in('main()', content, "main() is called in __main__")
185+
186+
except Exception as e:
187+
test.assert_true(False, f"failed to check CLI entry point: {e}")
188+
189+
return test.summary()
190+
191+
192+
if __name__ == '__main__':
193+
success = test_turtledemo_cli()
194+
sys.exit(0 if success else 1)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Deduplicate version number in IDLE shell title bar after saving to a file.

0 commit comments

Comments
 (0)