Предотвратить закрытие консольного приложения, когда оно не вызывается из существующего терминала? - PullRequest
8 голосов
/ 13 февраля 2010

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

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

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

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

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

Update0

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

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()

Ответы [ 2 ]

6 голосов
/ 14 февраля 2010

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

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

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

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

3 голосов
/ 13 февраля 2010

В 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 ветви try / except оператор - он сгенерирует исключение (на которое вы перехватываете и реагируете, устанавливая для вашей логической переменной значение False), если консоли нет, и просто работаете (в этом случае вы устанавливаете эту логическую переменную на True) если - это консоль.

...