mapper + lint
This commit is contained in:
@@ -1,148 +1,106 @@
|
||||
# [MODULE_PATH] superset_tool.utils.whiptail_fallback
|
||||
# [FILE] whiptail_fallback.py
|
||||
# [SEMANTICS] ui, fallback, console, utils, non‑interactive
|
||||
# <GRACE_MODULE id="superset_tool.utils.whiptail_fallback" name="whiptail_fallback.py">
|
||||
# @SEMANTICS: ui, fallback, console, utility, interactive
|
||||
# @PURPOSE: Предоставляет плотный консольный UI-fallback для интерактивных диалогов, имитируя `whiptail` для систем, где он недоступен.
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# [IMPORTS]
|
||||
# --------------------------------------------------------------
|
||||
# <IMPORTS>
|
||||
import sys
|
||||
from typing import List, Tuple, Optional, Any
|
||||
# [END_IMPORTS]
|
||||
# </IMPORTS>
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# [ENTITY: Service('ConsoleUI')]
|
||||
# --------------------------------------------------------------
|
||||
"""
|
||||
:purpose: Плотный консольный UI‑fallback для всех функций,
|
||||
которые в оригинальном проекте использовали ``whiptail``.
|
||||
Всё взаимодействие теперь **не‑интерактивно**: функции,
|
||||
выводящие сообщение, просто печатают его без ожидания
|
||||
``Enter``.
|
||||
"""
|
||||
# --- Начало кода модуля ---
|
||||
|
||||
def menu(
|
||||
title: str,
|
||||
prompt: str,
|
||||
choices: List[str],
|
||||
backtitle: str = "Superset Migration Tool",
|
||||
) -> Tuple[int, Optional[str]]:
|
||||
"""Return (rc, selected item). rc == 0 → OK."""
|
||||
print(f"\n=== {title} ===")
|
||||
print(prompt)
|
||||
# <ANCHOR id="menu" type="Function">
|
||||
# @PURPOSE: Отображает меню выбора и возвращает выбранный элемент.
|
||||
# @PARAM: title: str - Заголовок меню.
|
||||
# @PARAM: prompt: str - Приглашение к вводу.
|
||||
# @PARAM: choices: List[str] - Список вариантов для выбора.
|
||||
# @RETURN: Tuple[int, Optional[str]] - Кортеж (код возврата, выбранный элемент). rc=0 - успех.
|
||||
def menu(title: str, prompt: str, choices: List[str], **kwargs) -> Tuple[int, Optional[str]]:
|
||||
print(f"\n=== {title} ===\n{prompt}")
|
||||
for idx, item in enumerate(choices, 1):
|
||||
print(f"{idx}) {item}")
|
||||
|
||||
try:
|
||||
raw = input("\nВведите номер (0 – отмена): ").strip()
|
||||
sel = int(raw)
|
||||
if sel == 0:
|
||||
return 1, None
|
||||
return 0, choices[sel - 1]
|
||||
except Exception:
|
||||
return (0, choices[sel - 1]) if 0 < sel <= len(choices) else (1, None)
|
||||
except (ValueError, IndexError):
|
||||
return 1, None
|
||||
# </ANCHOR id="menu">
|
||||
|
||||
|
||||
def checklist(
|
||||
title: str,
|
||||
prompt: str,
|
||||
options: List[Tuple[str, str]],
|
||||
backtitle: str = "Superset Migration Tool",
|
||||
) -> Tuple[int, List[str]]:
|
||||
"""Return (rc, list of selected **values**)."""
|
||||
print(f"\n=== {title} ===")
|
||||
print(prompt)
|
||||
# <ANCHOR id="checklist" type="Function">
|
||||
# @PURPOSE: Отображает список с возможностью множественного выбора.
|
||||
# @PARAM: title: str - Заголовок.
|
||||
# @PARAM: prompt: str - Приглашение к вводу.
|
||||
# @PARAM: options: List[Tuple[str, str]] - Список кортежей (значение, метка).
|
||||
# @RETURN: Tuple[int, List[str]] - Кортеж (код возврата, список выбранных значений).
|
||||
def checklist(title: str, prompt: str, options: List[Tuple[str, str]], **kwargs) -> Tuple[int, List[str]]:
|
||||
print(f"\n=== {title} ===\n{prompt}")
|
||||
for idx, (val, label) in enumerate(options, 1):
|
||||
print(f"{idx}) [{val}] {label}")
|
||||
|
||||
raw = input("\nВведите номера через запятую (пустой ввод → отказ): ").strip()
|
||||
if not raw:
|
||||
return 1, []
|
||||
|
||||
if not raw: return 1, []
|
||||
try:
|
||||
indices = {int(x) for x in raw.split(",") if x.strip()}
|
||||
selected = [options[i - 1][0] for i in indices if 0 < i <= len(options)]
|
||||
return 0, selected
|
||||
except Exception:
|
||||
indices = {int(x.strip()) for x in raw.split(",") if x.strip()}
|
||||
selected_values = [options[i - 1][0] for i in indices if 0 < i <= len(options)]
|
||||
return 0, selected_values
|
||||
except (ValueError, IndexError):
|
||||
return 1, []
|
||||
# </ANCHOR id="checklist">
|
||||
|
||||
|
||||
def yesno(
|
||||
title: str,
|
||||
question: str,
|
||||
backtitle: str = "Superset Migration Tool",
|
||||
) -> bool:
|
||||
"""True → пользователь ответил «да». """
|
||||
# <ANCHOR id="yesno" type="Function">
|
||||
# @PURPOSE: Задает вопрос с ответом да/нет.
|
||||
# @PARAM: title: str - Заголовок.
|
||||
# @PARAM: question: str - Вопрос для пользователя.
|
||||
# @RETURN: bool - `True`, если пользователь ответил "да".
|
||||
def yesno(title: str, question: str, **kwargs) -> bool:
|
||||
ans = input(f"\n=== {title} ===\n{question} (y/n): ").strip().lower()
|
||||
return ans in ("y", "yes", "да", "д")
|
||||
# </ANCHOR id="yesno">
|
||||
|
||||
|
||||
def msgbox(
|
||||
title: str,
|
||||
msg: str,
|
||||
width: int = 60,
|
||||
height: int = 15,
|
||||
backtitle: str = "Superset Migration Tool",
|
||||
) -> None:
|
||||
"""Простой вывод сообщения – без ожидания Enter."""
|
||||
# <ANCHOR id="msgbox" type="Function">
|
||||
# @PURPOSE: Отображает информационное сообщение.
|
||||
# @PARAM: title: str - Заголовок.
|
||||
# @PARAM: msg: str - Текст сообщения.
|
||||
def msgbox(title: str, msg: str, **kwargs) -> None:
|
||||
print(f"\n=== {title} ===\n{msg}\n")
|
||||
# **Убрано:** input("Нажмите <Enter> для продолжения...")
|
||||
# </ANCHOR id="msgbox">
|
||||
|
||||
|
||||
def inputbox(
|
||||
title: str,
|
||||
prompt: str,
|
||||
backtitle: str = "Superset Migration Tool",
|
||||
) -> Tuple[int, Optional[str]]:
|
||||
"""Return (rc, введённая строка). rc == 0 → успешно."""
|
||||
# <ANCHOR id="inputbox" type="Function">
|
||||
# @PURPOSE: Запрашивает у пользователя текстовый ввод.
|
||||
# @PARAM: title: str - Заголовок.
|
||||
# @PARAM: prompt: str - Приглашение к вводу.
|
||||
# @RETURN: Tuple[int, Optional[str]] - Кортеж (код возврата, введенная строка).
|
||||
def inputbox(title: str, prompt: str, **kwargs) -> Tuple[int, Optional[str]]:
|
||||
print(f"\n=== {title} ===")
|
||||
val = input(f"{prompt}\n")
|
||||
if val == "":
|
||||
return 1, None
|
||||
return 0, val
|
||||
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# [ENTITY: Service('ConsoleGauge')]
|
||||
# --------------------------------------------------------------
|
||||
"""
|
||||
:purpose: Минимальная имитация ``whiptail``‑gauge в консоли.
|
||||
"""
|
||||
return (0, val) if val else (1, None)
|
||||
# </ANCHOR id="inputbox">
|
||||
|
||||
# <ANCHOR id="_ConsoleGauge" type="Class">
|
||||
# @PURPOSE: Контекстный менеджер для имитации `whiptail gauge` в консоли.
|
||||
# @INTERNAL
|
||||
class _ConsoleGauge:
|
||||
"""Контекст‑менеджер для простого прогресс‑бара."""
|
||||
def __init__(self, title: str, width: int = 60, height: int = 10):
|
||||
def __init__(self, title: str, **kwargs):
|
||||
self.title = title
|
||||
self.width = width
|
||||
self.height = height
|
||||
self._percent = 0
|
||||
|
||||
def __enter__(self):
|
||||
print(f"\n=== {self.title} ===")
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
sys.stdout.write("\n"); sys.stdout.flush()
|
||||
def set_text(self, txt: str) -> None:
|
||||
sys.stdout.write(f"\r{txt} ")
|
||||
sys.stdout.flush()
|
||||
|
||||
sys.stdout.write(f"\r{txt} "); sys.stdout.flush()
|
||||
def set_percent(self, percent: int) -> None:
|
||||
self._percent = percent
|
||||
sys.stdout.write(f"{percent}%")
|
||||
sys.stdout.flush()
|
||||
# [END_ENTITY]
|
||||
sys.stdout.write(f"{percent}%"); sys.stdout.flush()
|
||||
# </ANCHOR id="_ConsoleGauge">
|
||||
|
||||
def gauge(
|
||||
title: str,
|
||||
width: int = 60,
|
||||
height: int = 10,
|
||||
) -> Any:
|
||||
"""Always returns the console fallback gauge."""
|
||||
return _ConsoleGauge(title, width, height)
|
||||
# [END_ENTITY]
|
||||
# <ANCHOR id="gauge" type="Function">
|
||||
# @PURPOSE: Создает и возвращает экземпляр `_ConsoleGauge`.
|
||||
# @PARAM: title: str - Заголовок для индикатора прогресса.
|
||||
# @RETURN: _ConsoleGauge - Экземпляр контекстного менеджера.
|
||||
def gauge(title: str, **kwargs) -> _ConsoleGauge:
|
||||
return _ConsoleGauge(title, **kwargs)
|
||||
# </ANCHOR id="gauge">
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# [END_FILE whiptail_fallback.py]
|
||||
# --------------------------------------------------------------
|
||||
# --- Конец кода модуля ---
|
||||
|
||||
# </GRACE_MODULE id="superset_tool.utils.whiptail_fallback">
|
||||
Reference in New Issue
Block a user