try-except없이 Python에서 keyboardinterrupt 캡처


101

파이썬에서 KeyboardInterrupt모든 코드를 try- except문 안에 넣지 않고 이벤트 를 캡처하는 방법이 있습니까?

사용자가 Ctrl+를 누르면 흔적없이 깔끔하게 종료하고 싶습니다 C.

답변:


149

예, 모듈 신호를 사용하여 인터럽트 핸들러를 설치 하고 threading.Event를 사용하여 영원히 기다릴 수 있습니다 .

import signal
import sys
import time
import threading

def signal_handler(signal, frame):
    print('You pressed Ctrl+C!')
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
forever = threading.Event()
forever.wait()

10
시그널 모듈에는 플랫폼 별 문제가 있습니다.이 포스터에 영향을주지 않아야하지만 "Windows에서 signal ()은 SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV 또는 SIGTERM으로 만 호출 할 수 있습니다. A ValueError 다른 경우에 발생합니다. "
bgporter 2010

7
스레드와도 잘 작동합니다. while True: continue그래도 절대하지 않기를 바랍니다 . (그 스타일에서는 while True: pass어쨌든 깔끔 할 것입니다.) 그것은 매우 낭비적일 것입니다. while True: time.sleep(60 * 60 * 24)(한 번에 하루 동안자는 것은 전적으로 임의의 수치입니다) 와 같은 것을 시도하십시오 .
Chris Morgan

1
사용에 대한 Chris Morgan의 제안을 사용 time하고 있다면 잊지 마세요. import time:)
Seaux

1
sys.exit (0)을 호출하면 SystemExit 예외가 발생합니다. 다음과 함께 사용하면 잘 작동 할 수 있습니다. stackoverflow.com/a/13723190/353094
leetNightshade

