From 93943dc1fa10f664afe89706e1e6f93ce5efcbbd Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Sun, 22 Mar 2026 02:01:54 +0300 Subject: [PATCH] fix: lazy-import tkinter to fix CI on headless Linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit screen_overlay.py imported tkinter at module level, which cascaded through processor_manager → every test touching the app on CI where libtk8.6.so is unavailable. Move to TYPE_CHECKING + runtime lazy import so the overlay module loads cleanly on headless systems. Also fix test_processor_manager.py to use ProcessorDependencies(). --- .../wled_controller/core/capture/screen_overlay.py | 14 ++++++++++++-- server/tests/test_processor_manager.py | 6 +++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/server/src/wled_controller/core/capture/screen_overlay.py b/server/src/wled_controller/core/capture/screen_overlay.py index edbcb7b..477286f 100644 --- a/server/src/wled_controller/core/capture/screen_overlay.py +++ b/server/src/wled_controller/core/capture/screen_overlay.py @@ -1,11 +1,15 @@ """Screen overlay visualization for LED calibration testing.""" +from __future__ import annotations + import colorsys import logging import sys import threading -import tkinter as tk -from typing import Dict, List, Optional +from typing import TYPE_CHECKING, Dict, List, Optional + +if TYPE_CHECKING: + import tkinter as tk from wled_controller.core.capture.calibration import CalibrationConfig from wled_controller.core.capture_engines.base import DisplayInfo @@ -44,6 +48,8 @@ class OverlayWindow: def start(self, root: tk.Tk) -> None: """Create and show the overlay Toplevel (runs in Tk thread).""" + import tkinter as tk # lazy import — tkinter unavailable in headless CI + self._window = tk.Toplevel(root) self._setup_window() self._draw_visualization() @@ -74,6 +80,8 @@ class OverlayWindow: win.overrideredirect(True) win.attributes("-topmost", True) + import tkinter as tk + self._canvas = tk.Canvas( win, width=self.display_info.width, @@ -270,6 +278,8 @@ class OverlayManager: def _start_tk_thread(self) -> None: def _run(): + import tkinter as tk # lazy import — tkinter unavailable in headless CI + try: self._tk_root = tk.Tk() self._tk_root.withdraw() # invisible root — never shown diff --git a/server/tests/test_processor_manager.py b/server/tests/test_processor_manager.py index c696294..c33e958 100644 --- a/server/tests/test_processor_manager.py +++ b/server/tests/test_processor_manager.py @@ -2,7 +2,7 @@ import pytest -from wled_controller.core.processing.processor_manager import ProcessorManager +from wled_controller.core.processing.processor_manager import ProcessorDependencies, ProcessorManager from wled_controller.core.processing.processing_settings import ProcessingSettings from wled_controller.core.capture.calibration import create_default_calibration @@ -33,12 +33,12 @@ def mock_wled_responses(): @pytest.fixture def processor_manager(): """Provide processor manager instance.""" - return ProcessorManager() + return ProcessorManager(deps=ProcessorDependencies()) def test_processor_manager_init(): """Test processor manager initialization.""" - manager = ProcessorManager() + manager = ProcessorManager(deps=ProcessorDependencies()) assert manager is not None assert manager.get_all_devices() == []