signal
— 비동기 이벤트에 대한 처리기 설정¶
이 모듈은 파이썬에서 시그널 처리기를 사용하는 메커니즘을 제공합니다.
일반 규칙¶
signal.signal()
함수는 시그널이 수신될 때 실행될 사용자 정의 처리기를 정의하도록 합니다. 소수의 기본 처리기가 설치됩니다: SIGPIPE
는 무시되고 (그래서 파이프와 소켓에 대한 쓰기 에러는 일반 파이썬 예외로 보고될 수 있습니다) SIGINT
는 부모 프로세스가 변경하지 않았다면 KeyboardInterrupt
예외로 번역됩니다.
일단 설정되면, 특정 시그널에 대한 처리기는 명시적으로 재설정 될 때까지 (파이썬은 하부 구현과 관계없이 BSD 스타일 인터페이스를 흉내 냅니다) 설치된 상태로 유지됩니다. SIGCHLD
에 대한 처리기는 예외인데, 하부 구현을 따릅니다.
파이썬 시그널 처리기의 실행¶
파이썬 시그널 처리기는 저수준 (C) 시그널 처리기 내에서 실행되지 않습니다. 대신, 저수준 시그널 처리기는 가상 기계에게 나중에 (예를 들어 다음 바이트 코드 명령에서) 해당 파이썬 시그널 처리기를 실행하도록 지시하는 플래그를 설정합니다. 결과는 다음과 같습니다:
C 코드에서의 유효하지 않은 연산으로 인해 발생하는
SIGFPE
나SIGSEGV
와 같은 동기 에러를 잡는 것은 그리 의미가 없습니다. 파이썬은 시그널 처리기에서 C 코드로 돌아오는데, 같은 시그널을 다시 발생시켜서, 파이썬이 멈출 것입니다. 파이썬 3.3부터는,faulthandler
모듈을 사용하여 동기 에러를 보고할 수 있습니다.C로만 구현된 오래 실행되는 계산(가령 커다란 텍스트 본문에 대한 정규식 일치)은 수신된 시그널에 상관없이 임의의 시간 동안 중단없이 실행될 수 있습니다. 계산이 끝나면 파이썬 시그널 처리기가 호출됩니다.
If the handler raises an exception, it will be raised “out of thin air” in the main thread. See the note below for a discussion.
시그널과 스레드¶
파이썬 시그널 처리기는 시그널이 다른 스레드에서 수신될 때도 항상 메인 인터프리터의 메인 파이썬 스레드에서 실행됩니다. 이는 시그널을 스레드 간 통신 수단으로 사용할 수 없음을 의미합니다. 대신 threading
모듈의 동기화 프리미티브를 사용할 수 있습니다.
게다가, 메인 인터프리터의 메인 스레드만 새로운 시그널 처리기를 설정할 수 있습니다.
모듈 내용¶
버전 3.5에서 변경: 이하에 열거된 시그널 (SIG*), 처리기 (SIG_DFL
, SIG_IGN
) 및 sigmask (SIG_BLOCK
, SIG_UNBLOCK
, SIG_SETMASK
) 관련 상수는 열거형
으로 바뀌었습니다. getsignal()
, pthread_sigmask()
, sigpending()
및 sigwait()
함수는 사람이 읽을 수 있는 열거형
을 반환합니다.
signal
모듈에 정의된 변수는 다음과 같습니다:
-
signal.
SIG_DFL
¶ 이것은 두 가지 표준 시그널 처리 옵션 중 하나입니다; 단순히 시그널의 기본 기능을 수행합니다. 예를 들어, 대부분의 시스템에서
SIGQUIT
의 기본 동작은 코어를 덤프하고 종료하는 것이지만,SIGCHLD
의 기본 동작은 단순히 무시하는 것입니다.
-
signal.
SIG_IGN
¶ 이것은 주어진 시그널을 무시하는 또 다른 표준 시그널 처리기입니다.
-
signal.
SIGFPE
¶ 부동 소수점 예외. 예를 들어, 0으로 나누기.
더 보기
나누기나 모듈로 연산의 두 번째 인자가 0이면
ZeroDivisionError
가 발생합니다.
-
signal.
SIGILL
¶ 잘못된 명령어.
-
signal.
SIGINT
¶ 키보드 인터럽트 (CTRL + C).
기본 액션은
KeyboardInterrupt
를 발생시키는 것입니다.
-
signal.
SIGSEGV
¶ 세그먼테이션 오류: 유효하지 않은 메모리 참조.
-
signal.
SIGTERM
¶ 종료 시그널.
-
SIG*
모든 시그널 번호는 기호적으로 정의됩니다. 예를 들어, 행업(hangup) 시그널은
signal.SIGHUP
으로 정의됩니다; 변수 이름은<signal.h>
에 있는 C 프로그램에서 사용되는 이름과 동일합니다. ‘signal()
’에 대한 유닉스 매뉴얼 페이지는 존재하는 시그널을 나열합니다 (일부 시스템에서는 signal(2)이고, 다른 시스템에서는 signal(7)입니다). 모든 시스템이 같은 시그널 이름 집합을 정의하는 것은 아님에 유의하십시오; 시스템에서 정의한 이름만 이 모듈에서 정의합니다.
-
signal.
CTRL_C_EVENT
¶ Ctrl+C 키 입력 이벤트에 해당하는 시그널. 이 시그널은
os.kill()
에서만 사용할 수 있습니다.가용성: 윈도우.
버전 3.2에 추가.
-
signal.
CTRL_BREAK_EVENT
¶ Ctrl+Break 키 입력 이벤트에 해당하는 시그널. 이 시그널은
os.kill()
에서만 사용할 수 있습니다.가용성: 윈도우.
버전 3.2에 추가.
-
signal.
NSIG
¶ 가장 높은 시그널 번호보다 하나 큰 값.
-
signal.
ITIMER_VIRTUAL
¶ 프로세스가 실행 중일 때만 간격 타이머(interval timer)를 감소시키고, 만료 시 SIGVTALRM을 전달합니다.
-
signal.
ITIMER_PROF
¶ 프로세스가 실행될 때와 시스템이 프로세스를 대신하여 실행될 때 간격 타이머(interval timer)를 감소시킵니다. ITIMER_VIRTUAL과 함께 사용되어, 이 타이머는 일반적으로 사용자와 커널 공간에서 응용 프로그램이 소비한 시간을 프로파일링하는 데 사용됩니다. 만료 시 SIGPROF를 전달합니다.
-
signal.
SIG_BLOCK
¶ pthread_sigmask()
의 how 매개 변수에 가능한 값으로 시그널이 차단됨을 나타냅니다.버전 3.3에 추가.
-
signal.
SIG_UNBLOCK
¶ pthread_sigmask()
의 how 매개 변수에 가능한 값으로 시그널이 차단 해제됨을 나타냅니다.버전 3.3에 추가.
-
signal.
SIG_SETMASK
¶ pthread_sigmask()
의 how 매개 변수에 가능한 값으로 시그널 마스크가 교체됨을 나타냅니다.버전 3.3에 추가.
signal
모듈은 하나의 예외를 정의합니다:
-
exception
signal.
ItimerError
¶ 하부
setitimer()
나getitimer()
구현으로부터의 에러를 알리기 위해 발생합니다. 유효하지 않은 간격 타이머나 음의 시간이setitimer()
에 전달되면 이 에러가 예상됩니다. 이 에러는OSError
의 서브 형입니다.
signal
모듈은 다음 함수를 정의합니다:
-
signal.
alarm
(time)¶ time이 0이 아니면, 이 함수는
SIGALRM
시그널이 time 초 내에 프로세스로 전송되도록 요청합니다. 이전에 예약된 알람은 취소됩니다 (임의의 시간에 오직 하나의 알람만 예약될 수 있습니다). 반환된 값은 이전에 설정된 알람이 전달되기까지 남은 초(seconds)입니다. time이 0이면, 알람이 예약되지 않고, 예약된 알람이 취소됩니다. 반환 값이 0이면, 현재 예약된 알람이 없습니다.
-
signal.
getsignal
(signalnum)¶ 시그널 signalnum에 대한 현재 시그널 처리기를 반환합니다. 반환된 값은 콜러블 파이썬 객체이거나, 특수 값
signal.SIG_IGN
,signal.SIG_DFL
중 하나이거나None
일 수 있습니다. 여기서signal.SIG_IGN
은 시그널이 이전에 무시되었음을 의미하고,signal.SIG_DFL
은 시그널을 처리하는 기본 방법이 이전에 사용 중임을 의미하고,None
은 이전 시그널 처리기가 파이썬에서 설치되지 않았음을 의미합니다.
-
signal.
strsignal
(signalnum)¶ 시그널 signalnum의 시스템 설명을 반환합니다, 가령 “Interrupt”, “Segmentation fault”, 등. 시그널이 인식되지 않으면
None
을 반환합니다.버전 3.8에 추가.
-
signal.
valid_signals
()¶ 이 플랫폼에서 유효한 시그널 번호 집합을 반환합니다. 일부 시그널이 시스템에서 내부 용으로 예약되었으면
range(1, NSIG)
보다 작을 수 있습니다.버전 3.8에 추가.
-
signal.
pause
()¶ 시그널이 수신될 때까지 프로세스를 휴면 상태로 만듭니다; 그런 다음 적절한 처리기가 호출됩니다. 아무것도 반환하지 않습니다.
가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 signal(2)를 참조하십시오.
sigwait()
,sigwaitinfo()
,sigtimedwait()
및sigpending()
도 참조하십시오.
-
signal.
raise_signal
(signum)¶ 호출하는 프로세스에 시그널을 보냅니다. 아무것도 반환하지 않습니다.
버전 3.8에 추가.
-
signal.
pidfd_send_signal
(pidfd, sig, siginfo=None, flags=0)¶ 파일 기술자 pidfd가 참조하는 프로세스로 시그널 sig를 보냅니다. 파이썬은 현재 siginfo 매개 변수를 지원하지 않습니다;
None
이어야 합니다. flags 인자는 향후 확장을 위해 제공됩니다; 현재는 플래그 값이 정의되어 있지 않습니다.자세한 내용은 pidfd_send_signal(2) 매뉴얼 페이지를 참조하십시오.
가용성: 리눅스 5.1+
버전 3.9에 추가.
-
signal.
pthread_kill
(thread_id, signalnum)¶ 시그널 signalnum을 호출자와 같은 프로세스의 다른 스레드인 스레드 thread_id로 보냅니다. 대상 스레드는 임의의 (파이썬이거나 아닌) 코드를 실행 중일 수 있습니다. 그러나, 대상 스레드가 파이썬 인터프리터를 실행 중이면, 파이썬 시그널 처리기는 메인 인터프리터의 메인 스레드에서 실행됩니다. 따라서, 특정 파이썬 스레드에 시그널을 보내는 것의 유일한 용도는 실행 중인 시스템 호출이
InterruptedError
로 실패하도록 하는 것입니다.thread_id에 적합한 값을 얻으려면
threading.get_ident()
나threading.Thread
객체의ident
어트리뷰트를 사용하십시오.signalnum이 0이면, 시그널이 전송되지 않지만, 여전히 에러 검사가 수행됩니다; 대상 스레드가 여전히 실행 중인지 확인하는 데 사용할 수 있습니다.
인자
thread_id
,signalnum
으로 감사 이벤트signal.pthread_kill
을 발생시킵니다.가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 pthread_kill(3)을 참조하십시오.
os.kill()
도 참조하십시오.버전 3.3에 추가.
-
signal.
pthread_sigmask
(how, mask)¶ 호출하는 스레드의 시그널 마스크를 가져오거나 변경하거나 가져오면서 변경합니다. 시그널 마스크는 호출자에게 현재 배달이 차단된 시그널 집합입니다. 이전 시그널 마스크를 시그널 집합으로 반환합니다.
호출의 동작은 다음과 같이 how 값에 따라 다릅니다.
SIG_BLOCK
: 차단된 시그널 집합은 현재 집합과 mask 인자의 합집합입니다.SIG_UNBLOCK
: mask에 있는 시그널이 차단된 시그널의 현재 집합에서 제거됩니다. 차단되지 않은 시그널을 차단 해제하려고 시도할 수 있습니다.SIG_SETMASK
: 차단된 시그널 집합이 mask 인자로 설정됩니다.
mask는 시그널 번호 집합입니다 (예를 들어 {
signal.SIGINT
,signal.SIGTERM
}). 모든 시그널을 포함한 전체 마스크를 얻으려면valid_signals()
를 사용하십시오.예를 들어,
signal.pthread_sigmask(signal.SIG_BLOCK, [])
은 호출하는 스레드의 시그널 마스크를 읽습니다.SIGKILL
과SIGSTOP
은 차단할 수 없습니다.가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 sigprocmask(3)과 pthread_sigmask(3)을 참조하십시오.
pause()
,sigpending()
및sigwait()
도 참조하십시오.버전 3.3에 추가.
-
signal.
setitimer
(which, seconds, interval=0.0)¶ seconds(
alarm()
과 달리 float가 허용됩니다) 이후에 그리고 (interval이 0이 아니면) 그 후로 interval 초마다 발사(fire)하도록 which로 지정된 간격 타이머(signal.ITIMER_REAL
,signal.ITIMER_VIRTUAL
또는signal.ITIMER_PROF
중 하나)를 설정합니다. which로 지정된 간격 타이머는 seconds를 0으로 설정하여 지울 수 있습니다.간격 타이머가 발사(fire)하면, 시그널이 프로세스로 전송됩니다. 전송된 시그널은 사용 중인 타이머에 따라 다릅니다;
signal.ITIMER_REAL
은SIGALRM
을,signal.ITIMER_VIRTUAL
은SIGVTALRM
을,signal.ITIMER_PROF
는SIGPROF
를 전달합니다.이전 값은 튜플로 반환됩니다: (지연, 간격).
유효하지 않은 간격 타이머를 전달하려고 하면
ItimerError
가 발생합니다.가용성: 유닉스.
-
signal.
set_wakeup_fd
(fd, *, warn_on_full_buffer=True)¶ 웨이크업 파일 기술자를 fd로 설정합니다. 시그널이 수신되면 시그널 번호는 단일 바이트로 fd에 기록됩니다. 라이브러리에서 poll이나 select 호출을 깨워서, 시그널을 완전히 처리하는 데 사용될 수 있습니다.
이전 웨이크업 fd가 반환됩니다 (또는 파일 기술자 웨이크업이 활성화되지 않았으면 -1). fd가 -1이면, 파일 기술자 웨이크업이 비활성화됩니다. -1이 아니면, fd는 비 블로킹이어야 합니다. poll이나 select를 다시 호출하기 전에 fd에서 바이트를 제거하는 것은 라이브러리의 책임입니다.
스레드가 활성화되었을 때, 이 함수는 메인 인터프리터의 메인 스레드에서만 호출할 수 있습니다; 다른 스레드에서 호출하려고 하면
ValueError
예외가 발생합니다.이 함수를 사용하는 일반적인 두 가지 방법이 있습니다. 두 방법 모두, 시그널이 도착할 때 깨어나기 위해 fd를 사용하지만, 어떤 시그널이나 시그널들이 도착했는지 판단하는 방법이 다릅니다.
첫 번째 방법에서는, fd의 버퍼에서 데이터를 읽고, 바이트 값이 시그널 번호를 제공합니다. 이것은 간단합니다만, 드물게 문제가 될 수 있습니다: 일반적으로 fd에는 제한된 버퍼 공간이 있으며, 너무 많은 시그널이 너무 빨리 도착하면, 버퍼가 가득 차고, 일부 시그널이 손실될 수 있습니다. 이 방법을 사용하면, 시그널이 손실될 때 최소한 stderr에 경고가 인쇄되도록
warn_on_full_buffer=True
를 설정해야 합니다.두 번째 방법에서는, 오직 웨이크업만을 위해 웨이크업 fd를 사용하고, 실제 바이트 값은 무시합니다. 이 경우, 우리가 신경 쓰는 것은 fd의 버퍼가 비어 있는지 비어 있지 않은지 입니다; 가득 찬 버퍼는 전혀 문제를 가리키지 않습니다. 이 방법을 사용하면, 사용자가 가짜 경고 메시지로 혼동되지 않도록
warn_on_full_buffer=False
를 설정해야 합니다.버전 3.5에서 변경: 윈도우에서, 이 함수는 이제 소켓 핸들도 지원합니다.
버전 3.7에서 변경:
warn_on_full_buffer
매개 변수를 추가했습니다.
-
signal.
siginterrupt
(signalnum, flag)¶ 시스템 호출 재시작 동작을 변경합니다: flag가
False
이면, 시그널 signalnum에 의해 인터럽트 될 때 시스템 호출이 다시 시작되고, 그렇지 않으면 시스템 호출이 중단됩니다. 아무것도 반환하지 않습니다.가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 siginterrupt(3)을 참조하십시오.
signal()
로 시그널 처리기를 설치하면 주어진 시그널에 대해 flag 값을 참으로siginterrupt()
를 묵시적으로 호출하여 재시작 동작을 인터럽트 가능으로 재설정합니다.
-
signal.
signal
(signalnum, handler)¶ 시그널 signalnum의 처리기를 함수 handler로 설정합니다. handler는 두 개의 인자(아래를 참조하십시오)를 취하는 콜러블 파이썬 객체, 또는 특수 값
signal.SIG_IGN
이나signal.SIG_DFL
중 하나일 수 있습니다. 이전 시그널 처리기가 반환됩니다 (위의getsignal()
설명을 참조하십시오). (자세한 내용은 유닉스 매뉴얼 페이지 signal(2)를 참조하십시오.)스레드가 활성화되었을 때, 이 함수는 메인 인터프리터의 메인 스레드에서만 호출할 수 있습니다; 다른 스레드에서 호출하려고 하면
ValueError
예외가 발생합니다.handler는 두 개의 인자로 호출됩니다: 시그널 번호와 현재 스택 프레임 (
None
이나 프레임 객체; 프레임 객체에 대한 설명은, 형 계층에 있는 설명을 참조하거나inspect
모듈의 어트리뷰트 설명을 참조하십시오).윈도우에서,
signal()
은SIGABRT
,SIGFPE
,SIGILL
,SIGINT
,SIGSEGV
,SIGTERM
또는SIGBREAK
로만 호출 할 수 있습니다. 다른 경우에는ValueError
가 발생합니다. 모든 시스템이 같은 시그널 이름 집합을 정의하는 것은 아님에 유의하십시오; 시그널 이름이SIG*
모듈 수준 상수로 정의되지 않으면AttributeError
가 발생합니다.
-
signal.
sigpending
()¶ 호출하는 스레드로 전달 계류 중인 시그널 집합을 검사합니다 (즉, 차단된 동안 발생한 시그널). 계류 중인 시그널 집합을 반환합니다.
가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 sigpending(2)를 참조하십시오.
pause()
,pthread_sigmask()
및sigwait()
도 참조하십시오.버전 3.3에 추가.
-
signal.
sigwait
(sigset)¶ 시그널 집합 sigset에 지정된 시그널 중 하나가 전달될 때까지 호출하는 스레드의 실행을 일시 중단합니다. 이 함수는 시그널을 받아들이고 (계류 중인 시그널 목록에서 제거합니다), 시그널 번호를 반환합니다.
가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 sigwait(3)을 참조하십시오.
pause()
,pthread_sigmask()
,sigpending()
,sigwaitinfo()
및sigtimedwait()
도 참조하십시오.버전 3.3에 추가.
-
signal.
sigwaitinfo
(sigset)¶ 시그널 집합 sigset에 지정된 시그널 중 하나가 전달될 때까지 호출하는 스레드의 실행을 일시 중단합니다. 이 함수는 시그널을 받아들이고 계류 중인 시그널 목록에서 제거합니다. sigset의 시그널 중 하나가 이미 호출하는 스레드에 대해 계류 중이면, 함수는 해당 시그널에 대한 정보와 함께 즉시 반환합니다. 전달된 시그널에 대해 시그널 처리기가 호출되지 않습니다. 이 함수는 sigset에 없는 시그널에 의해 중단되면
InterruptedError
를 발생시킵니다.반환 값은
siginfo_t
구조체에 포함된 데이터, 즉si_signo
,si_code
,si_errno
,si_pid
,si_uid
,si_status
,si_band
를 표현하는 객체입니다.가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 sigwaitinfo(2)를 참조하십시오.
pause()
,sigwait()
및sigtimedwait()
도 참조하십시오.버전 3.3에 추가.
버전 3.5에서 변경: 이 함수는 이제 sigset에 없는 시그널에 의해 중단되고 시그널 처리기가 예외를 발생시키지 않으면 재시도됩니다 (이유는 PEP 475를 참조하십시오).
-
signal.
sigtimedwait
(sigset, timeout)¶ sigwaitinfo()
와 유사하지만, 시간제한을 지정하는 추가 timeout 인자를 취합니다. timeout이0
으로 지정되면, 폴링이 수행됩니다. 시간제한 초과가 발생하면None
을 반환합니다.가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 sigtimedwait(2)를 참조하십시오.
pause()
,sigwait()
및sigwaitinfo()
도 참조하십시오.버전 3.3에 추가.
버전 3.5에서 변경: 이 함수는 이제 sigset에 없는 시그널에 의해 중단되고 시그널 처리기가 예외를 발생시키지 않으면 다시 계산된 timeout으로 재시도됩니다 (이유는 PEP 475를 참조하십시오).
예¶
다음은 최소한의 예제 프로그램입니다. alarm()
함수를 사용하여 파일을 여는 데 대기하는 시간을 제한합니다; 이것은 파일이 켜져 있지 않을 수 있는 직렬 장치를 위한 파일일 때 유용하며, 일반적으로 os.open()
이 무기한 정지됩니다. 해결책은 파일을 열기 전에 5초 알람을 설정하는 것입니다; 작업이 너무 오래 걸리면, 알람 시그널이 전송되고, 처리기가 예외를 발생시킵니다.
import signal, os
def handler(signum, frame):
print('Signal handler called with signal', signum)
raise OSError("Couldn't open device!")
# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)
# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)
signal.alarm(0) # Disable the alarm
SIGPIPE에 대한 참고 사항¶
프로그램의 출력을 head(1)와 같은 도구로 파이핑 하면 표준 출력의 수신기가 일찍 닫힐 때 여러분의 프로세스로 SIGPIPE
시그널이 전송됩니다. 이것은 BrokenPipeError: [Errno 32] Broken pipe
와 같은 예외를 일으킵니다. 이 경우를 처리하려면, 다음과 같이 이 예외를 포착하도록 진입점을 감싸십시오:
import os
import sys
def main():
try:
# simulate large output (your code replaces this loop)
for x in range(10000):
print("y")
# flush output here to force SIGPIPE to be triggered
# while inside this try block.
sys.stdout.flush()
except BrokenPipeError:
# Python flushes standard streams on exit; redirect remaining output
# to devnull to avoid another BrokenPipeError at shutdown
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno())
sys.exit(1) # Python exits with error code 1 on EPIPE
if __name__ == '__main__':
main()
Do not set SIGPIPE
’s disposition to SIG_DFL
in
order to avoid BrokenPipeError
. Doing that would cause
your program to exit unexpectedly whenever any socket
connection is interrupted while your program is still writing to
it.
Note on Signal Handlers and Exceptions¶
If a signal handler raises an exception, the exception will be propagated to
the main thread and may be raised after any bytecode instruction. Most
notably, a KeyboardInterrupt
may appear at any point during execution.
Most Python code, including the standard library, cannot be made robust against
this, and so a KeyboardInterrupt
(or any other exception resulting from
a signal handler) may on rare occasions put the program in an unexpected state.
To illustrate this issue, consider the following code:
class SpamContext:
def __init__(self):
self.lock = threading.Lock()
def __enter__(self):
# If KeyboardInterrupt occurs here, everything is fine
self.lock.acquire()
# If KeyboardInterrupt occcurs here, __exit__ will not be called
...
# KeyboardInterrupt could occur just before the function returns
def __exit__(self, exc_type, exc_val, exc_tb):
...
self.lock.release()
For many programs, especially those that merely want to exit on
KeyboardInterrupt
, this is not a problem, but applications that are
complex or require high reliability should avoid raising exceptions from signal
handlers. They should also avoid catching KeyboardInterrupt
as a means
of gracefully shutting down. Instead, they should install their own
SIGINT
handler. Below is an example of an HTTP server that avoids
KeyboardInterrupt
:
import signal
import socket
from selectors import DefaultSelector, EVENT_READ
from http.server import HTTPServer, SimpleHTTPRequestHandler
interrupt_read, interrupt_write = socket.socketpair()
def handler(signum, frame):
print('Signal handler called with signal', signum)
interrupt_write.send(b'\0')
signal.signal(signal.SIGINT, handler)
def serve_forever(httpd):
sel = DefaultSelector()
sel.register(interrupt_read, EVENT_READ)
sel.register(httpd, EVENT_READ)
while True:
for key, _ in sel.select():
if key.fileobj == interrupt_read:
interrupt_read.recv(1)
return
if key.fileobj == httpd:
httpd.handle_request()
print("Serving on port 8000")
httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler)
serve_forever(httpd)
print("Shutdown...")