Import turtle lazily so tests run without a Tk library

CI on the runner failed at collection: libtk8.6 is missing, so importing
us_states_game.main imported turtle and tkinter. turtle is now imported when
TurtleScreen is created.

Task: MIL-003#7
This commit is contained in:
2026-10-13 16:00:19 +08:00
parent 172778d22b
commit 851fabe76e
2 changed files with 23 additions and 3 deletions
+10 -3
View File
@@ -1,11 +1,13 @@
"""Turtle implementation of the game screen. """Turtle implementation of the game screen.
@file turtle_screen.py @file turtle_screen.py
@brief The only module that imports turtle; draws the map and asks questions. @brief The only module that uses turtle; draws the map and asks questions.
turtle is imported when `TurtleScreen` is created, not when this module is
imported, so the rest of the program and its tests need no Tk library.
""" """
import logging import logging
import turtle
from us_states_game.constants import ( from us_states_game.constants import (
LOGGER_NAME, LOGGER_NAME,
@@ -34,7 +36,12 @@ class TurtleScreen:
"""Game screen drawn with Turtle graphics; implements `GameScreen`.""" """Game screen drawn with Turtle graphics; implements `GameScreen`."""
def __init__(self) -> None: def __init__(self) -> None:
"""Open the window with the blank map.""" """Open the window with the blank map.
turtle (and so Tk) is imported here, at runtime.
"""
import turtle # noqa: PLC0415 # deferred: Tk may be missing at import time
self.screen = turtle.Screen() self.screen = turtle.Screen()
self.screen.setup(SCREEN_WIDTH, SCREEN_HEIGHT) self.screen.setup(SCREEN_WIDTH, SCREEN_HEIGHT)
self.screen.title(SCREEN_TITLE) self.screen.title(SCREEN_TITLE)
+13
View File
@@ -89,3 +89,16 @@ def test_game_and_states_modules_do_not_import_turtle() -> None:
) )
result = subprocess.run([sys.executable, "-c", code], check=False) result = subprocess.run([sys.executable, "-c", code], check=False)
assert result.returncode == 0 assert result.returncode == 0
def test_main_imports_without_tk_library() -> None:
src = Path(__file__).resolve().parent.parent / "src"
code = (
"import sys; "
"sys.modules['tkinter'] = None; sys.modules['_tkinter'] = None; "
f"sys.path.insert(0, {str(src)!r}); "
"import us_states_game.main, us_states_game.turtle_screen; "
"sys.exit(1 if 'turtle' in sys.modules else 0)"
)
result = subprocess.run([sys.executable, "-I", "-c", code], check=False)
assert result.returncode == 0