mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 21:27:02 +01:00
cups_queue.py 424 -> 245. display_cups_queue_status, _offer_queue_fix, the _dwj_* choices and the _handle_* dispatchers move to _queue_fix.py. The _cups_* action primitives stay put: test_cups_queue.py patches subprocess and time on cups_queue, and those primitives are their only consumers, so moving them would have left subprocess.run unpatched and free to restart the real CUPS service mid-suite. test_cups_queue.py therefore needs no edit; only part2/part3 repoint their MOD. Pass count is 402 before and after. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
204 lines
6.9 KiB
Python
204 lines
6.9 KiB
Python
"""CUPS queue status display and the interactive queue-fix prompt.
|
|
|
|
Split out of :mod:`python_pkg.brother_printer.cups_queue` to keep it under the
|
|
250-line cap. The ``_cups_*`` action primitives deliberately stay in
|
|
``cups_queue``: ``tests/test_cups_queue.py`` patches ``subprocess`` and
|
|
``time`` on that module, and moving them would let a real ``subprocess.run``
|
|
reach the live CUPS daemon.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
from python_pkg.brother_printer.constants import (
|
|
BOLD,
|
|
CYAN,
|
|
DIM,
|
|
GREEN,
|
|
RED,
|
|
RESET,
|
|
YELLOW,
|
|
_out,
|
|
_prompt,
|
|
)
|
|
from python_pkg.brother_printer.cups_queue import (
|
|
_cups_cancel_all_jobs,
|
|
_cups_enable_printer,
|
|
_cups_restart_service,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Callable
|
|
|
|
from python_pkg.brother_printer.data_classes import CUPSQueueStatus
|
|
|
|
|
|
# ── Queue status display ────────────────────────────────────────────
|
|
|
|
|
|
def display_cups_queue_status(queue: CUPSQueueStatus) -> None:
|
|
"""Display CUPS queue status and offer interactive fixes."""
|
|
if not queue.printer_name:
|
|
return
|
|
if queue.enabled and not queue.jobs and not queue.has_backend_errors:
|
|
return
|
|
|
|
_out()
|
|
_out(f"{BOLD}── Print Queue ──{RESET}")
|
|
_out()
|
|
|
|
if queue.has_backend_errors and queue.enabled and not queue.jobs:
|
|
_out(f" {YELLOW}{BOLD}⚡ CUPS backend has stale errors{RESET}")
|
|
_out(
|
|
f" {DIM}New print jobs may silently fail."
|
|
f" A CUPS restart usually fixes this.{RESET}"
|
|
)
|
|
_out()
|
|
|
|
if not queue.enabled:
|
|
_out(f" {RED}{BOLD}⚠ Printer queue is DISABLED{RESET}")
|
|
if queue.reason:
|
|
_out(f" {DIM}Reason: {queue.reason}{RESET}")
|
|
_out()
|
|
|
|
if queue.jobs:
|
|
_out(f" {BOLD}Pending jobs ({len(queue.jobs)}):{RESET}")
|
|
for job in queue.jobs:
|
|
_out(f" {job.job_id} {DIM}{job.user} {job.size}B {job.date}{RESET}")
|
|
_out()
|
|
|
|
_offer_queue_fix(queue)
|
|
|
|
|
|
# ── Interactive queue fix ────────────────────────────────────────────
|
|
|
|
|
|
def _offer_queue_fix(queue: CUPSQueueStatus) -> None:
|
|
"""Prompt the user to fix a disabled queue / pending jobs."""
|
|
_out(f" {BOLD}Available actions:{RESET}")
|
|
|
|
options: list[str] = []
|
|
if not queue.enabled and queue.jobs:
|
|
_out(f" {CYAN}1){RESET} Re-enable printer and retry all jobs")
|
|
_out(f" {CYAN}2){RESET} Re-enable printer and cancel all jobs")
|
|
_out(f" {CYAN}3){RESET} Cancel all jobs (keep printer disabled)")
|
|
_out(f" {CYAN}4){RESET} Restart CUPS service (fixes stale backend)")
|
|
_out(f" {CYAN}5){RESET} Restart CUPS + re-enable + retry all jobs")
|
|
_out(f" {CYAN}6){RESET} Do nothing")
|
|
options = ["1", "2", "3", "4", "5", "6"]
|
|
elif not queue.enabled:
|
|
_out(f" {CYAN}1){RESET} Re-enable printer")
|
|
_out(f" {CYAN}2){RESET} Restart CUPS service (fixes stale backend)")
|
|
_out(f" {CYAN}3){RESET} Do nothing")
|
|
options = ["1", "2", "3"]
|
|
elif queue.jobs:
|
|
_out(f" {CYAN}1){RESET} Cancel all pending jobs")
|
|
_out(f" {CYAN}2){RESET} Restart CUPS service (fixes stale backend)")
|
|
_out(f" {CYAN}3){RESET} Do nothing")
|
|
options = ["1", "2", "3"]
|
|
else:
|
|
_out(f" {CYAN}1){RESET} Restart CUPS service (fixes stale backend)")
|
|
_out(f" {CYAN}2){RESET} Do nothing")
|
|
options = ["1", "2"]
|
|
|
|
_out()
|
|
choice = _prompt(f" Choose [{'/'.join(options)}]: ")
|
|
_out()
|
|
|
|
if not queue.enabled and queue.jobs:
|
|
_handle_disabled_with_jobs(queue, choice)
|
|
elif not queue.enabled:
|
|
_handle_disabled_no_jobs(queue, choice)
|
|
elif queue.jobs:
|
|
_handle_enabled_with_jobs(queue, choice)
|
|
else:
|
|
_handle_backend_errors_only(choice)
|
|
|
|
|
|
def _dwj_enable_only(printer_name: str) -> None:
|
|
"""Choice 1: re-enable printer so queued jobs are retried."""
|
|
if _cups_enable_printer(printer_name):
|
|
_out(f" {GREEN}✓ Printer re-enabled. Jobs will be retried.{RESET}")
|
|
|
|
|
|
def _dwj_cancel_and_enable(printer_name: str) -> None:
|
|
"""Choice 2: cancel all jobs then re-enable."""
|
|
_cups_cancel_all_jobs(printer_name)
|
|
if _cups_enable_printer(printer_name):
|
|
_out(f" {GREEN}✓ All jobs cancelled and printer re-enabled.{RESET}")
|
|
|
|
|
|
def _dwj_cancel_only(printer_name: str) -> None:
|
|
"""Choice 3: cancel all jobs."""
|
|
if _cups_cancel_all_jobs(printer_name):
|
|
_out(f" {GREEN}✓ All jobs cancelled.{RESET}")
|
|
|
|
|
|
def _dwj_restart_only(_printer_name: str) -> None:
|
|
"""Choice 4: restart CUPS."""
|
|
if _cups_restart_service():
|
|
_out(f" {GREEN}✓ CUPS restarted.{RESET}")
|
|
|
|
|
|
def _dwj_restart_and_enable(printer_name: str) -> None:
|
|
"""Choice 5: restart CUPS and re-enable printer."""
|
|
if _cups_restart_service():
|
|
_cups_enable_printer(printer_name)
|
|
_out(
|
|
f" {GREEN}✓ CUPS restarted, printer re-enabled."
|
|
f" Jobs will be retried.{RESET}"
|
|
)
|
|
|
|
|
|
_DWJ_ACTIONS: dict[str, Callable[[str], None]] = {
|
|
"1": _dwj_enable_only,
|
|
"2": _dwj_cancel_and_enable,
|
|
"3": _dwj_cancel_only,
|
|
"4": _dwj_restart_only,
|
|
"5": _dwj_restart_and_enable,
|
|
}
|
|
|
|
|
|
def _handle_disabled_with_jobs(queue: CUPSQueueStatus, choice: str) -> None:
|
|
"""Handle fix for disabled printer with pending jobs."""
|
|
action = _DWJ_ACTIONS.get(choice)
|
|
if action is not None:
|
|
action(queue.printer_name)
|
|
else:
|
|
_out(f" {DIM}No changes made.{RESET}")
|
|
|
|
|
|
def _handle_disabled_no_jobs(queue: CUPSQueueStatus, choice: str) -> None:
|
|
"""Handle fix for disabled printer with no pending jobs."""
|
|
if choice == "1":
|
|
if _cups_enable_printer(queue.printer_name):
|
|
_out(f" {GREEN}✓ Printer re-enabled.{RESET}")
|
|
elif choice == "2":
|
|
if _cups_restart_service():
|
|
_cups_enable_printer(queue.printer_name)
|
|
_out(f" {GREEN}✓ CUPS restarted and printer re-enabled.{RESET}")
|
|
else:
|
|
_out(f" {DIM}No changes made.{RESET}")
|
|
|
|
|
|
def _handle_enabled_with_jobs(queue: CUPSQueueStatus, choice: str) -> None:
|
|
"""Handle fix for enabled printer with stuck jobs."""
|
|
if choice == "1":
|
|
if _cups_cancel_all_jobs(queue.printer_name):
|
|
_out(f" {GREEN}✓ All jobs cancelled.{RESET}")
|
|
elif choice == "2":
|
|
if _cups_restart_service():
|
|
_out(f" {GREEN}✓ CUPS restarted.{RESET}")
|
|
else:
|
|
_out(f" {DIM}No changes made.{RESET}")
|
|
|
|
|
|
def _handle_backend_errors_only(choice: str) -> None:
|
|
"""Handle fix when only stale backend errors are detected."""
|
|
if choice == "1":
|
|
if _cups_restart_service():
|
|
_out(f" {GREEN}✓ CUPS restarted. Stale backend errors cleared.{RESET}")
|
|
else:
|
|
_out(f" {DIM}No changes made.{RESET}")
|