Препятствуйте тому, чтобы консольное приложение закрылось, при отсутствии вызова от существующего терминала?

Существует много вариантов на этом виде вопроса. Однако я конкретно после способа предотвратить консольное приложение в Python от закрытия, когда это не вызывается от терминала (или другая консоль, как это можно назвать в Windows). Пример, где это могло произойти, дважды щелкает по a .py файл от Windows Explorer.

Обычно я использую что-то как следующий фрагмент кода, но он имеет неудачный побочный эффект работы, даже если приложение вызывается от существующего терминала:

def press_any_key():
    if os.name == "nt":
        os.system("pause")
atexit.register(press_any_key)

Это также делает предположение, что все пользователи Windows вызывают приложение из Windows "оболочка", и что только пользователи Windows могут выполнить программу от местоположения кроме существующего терминала.

Существует ли (предпочтительно кросс-платформенный) способ обнаружить, если мое приложение было вызвано от терминала, и/или необходимо ли обеспечить, "нажимают какую-либо клавишу..." функциональность для в настоящее время рабочего экземпляра? Обратите внимание, что, обращаясь к пакету, удар или любые другие "обходные решения" процесса обертки очень нежелательны.

Update0

Используя ответ Alex Martelli ниже, я произвел эту функцию:

def register_pause_before_closing_console():
    import atexit, os
    if os.name == 'nt':
        from win32api import GetConsoleTitle
        if not GetConsoleTitle().startswith(os.environ["COMSPEC"]):
            atexit.register(lambda: os.system("pause"))

if __name__ == '__main__':
    register_pause_before_closing_console()

Если другие подходящие ответы возникнут, то я добавлю больше кода для других платформ и настольных сред.

Update1

В духе использования pywin32, я произвел эту функцию, которая изменяет к лучшему тот выше, с помощью принятого ответа. Закомментированный код является альтернативной реализацией как происходящий в Update0. Если использование pywin32 не является опцией, перейдите по ссылке в принятом ответе. Пауза или getch () для дегустации.

def _current_process_owns_console():
    #import os, win32api
    #return not win32api.GetConsoleTitle().startswith(os.environ["COMSPEC"])

    import win32console, win32process
    conswnd = win32console.GetConsoleWindow()
    wndpid = win32process.GetWindowThreadProcessId(conswnd)[1]
    curpid = win32process.GetCurrentProcessId()
    return curpid == wndpid

def register_pause_before_closing_console():
    import atexit, os, pdb
    if os.name == 'nt':
        if _current_process_owns_console():
            atexit.register(lambda: os.system("pause"))

if __name__ == '__main__':
    register_pause_before_closing_console()

8
задан Community 23 May 2017 в 12:02
поделиться

2 ответа

Во-первых, попытка отговорить вас от хитрых взломов. Совершенно уместно иметь отдельный ярлык, предназначенный для запуска из проводника, который делает несколько разные вещи (например, удерживает консоль открытой) от сценария, который будет использоваться из командной строки. Как уже отмечал Алекс, это не проблема nix, и правильнее всего всегда выходить аккуратно, иначе пользователи будут жаловаться.

Если вам все еще нужно обходное решение, вот код , чтобы определить, когда консоль не должна закрывать , которая достаточно чиста. Требуется Windows 2000 или более поздняя версия, логика содержится в этой функции:

def owns_console():
    wnd = GetConsoleWindow()
    if wnd is None:
        return False
    return GetCurrentProcessId() == GetWindowThreadProcessId(wnd)

По сути, она получает PID процесса, который владеет консолью, которую использует Python, и нашего процесса. Если они такие же, то при выходе консоль уйдет, поэтому ее нужно держать открытой. Если они разные или консоль не подключена, Python должен нормально завершиться.

6
ответ дан 5 December 2019 в 18:59
поделиться

В Unix, sys.stdin.isatty () надежно сообщает вам, поступает ли стандартный ввод с терминального устройства (или перенаправляется в противном случае), и аналогично для того же метода на sys.stdout и sys.stderr - так что вы можете использовать эти вызовы, чтобы определить, выполняется ли приложение в интерактивном режиме или в некоторая неинтерактивная среда (например, задание cron). То, как вы хотите их использовать, зависит от того, что вы хотите сделать, если (например) и стандартный ввод, и вывод перенаправляются на нетерминальный, но стандартная ошибка направляется на терминал - рассмотрите каждый из 8 возможностей, все из которых перенаправлены на нетерминалы ни к одной из них, и решают, что вы хотите делать в каждом случае.

В Windows ситуация иная, поскольку выполняется файл .py (в отличие от файла .pyw файл) создаст новую временную консоль (в Unix нет точно эквивалентной ситуации); Полагаю, вы хотите разобраться с этим случаем? (Или это просто перенаправление стандартных потоков ввода-вывода в файлы, что возможно в Windows примерно так же, как в Unix?). Я думаю, что лучшим подходом в Windows может быть использование win32api.SetConsoleCtrlHandler для установки обработчика таких событий, как CTRL_CLOSE_EVENT - таким образом обработчик должен быть вызван (в данном случае, когда консоль закрывается), если есть консоль для процесса, но не иначе. Или, если все, что вас волнует, это то, есть ли консоль вообще или нет (в противном случае вы предпочитаете решать все по-своему), попробуйте вызвать win32api.GetConsoleTitle в try ветви a попробуйте / except - он сгенерирует исключение (которое вы перехватываете и отвечаете, устанавливая для своей логической переменной значение False ), если нет console, и просто работайте (в этом случае вы устанавливаете эту логическую переменную на True ), если есть консоль.

3
ответ дан 5 December 2019 в 18:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: