Tensionix ENDERU
GitHub28 репозиториевDaily Tech3 подписчикаaudion.devвитрина продуктовRSSлента выпусков
← Все заметкиProjects

Практика Исправления Кодировок В Терминале Audion GUI

Этот документ фиксирует решения, которые шаблон должен переносить в новые GUI-проекты, чтобы терминальное окно нормально показывало вывод WinGet, PowerShell, CMD, Python и других Windows CLI.

Практика родилась на канарейке Audion Winget, где нужно было убрать кракозябры в русском выводе WinGet, сохранить progress bar, не сломать псевдографику и оставить ANSI-color.

Проблема

Встроенный терминал GUI получает вывод дочерних процессов через pipe, а не как обычное консольное окно Windows. Из-за этого:

Главный Принцип

Не доверять одной фиксированной кодировке для всего вывода Windows CLI.

WinGet, PowerShell, CMD, Python и старые консольные утилиты могут отдавать разные байты в зависимости от версии Windows, локали, способа запуска и того, считают ли они stdout настоящей консолью или pipe.

Правильная цепочка:

  1. Читать внешний процесс как bytes, где это возможно.
  2. Декодировать каждую порцию вывода через общий эвристический декодер.
  3. Не удалять ANSI escape-последовательности на этапе декодирования.
  4. Удалять только чистую spinner-анимацию, но не progress bar и не псевдографику.
  5. Рендерить ANSI в HTML уже на стороне GUI.

Общий Декодер Вывода

Шаблонный файл:

system_core/core/output_decode.py

Ключевые идеи:

Важно: декодер не должен удалять ANSI. ANSI escape-коды должны пройти дальше, чтобы GUI мог раскрасить вывод.

Запуск Процессов

Базовая функция utf8_subprocess_env в system_core/core/jobs.py задаёт:

PYTHONUTF8=1
PYTHONIOENCODING=utf-8
PYTHONUNBUFFERED=1

Для CLI-процессов, где нужен цветной вывод, добавляйте:

env = utf8_subprocess_env(
    {
        "AUDION_DISABLE_FZF": "1",
        "AUDION_GUI_TERMINAL": "1",
        "CLICOLOR": "1",
        "CLICOLOR_FORCE": "1",
        "FORCE_COLOR": "1",
    }
)
env.pop("NO_COLOR", None)

Это помогает Python-процессам и современным CLI, но не заменяет декодер. WinGet и системные утилиты всё равно нужно читать осторожно.

Почему Внешние CLI Лучше Читать Как Bytes

Для WinGet и большинства внешних команд лучше не включать text=True в subprocess.Popen.

Причина: Python тогда декодирует stdout сам, до эвристического декодера. Если Python выбрал не ту кодировку, GUI получит уже испорченный текст.

Рабочий шаблон:

Обработка Carriage Return И Spinner

WinGet активно использует \r для progress и spinner.

Практика:

text = text.replace("\r\n", "\n").replace("\r", "\n")

Затем чистые spinner-строки можно отбрасывать:

SPINNER_FRAME_CHARS = set("-\\|/ \t")

Отбрасывать можно только строки, которые состоят исключительно из символов spinner. Нельзя выкидывать строки с блоками , , табличной графикой или текстом, иначе потеряется полезный progress WinGet.

ANSI-Color

ANSI должен сохраняться до HTML-render.

Шаблонный renderer:

system_core/core/ansi.py

Что он делает:

Практическая проверка:

PowerShell И CMD

Для пользовательского терминала GUI:

Это снижает шанс неверного вывода, но не отменяет общий декодер, потому что WinGet и сторонние CLI могут вести себя иначе.

Что Переносить В Новые Проекты

Минимальный набор:

  1. Скопировать или оставить шаблонный system_core/core/output_decode.py.
  2. Использовать decode_process_bytes для stdout/stderr внешних процессов.
  3. Читать WinGet/CLI как bytes, не как text=True.
  4. Оставить text=True, encoding="utf-8" только для своих Python-процессов, где контролируется PYTHONIOENCODING=utf-8.
  5. В окружение процесса добавить PYTHONUTF8, PYTHONIOENCODING, PYTHONUNBUFFERED.
  6. Не задавать NO_COLOR; для цветного вывода использовать CLICOLOR_FORCE=1 и FORCE_COLOR=1.
  7. \r переводить в \n.
  8. Отфильтровывать только spinner-only строки.
  9. ANSI рендерить отдельным HTML renderer, а не удалять на входе.
  10. Проверить три сценария: русский CLI-текст, progress bar, ANSI-color.

Чего Не Делать

Быстрый Smoke-Тест

Для проверки нового проекта достаточно прогнать в терминале GUI команды, которые дают разные типы вывода:

Write-Output 'Найдено 7-Zip [7zip.7zip] Версия 26.01'
Write-Output 'Скачивание https://7-zip.org/a/7z2601-x64.msi'
Write-Output '████████████████ 1.90 MB / 1.90 MB'
Write-Output '┌──────────┬────────────┬─────────┐'
Write-Output '│ Name     │ ID         │ Version │'
Write-Output '├──────────┼────────────┼─────────┤'
Write-Output '│ 7-Zip    │ 7zip.7zip  │ 26.01   │'
Write-Output '└──────────┴────────────┴─────────┘'
Write-Output "`e[33mЖёлтое ANSI-предупреждение`e[0m"

Ожидаемый результат:

Итог

Правка кодировок оказалась не одной настройкой, а цепочкой:

bytes stdout -> эвристический decode -> аккуратная обработка \r -> фильтр spinner -> сохранение ANSI -> HTML-render

Именно эта цепочка дала стабильный вывод WinGet в GUI и не сломала ANSI-color.

Правлено 28.08.2026