fix(tui): remove type: ignore suppressions and fix formatting in sidebar steps
CI / build (pull_request) Failing after 1m20s
CI / helm (pull_request) Successful in 35s
CI / push-validation (pull_request) Successful in 28s
CI / lint (pull_request) Successful in 3m54s
CI / typecheck (pull_request) Successful in 4m33s
CI / quality (pull_request) Successful in 4m11s
CI / security (pull_request) Successful in 4m33s
CI / unit_tests (pull_request) Failing after 4m32s
CI / docker (pull_request) Has been skipped
CI / integration_tests (pull_request) Successful in 8m17s
CI / e2e_tests (pull_request) Successful in 8m33s
CI / coverage (pull_request) Successful in 13m40s
CI / status-check (pull_request) Failing after 3s

Remove all 47 # type: ignore[attr-defined] comments from
tui_mainscreen_sidebar_steps.py by dropping explicit object type
annotations on behave context parameters (matching the pattern used
by the existing tui_app_coverage_steps.py). Fix ruff format violation.
Add new sidebar public symbols to vulture_whitelist.py.

ISSUES CLOSED: #10025
This commit is contained in:
2026-04-22 22:32:20 +00:00
parent e0de0d0c46
commit ec378a90c6
2 changed files with 94 additions and 80 deletions
+82 -80
View File
@@ -13,7 +13,7 @@ from __future__ import annotations
import importlib
import sys
from types import ModuleType
from typing import ClassVar
from typing import Any, ClassVar
from unittest.mock import MagicMock
from behave import given, then, when
@@ -62,7 +62,7 @@ def _build_mock_textual_for_sidebar() -> dict[str, ModuleType]:
def pop_screen(self) -> None:
self._pop_screen_calls += 1
def compose(self): # type: ignore[return]
def compose(self) -> Any:
return iter([])
class MockApp(MockScreen):
@@ -139,12 +139,12 @@ def _build_mock_textual_for_sidebar() -> dict[str, ModuleType]:
}
def _install_mock_textual_sidebar(context: object) -> None:
def _install_mock_textual_sidebar(context):
"""Inject mock textual into sys.modules and reload the app module."""
mocks = _build_mock_textual_for_sidebar()
context._sidebar_saved_modules = {} # type: ignore[attr-defined]
context._sidebar_saved_modules = {}
for key in _MOCK_TEXTUAL_KEYS:
context._sidebar_saved_modules[key] = sys.modules.pop(key, None) # type: ignore[attr-defined]
context._sidebar_saved_modules[key] = sys.modules.pop(key, None)
for key, mod in mocks.items():
sys.modules[key] = mod
@@ -164,11 +164,11 @@ def _install_mock_textual_sidebar(context: object) -> None:
import cleveragents.tui.app as app_mod
importlib.reload(app_mod)
context._sidebar_app_mod = app_mod # type: ignore[attr-defined]
context._sidebar_mocks = mocks # type: ignore[attr-defined]
context._sidebar_app_mod = app_mod
context._sidebar_mocks = mocks
def _restore_modules_sidebar(context: object) -> None:
def _restore_modules_sidebar(context):
"""Restore original sys.modules and reload the app module."""
for key, val in getattr(context, "_sidebar_saved_modules", {}).items():
if val is None:
@@ -199,9 +199,9 @@ def _restore_modules_sidebar(context: object) -> None:
@given("the TUI app module is loaded with mocked Textual for sidebar tests")
def step_load_with_mock_textual_sidebar(context: object) -> None:
def step_load_with_mock_textual_sidebar(context):
_install_mock_textual_sidebar(context)
context.add_cleanup(lambda: _restore_modules_sidebar(context)) # type: ignore[attr-defined]
context.add_cleanup(lambda: _restore_modules_sidebar(context))
# ---------------------------------------------------------------------------
@@ -210,29 +210,29 @@ def step_load_with_mock_textual_sidebar(context: object) -> None:
@then("SidebarState.HIDDEN should exist in the app module")
def step_sidebar_state_hidden(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
def step_sidebar_state_hidden(context):
mod = context._sidebar_app_mod
assert hasattr(mod, "SidebarState"), "SidebarState not found in app module"
assert hasattr(mod.SidebarState, "HIDDEN"), "SidebarState.HIDDEN not found"
@then("SidebarState.VISIBLE should exist in the app module")
def step_sidebar_state_visible(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
def step_sidebar_state_visible(context):
mod = context._sidebar_app_mod
assert hasattr(mod, "SidebarState"), "SidebarState not found in app module"
assert hasattr(mod.SidebarState, "VISIBLE"), "SidebarState.VISIBLE not found"
@then("SidebarState.FULLSCREEN should exist in the app module")
def step_sidebar_state_fullscreen(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
def step_sidebar_state_fullscreen(context):
mod = context._sidebar_app_mod
assert hasattr(mod, "SidebarState"), "SidebarState not found in app module"
assert hasattr(mod.SidebarState, "FULLSCREEN"), "SidebarState.FULLSCREEN not found"
@then("SidebarState should have exactly 3 members")
def step_sidebar_state_3_members(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
def step_sidebar_state_3_members(context):
mod = context._sidebar_app_mod
assert len(list(mod.SidebarState)) == 3, (
f"Expected 3 SidebarState members, got {len(list(mod.SidebarState))}"
)
@@ -244,44 +244,44 @@ def step_sidebar_state_3_members(context: object) -> None:
@then("MainScreen class should exist in the app module")
def step_mainscreen_exists(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
def step_mainscreen_exists(context):
mod = context._sidebar_app_mod
assert hasattr(mod, "MainScreen"), "MainScreen not found in app module"
@when("I instantiate MainScreen")
def step_instantiate_mainscreen(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
context._mainscreen = mod.MainScreen() # type: ignore[attr-defined]
def step_instantiate_mainscreen(context):
mod = context._sidebar_app_mod
context._mainscreen = mod.MainScreen()
@then("the MainScreen sidebar state should be HIDDEN")
def step_mainscreen_state_hidden(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
assert context._mainscreen._sidebar_state == mod.SidebarState.HIDDEN, ( # type: ignore[attr-defined]
f"Expected HIDDEN, got {context._mainscreen._sidebar_state}" # type: ignore[attr-defined]
def step_mainscreen_state_hidden(context):
mod = context._sidebar_app_mod
assert context._mainscreen._sidebar_state == mod.SidebarState.HIDDEN, (
f"Expected HIDDEN, got {context._mainscreen._sidebar_state}"
)
@then("the MainScreen sidebar state should be VISIBLE")
def step_mainscreen_state_visible(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
assert context._mainscreen._sidebar_state == mod.SidebarState.VISIBLE, ( # type: ignore[attr-defined]
f"Expected VISIBLE, got {context._mainscreen._sidebar_state}" # type: ignore[attr-defined]
def step_mainscreen_state_visible(context):
mod = context._sidebar_app_mod
assert context._mainscreen._sidebar_state == mod.SidebarState.VISIBLE, (
f"Expected VISIBLE, got {context._mainscreen._sidebar_state}"
)
@then("the MainScreen sidebar state should be FULLSCREEN")
def step_mainscreen_state_fullscreen(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
assert context._mainscreen._sidebar_state == mod.SidebarState.FULLSCREEN, ( # type: ignore[attr-defined]
f"Expected FULLSCREEN, got {context._mainscreen._sidebar_state}" # type: ignore[attr-defined]
def step_mainscreen_state_fullscreen(context):
mod = context._sidebar_app_mod
assert context._mainscreen._sidebar_state == mod.SidebarState.FULLSCREEN, (
f"Expected FULLSCREEN, got {context._mainscreen._sidebar_state}"
)
@then("MainScreen should have a shift+tab binding for cycle_sidebar")
def step_mainscreen_shift_tab_binding(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
def step_mainscreen_shift_tab_binding(context):
mod = context._sidebar_app_mod
bindings = mod.MainScreen.BINDINGS
actions = [b[1] for b in bindings]
assert "cycle_sidebar" in actions, (
@@ -297,8 +297,8 @@ def step_mainscreen_shift_tab_binding(context: object) -> None:
@then("MainScreen should have an escape binding for escape_sidebar")
def step_mainscreen_escape_binding(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
def step_mainscreen_escape_binding(context):
mod = context._sidebar_app_mod
bindings = mod.MainScreen.BINDINGS
actions = [b[1] for b in bindings]
assert "escape_sidebar" in actions, (
@@ -318,19 +318,19 @@ def step_mainscreen_escape_binding(context: object) -> None:
@when("I call action_cycle_sidebar on MainScreen")
def step_call_cycle_sidebar(context: object) -> None:
context._mainscreen.action_cycle_sidebar() # type: ignore[attr-defined]
def step_call_cycle_sidebar(context):
context._mainscreen.action_cycle_sidebar()
@when("I call action_escape_sidebar on MainScreen")
def step_call_escape_sidebar(context: object) -> None:
context._mainscreen.action_escape_sidebar() # type: ignore[attr-defined]
def step_call_escape_sidebar(context):
context._mainscreen.action_escape_sidebar()
@then("push_screen should have been called with SidebarFullScreen")
def step_push_screen_called(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
calls = context._mainscreen._push_screen_calls # type: ignore[attr-defined]
def step_push_screen_called(context):
mod = context._sidebar_app_mod
calls = context._mainscreen._push_screen_calls
assert len(calls) > 0, "push_screen was never called"
last_call = calls[-1]
assert isinstance(last_call, mod.SidebarFullScreen), (
@@ -344,37 +344,37 @@ def step_push_screen_called(context: object) -> None:
@then("PlansPanel class should exist in the app module")
def step_planspanel_exists(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
def step_planspanel_exists(context):
mod = context._sidebar_app_mod
assert hasattr(mod, "PlansPanel"), "PlansPanel not found in app module"
@then("ProjectsPanel class should exist in the app module")
def step_projectspanel_exists(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
def step_projectspanel_exists(context):
mod = context._sidebar_app_mod
assert hasattr(mod, "ProjectsPanel"), "ProjectsPanel not found in app module"
@when("I instantiate PlansPanel")
def step_instantiate_planspanel(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
context._planspanel = mod.PlansPanel() # type: ignore[attr-defined]
def step_instantiate_planspanel(context):
mod = context._sidebar_app_mod
context._planspanel = mod.PlansPanel()
@then("PlansPanel instance should be created successfully")
def step_planspanel_created(context: object) -> None:
assert context._planspanel is not None # type: ignore[attr-defined]
def step_planspanel_created(context):
assert context._planspanel is not None
@when("I instantiate ProjectsPanel")
def step_instantiate_projectspanel(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
context._projectspanel = mod.ProjectsPanel() # type: ignore[attr-defined]
def step_instantiate_projectspanel(context):
mod = context._sidebar_app_mod
context._projectspanel = mod.ProjectsPanel()
@then("ProjectsPanel instance should be created successfully")
def step_projectspanel_created(context: object) -> None:
assert context._projectspanel is not None # type: ignore[attr-defined]
def step_projectspanel_created(context):
assert context._projectspanel is not None
# ---------------------------------------------------------------------------
@@ -383,14 +383,16 @@ def step_projectspanel_created(context: object) -> None:
@then("SidebarFullScreen class should exist in the app module")
def step_sidebarfullscreen_exists(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
assert hasattr(mod, "SidebarFullScreen"), "SidebarFullScreen not found in app module"
def step_sidebarfullscreen_exists(context):
mod = context._sidebar_app_mod
assert hasattr(mod, "SidebarFullScreen"), (
"SidebarFullScreen not found in app module"
)
@then("SidebarFullScreen should have an escape binding")
def step_sidebarfullscreen_escape_binding(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
def step_sidebarfullscreen_escape_binding(context):
mod = context._sidebar_app_mod
bindings = mod.SidebarFullScreen.BINDINGS
keys = [b[0] for b in bindings]
assert any("escape" in k.lower() for k in keys), (
@@ -399,14 +401,14 @@ def step_sidebarfullscreen_escape_binding(context: object) -> None:
@when("I instantiate SidebarFullScreen")
def step_instantiate_sidebarfullscreen(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
context._sidebarfullscreen = mod.SidebarFullScreen() # type: ignore[attr-defined]
def step_instantiate_sidebarfullscreen(context):
mod = context._sidebar_app_mod
context._sidebarfullscreen = mod.SidebarFullScreen()
@then("SidebarFullScreen instance should be created successfully")
def step_sidebarfullscreen_created(context: object) -> None:
assert context._sidebarfullscreen is not None # type: ignore[attr-defined]
def step_sidebarfullscreen_created(context):
assert context._sidebarfullscreen is not None
# ---------------------------------------------------------------------------
@@ -415,13 +417,13 @@ def step_sidebarfullscreen_created(context: object) -> None:
@when("I call compose on MainScreen")
def step_call_compose_mainscreen(context: object) -> None:
context._mainscreen_compose_items = list(context._mainscreen.compose()) # type: ignore[attr-defined]
def step_call_compose_mainscreen(context):
context._mainscreen_compose_items = list(context._mainscreen.compose())
@then("MainScreen compose should yield at least {count:d} widget")
def step_mainscreen_compose_count(context: object, count: int) -> None:
items = context._mainscreen_compose_items # type: ignore[attr-defined]
def step_mainscreen_compose_count(context, count):
items = context._mainscreen_compose_items
assert len(items) >= count, (
f"Expected at least {count} widgets from MainScreen.compose(), got {len(items)}"
)
@@ -433,18 +435,18 @@ def step_mainscreen_compose_count(context: object, count: int) -> None:
@then("the sidebar widget should not be displayed when state is HIDDEN")
def step_sidebar_hidden_state(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
screen = context._mainscreen # type: ignore[attr-defined]
def step_sidebar_hidden_state(context):
mod = context._sidebar_app_mod
screen = context._mainscreen
assert screen._sidebar_state == mod.SidebarState.HIDDEN
# Verify the sidebar display property reflects hidden state
assert screen._sidebar_visible is False
@then("the sidebar widget should be displayed when state is VISIBLE")
def step_sidebar_visible_state(context: object) -> None:
mod = context._sidebar_app_mod # type: ignore[attr-defined]
screen = context._mainscreen # type: ignore[attr-defined]
def step_sidebar_visible_state(context):
mod = context._sidebar_app_mod
screen = context._mainscreen
assert screen._sidebar_state == mod.SidebarState.VISIBLE
# Verify the sidebar display property reflects visible state
assert screen._sidebar_visible is True
+12
View File
@@ -1216,3 +1216,15 @@ revert_decisions # noqa: B018, F821
# Extension protocol parameters — required by Protocol interface definitions
destination # noqa: B018, F821
# TUI MainScreen 3-state sidebar (ADR-044) — public API
SidebarState # noqa: B018, F821
PlansPanel # noqa: B018, F821
ProjectsPanel # noqa: B018, F821
MainScreen # noqa: B018, F821
SidebarFullScreen # noqa: B018, F821
_sidebar_state # noqa: B018, F821
_sidebar_visible # noqa: B018, F821
action_cycle_sidebar # noqa: B018, F821
action_escape_sidebar # noqa: B018, F821
action_pop_sidebar # noqa: B018, F821