From 979df127a6a25fca3a72f0d454d49efce93eb8d8 Mon Sep 17 00:00:00 2001 From: engelgardt Date: Wed, 3 Jun 2026 17:55:52 +0300 Subject: [PATCH] Release 1.2.1: auto-select single NIC - Auto-select the network adapter when only one is found (configurable via auto_select_single; asked on first run) --- src/netswitch/app.py | 8 +++++-- src/netswitch/config.py | 47 ++++++++++++++++++++++++++++++++--------- src/netswitch/i18n.py | 2 ++ 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/netswitch/app.py b/src/netswitch/app.py index 5ea1c51..3171fe9 100644 --- a/src/netswitch/app.py +++ b/src/netswitch/app.py @@ -18,7 +18,7 @@ from .update_check import check_for_update from .network import list_adapters, set_static_ip, revert_to_dhcp, get_current_ipv4 -def _pick_adapter(console: Console) -> dict | None: +def _pick_adapter(console: Console, auto_single: bool = True) -> dict | None: adapters = list_adapters() if not adapters: console.print(f"[red]{t('no_adapters')}[/]") @@ -29,6 +29,10 @@ def _pick_adapter(console: Console) -> dict | None: ip = a.get("IPv4") or "—" console.print(f" {i}) [{a['Status']:<12}] {a['Name']} ({a['Description']}) {ip}") + if auto_single and len(adapters) == 1: + console.print(f"[green]{t('only_adapter', name=adapters[0]['Name'])}[/]") + return adapters[0] + while True: s = Prompt.ask(t("select_adapter")).strip() if s.isdigit() and 1 <= int(s) <= len(adapters): @@ -80,7 +84,7 @@ def main() -> None: console.print(title) console.print() - nic = _pick_adapter(console) + nic = _pick_adapter(console, cfg_data.get("auto_select_single", True)) if not nic: input(t("press_enter")); return diff --git a/src/netswitch/config.py b/src/netswitch/config.py index 3ec5cdd..b0c46c4 100644 --- a/src/netswitch/config.py +++ b/src/netswitch/config.py @@ -19,16 +19,25 @@ CONFIG_HEADER = """\ # --------------------------------------------------------------------------- # netswitch configuration # -# To change the interface language, edit the 'language' value below. -# Valid values: en, ru +# language : interface language. Valid: en, ru +# auto_select_single : auto-pick the network adapter when only one is found, +# without asking (yes/no). # -# Чтобы сменить язык интерфейса, измените значение 'language' ниже. -# Допустимые значения: en, ru +# language : язык интерфейса. Допустимо: en, ru +# auto_select_single : автоматически выбирать сетевой адаптер, если он +# единственный, не спрашивая (yes/no). # --------------------------------------------------------------------------- """ +def _to_bool(s: str, default: bool) -> bool: + s = (s or "").strip().lower() + if s in ("yes", "true", "y", "1", "on"): return True + if s in ("no", "false", "n", "0", "off"): return False + return default + + def app_dir() -> Path: """Directory holding the running executable (or source folder when run via python).""" if getattr(sys, "frozen", False): @@ -57,10 +66,26 @@ def _ask_language() -> str: print("Please enter 1 or 2 / Введите 1 или 2") -def _write_config(lang: str) -> None: +def _ask_auto_select() -> bool: + """First-run prompt — auto-select a lone adapter? Bilingual stdin.""" + print() + print("Auto-select the network adapter when only one is found? (y/n)") + print("Автовыбор сетевого адаптера, если он единственный? (y/n)") + while True: + try: + c = input("> ").strip().lower() + except (EOFError, KeyboardInterrupt): + return True + if c in ("y", "yes", "д", "да", "1"): return True + if c in ("n", "no", "н", "нет", "0"): return False + print("y / n") + + +def _write_config(lang: str, auto_select: bool) -> None: path = config_path() cp = configparser.ConfigParser() - cp["General"] = {"language": lang} + cp["General"] = {"language": lang, + "auto_select_single": "yes" if auto_select else "no"} with path.open("w", encoding="utf-8") as f: f.write(CONFIG_HEADER) cp.write(f) @@ -70,19 +95,21 @@ def load_config() -> dict: path = config_path() if not path.exists(): lang = _ask_language() + auto = _ask_auto_select() try: - _write_config(lang) + _write_config(lang, auto) except OSError: pass - return {"language": lang} + return {"language": lang, "auto_select_single": auto} cp = configparser.ConfigParser() try: cp.read(path, encoding="utf-8") except (configparser.Error, OSError): - return {"language": DEFAULT_LANG} + return {"language": DEFAULT_LANG, "auto_select_single": True} lang = (cp.get("General", "language", fallback=DEFAULT_LANG) or DEFAULT_LANG).strip().lower() if lang not in SUPPORTED_LANGS: lang = DEFAULT_LANG - return {"language": lang} + auto = _to_bool(cp.get("General", "auto_select_single", fallback="yes"), True) + return {"language": lang, "auto_select_single": auto} diff --git a/src/netswitch/i18n.py b/src/netswitch/i18n.py index 08ec960..234538c 100644 --- a/src/netswitch/i18n.py +++ b/src/netswitch/i18n.py @@ -14,6 +14,7 @@ STRINGS: dict[str, dict[str, str]] = { "no_adapters": "No physical wired adapters found.", "press_enter": "Press Enter to exit", "available_adapters": "Available adapters:", + "only_adapter": "Only one adapter, auto-selected: {name}", "select_adapter": "Select adapter number", "invalid_selection": "Invalid selection.", "selected": "Selected: {name}", @@ -40,6 +41,7 @@ STRINGS: dict[str, dict[str, str]] = { "no_adapters": "Подходящие проводные адаптеры не найдены.", "press_enter": "Нажмите Enter для выхода", "available_adapters": "Доступные адаптеры:", + "only_adapter": "Адаптер один, выбран автоматически: {name}", "select_adapter": "Введите номер адаптера", "invalid_selection": "Неверный выбор.", "selected": "Выбрано: {name}",