2
대신 반복적으로 수면의) signal.pause를 (사용할 수 있습니다
Croad Langshan을

36

트레이스 백을 표시하지 않으려면 다음과 같이 코드를 작성하십시오.

## all your app logic here
def main():
   ## whatever your app does.


if __name__ == "__main__":
   try:
      main()
   except KeyboardInterrupt:
      # do nothing here
      pass

(예, 이것이 질문에 직접 답하지 않는다는 것을 알고 있지만 try / except 블록이 필요한 이유는 명확하지 않습니다. 이로 인해 OP에 덜 성 가실 수 있습니다.)


5
어떤 이유로, 이것이 항상 저에게 효과가있는 것은 아닙니다. signal.signal( signal.SIGINT, lambda s, f : sys.exit(0))항상 그렇습니다.
Hal Canary

이것은 스레드를 사용하는 pygtk와 같이 항상 작동하지 않습니다. 때때로 ^ C는 전체 프로세스 대신 현재 스레드를 죽이기 때문에 예외는 해당 스레드를 통해서만 전파됩니다.
Sudo Bash

다른 SO 질문은 pygtk와 Ctrl + C에 대해 구체적있다 : stackoverflow.com/questions/16410852/...은
bgporter

30

자체 시그널 핸들러를 설정하는 대안은 컨텍스트 관리자를 사용하여 예외를 포착하고 무시하는 것입니다.

>>> class CleanExit(object):
...     def __enter__(self):
...             return self
...     def __exit__(self, exc_type, exc_value, exc_tb):
...             if exc_type is KeyboardInterrupt:
...                     return True
...             return exc_type is None
... 
>>> with CleanExit():
...     input()    #just to test it
... 
>>>

이것은 무슨 일이 일어나고 있는지에 대한 명시적인 언급을 유지하면서 try- except블록을 제거합니다 .

또한 매번 신호 처리기를 다시 설정하고 재설정 할 필요없이 코드의 일부 부분에서만 인터럽트를 무시할 수 있습니다.


1
이 솔루션은 신호를 처리하는 것보다 목적을 표현하는 데 좀 더 직접적으로 보입니다.
Seaux 2013 년

다중 처리 라이브러리를 사용하여 어떤 개체에 해당 메서드를 추가해야하는지 잘 모르겠습니다. .. 어떤 단서가 있습니까?
Stéphane

@ Stéphane 무슨 뜻이야? 다중 처리를 다룰 때 부모와 자식 프로세스 모두에서 신호가 트리거 될 수 있기 때문에 신호를 처리해야합니다. 실제로 수행하는 작업과 소프트웨어 사용 방법에 따라 다릅니다.
Bakuriu

8

나는 이것이 오래된 질문이라는 것을 알고 있지만 먼저 여기에 와서 atexit모듈 을 발견했습니다 . 아직까지는 크로스 플랫폼 실적이나 전체주의 사항 목록에 대해 알지 못합니다.하지만 지금까지 KeyboardInterruptLinux에서 사후 정리 를 처리 할 때 찾던 것이 바로 그것입니다 . 문제에 접근하는 다른 방법을 던지고 싶었습니다.

Fabric 작업의 맥락에서 종료 후 정리를 수행하고 싶으므로 모든 것을 try/ 안에 래핑하는 것은 except나에게도 옵션이 아닙니다. atexit코드가 최상위 수준의 제어 흐름에 있지 않은 이러한 상황에 적합 하다고 생각 합니다.

atexit 다음과 같이 매우 유능하고 즉시 읽을 수 있습니다.

import atexit

def goodbye():
    print "You are now leaving the Python sector."

atexit.register(goodbye)

데코레이터로 사용할 수도 있습니다 (2.6 기준,이 예제는 문서에서 가져온 것입니다).

import atexit

@atexit.register
def goodbye():
    print "You are now leaving the Python sector."

구체적으로 작성하고 싶다면 KeyboardInterrupt이 질문에 대한 다른 사람의 대답이 더 낫습니다.

그러나 atexit모듈은 코드가 70 줄에 불과하며 예외를 다르게 처리하는 유사한 버전을 만드는 것이 어렵지 않습니다 (예 : 예외를 콜백 함수에 인수로 전달). (그 제한은 atexit수정 된 버전을 보증합니다. 현재 저는 exit-callback-function이 예외에 대해 알 수있는 방법을 생각할 수 없습니다. atexit핸들러는 예외를 포착하고 콜백을 호출 한 다음 다시 발생합니다. 예외입니다.하지만 다르게 할 수 있습니다.)

자세한 내용은 다음을 참조하십시오.


atexit가 KeyboardInterrupt (python 3.7)에서 작동하지 않음
TimZaman

여기에서 KeyboardInterrupt에 대해 작업했습니다 (python 3.7, MacOS). 플랫폼 별 특질일까요?
Niko Nyman

4

을 대체하여 (가장 분명하고 적절한 "최상의"솔루션이지만 이미 알고 있고 다른 것을 요청한 KeyboardInterrupt)없이에 대한 스택 추적 인쇄를 방지 할 수 있습니다 . 같은 것try: ... except KeyboardInterrupt: passsys.excepthook

def custom_excepthook(type, value, traceback):
    if type is KeyboardInterrupt:
        return # do nothing
    else:
        sys.__excepthook__(type, value, traceback)

사용자가 ctrl-c를 누르면 추적없이 깨끗한 종료를 원합니다
Alex

7
이것은 전혀 사실이 아닙니다. KeyboardInterrupt 예외는 인터럽트 처리기 중에 생성됩니다. SIGINT의 기본 처리기는 KeyboardInterrupt를 발생 시키므로 해당 동작을 원하지 않는 경우 SIGINT에 대해 다른 신호 처리기를 제공하기 만하면됩니다. 예외는 try / except에서만 처리 할 수 ​​있다는 점에서 정확하지만이 경우 예외가 처음부터 발생하지 않도록 유지할 수 있습니다.
Matt

1
예, 게시 후 약 3 분 후에 kotlinski의 답변이 올랐습니다.)

2

나는 모든 사람들이 제안한 해결책을 시도했지만 실제로 작동하도록 코드를 직접 수정해야했습니다. 다음은 즉석 코드입니다.

import signal
import sys
import time

def signal_handler(signal, frame):
    print('You pressed Ctrl+C!')
    print(signal) # Value is 2 for CTRL + C
    print(frame) # Where your execution of program is at moment - the Line Number
    sys.exit(0)

#Assign Handler Function
signal.signal(signal.SIGINT, signal_handler)

# Simple Time Loop of 5 Seconds
secondsCount = 5
print('Press Ctrl+C in next '+str(secondsCount))
timeLoopRun = True 
while timeLoopRun:  
    time.sleep(1)
    if secondsCount < 1:
        timeLoopRun = False
    print('Closing in '+ str(secondsCount)+ ' seconds')
    secondsCount = secondsCount - 1

0

누군가가 빠른 최소 솔루션을 찾고 있다면

import signal

# The code which crashes program on interruption

signal.signal(signal.SIGINT, call_this_function_if_interrupted)

# The code skipped if interrupted
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.