signal
— Ställ in hanterare för asynkrona händelser¶
Källkod: Lib/signal.py
Denna modul tillhandahåller mekanismer för att använda signalhanterare i Python.
Allmänna regler¶
Funktionen signal.signal()
gör det möjligt att definiera anpassade hanterare som ska köras när en signal tas emot. Ett litet antal standardhanterare är installerade: SIGPIPE
ignoreras (så att skrivfel på pipes och sockets kan rapporteras som vanliga Python-undantag) och SIGINT
översätts till ett KeyboardInterrupt
-undantag om den överordnade processen inte har ändrat det.
En hanterare för en viss signal, när den väl är inställd, förblir installerad tills den uttryckligen återställs (Python emulerar BSD-stilgränssnittet oavsett den underliggande implementeringen), med undantag för hanteraren för SIGCHLD
, som följer den underliggande implementeringen.
På WebAssembly-plattformar är signaler emulerade och beter sig därför annorlunda. Flera funktioner och signaler är inte tillgängliga på dessa plattformar.
Exekvering av Python-signalhanterare¶
En Python-signalhanterare exekveras inte inuti lågnivåsignalhanteraren (C). Istället sätter signalhanteraren på låg nivå en flagga som talar om för virtual machine att exekvera motsvarande Python-signalhanterare vid ett senare tillfälle (till exempel vid nästa bytecode-instruktion). Detta har konsekvenser:
Det är meningslöst att fånga synkrona fel som
SIGFPE
ellerSIGSEGV
som orsakas av en ogiltig operation i C-koden. Python kommer att återvända från signalhanteraren till C-koden, som sannolikt kommer att ge upphov till samma signal igen, vilket gör att Python uppenbarligen hänger sig. Från och med Python 3.3 och framåt kan du använda modulenfaulthandler
för att rapportera synkrona fel.En långdragen beräkning som implementeras enbart i C (t.ex. matchning av reguljära uttryck i en stor textmassa) kan köras oavbrutet under godtycklig tid, oavsett vilka signaler som tas emot. Pythons signalhanterare kommer att anropas när beräkningen är klar.
Om hanteraren skapar ett undantag kommer det att skapas ”ur tomma intet” i huvudtråden. Se noten nedan för en diskussion.
Signaler och trådar¶
Pythons signalhanterare exekveras alltid i huvudtolkens Python-tråd, även om signalen mottogs i en annan tråd. Detta innebär att signaler inte kan användas som ett medel för kommunikation mellan trådar. Du kan använda synkroniseringsprimitiven från modulen threading
istället.
Dessutom är det bara huvudtråden i huvudtolken som får ställa in en ny signalhanterare.
Modulens innehåll¶
Ändrad i version 3.5: signal (SIG*), hanterare (SIG_DFL
, SIG_IGN
) och sigmask (SIG_BLOCK
, SIG_UNBLOCK
, SIG_SETMASK
) relaterade konstanter listade nedan omvandlades till enums
(Signals
, Handlers
respektive Sigmasks
). funktionerna getsignal()
, pthread_sigmask()
, sigpending()
och sigwait()
returnerar mänskligt läsbara enums
som Signals
-objekt.
Signalmodulen definierar tre enumer:
- class signal.Signals¶
enum.IntEnum
samling av SIG*-konstanter och CTRL_*-konstanter.Tillagd i version 3.5.
- class signal.Handlers¶
enum.IntEnum
samlar in konstanternaSIG_DFL
ochSIG_IGN
.Tillagd i version 3.5.
- class signal.Sigmasks¶
enum.IntEnum
samlar konstanternaSIG_BLOCK
,SIG_UNBLOCK
ochSIG_SETMASK
.Tillgänglighet: Unix.
Se man-sidorna sigprocmask(2) och pthread_sigmask(3) för ytterligare information.
Tillagd i version 3.5.
De variabler som definieras i modulen signal
är:
- signal.SIG_DFL¶
Detta är ett av två standardalternativ för signalhantering; det kommer helt enkelt att utföra standardfunktionen för signalen. På de flesta system är till exempel standardåtgärden för
SIGQUIT
att dumpa kärnan och avsluta, medan standardåtgärden förSIGCHLD
är att helt enkelt ignorera den.
- signal.SIG_IGN¶
Detta är en annan standardsignalhanterare, som helt enkelt ignorerar den givna signalen.
- signal.SIGALRM¶
Timersignal från alarm(2).
Tillgänglighet: Unix.
- signal.SIGBREAK¶
Avbrott från tangentbordet (CTRL + BREAK).
Tillgänglighet: Windows.
- signal.SIGBUS¶
Bussfel (dålig minnesåtkomst).
Tillgänglighet: Unix.
- signal.SIGCHLD¶
Barnprocessen stoppades eller avslutades.
Tillgänglighet: Unix.
- signal.SIGCLD¶
Alias till
SIGCHLD
.Tillgänglighet: not macOS.
- signal.SIGCONT¶
Fortsätt processen om den för närvarande är stoppad
Tillgänglighet: Unix.
- signal.SIGFPE¶
Undantag för flyttal. Till exempel division med noll.
Se även
ZeroDivisionError
uppstår när det andra argumentet i en divisions- eller modulooperation är noll.
- signal.SIGHUP¶
Upphängning upptäckt på styrande terminal eller död i styrande process.
Tillgänglighet: Unix.
- signal.SIGILL¶
Olaglig instruktion.
- signal.SIGINT¶
Avbrott från tangentbordet (CTRL + C).
Standardåtgärden är att skapa
KeyboardInterrupt
.
- signal.SIGKILL¶
Dödssignal.
Den kan inte fångas, blockeras eller ignoreras.
Tillgänglighet: Unix.
- signal.SIGPIPE¶
Trasigt rör: skriv till ett rör utan läsare.
Standardåtgärden är att ignorera signalen.
Tillgänglighet: Unix.
- signal.SIGSEGV¶
Segmenteringsfel: ogiltig minnesreferens.
- signal.SIGSTKFLT¶
Stapelfel på coprocessor. Linuxkärnan ger inte ut den här signalen: den kan bara ges ut i användarutrymmet.
Tillgänglighet: Linux.
På arkitekturer där signalen är tillgänglig. Se man-sidan signal(7) för ytterligare information.
Tillagd i version 3.11.
- signal.SIGTERM¶
Signal för avslutning.
- signal.SIGUSR1¶
Användardefinierad signal 1.
Tillgänglighet: Unix.
- signal.SIGUSR2¶
Användardefinierad signal 2.
Tillgänglighet: Unix.
- signal.SIGWINCH¶
Signal för ändring av fönsterstorlek.
Tillgänglighet: Unix.
- SIG*
Alla signalnummer är symboliskt definierade. Till exempel definieras hangup-signalen som
signal.SIGHUP
; variabelnamnen är identiska med de namn som används i C-program och som finns i<signal.h>
. Unix man page för ’signal()
’ listar de befintliga signalerna (på vissa system är detta signal(2), på andra finns listan i signal(7)). Observera att inte alla system definierar samma uppsättning signalnamn; endast de namn som definieras av systemet definieras av den här modulen.
- signal.CTRL_C_EVENT¶
Den signal som motsvarar händelsen Ctrl+C tangenttryckning. Denna signal kan endast användas med
os.kill()
.Tillgänglighet: Windows.
Tillagd i version 3.2.
- signal.CTRL_BREAK_EVENT¶
Den signal som motsvarar händelsen Ctrl+Break tangenttryckning. Denna signal kan endast användas med
os.kill()
.Tillgänglighet: Windows.
Tillagd i version 3.2.
- signal.NSIG¶
Ett mer än numret på det högsta signalnumret. Använd
valid_signals()
för att få giltiga signalnummer.
- signal.ITIMER_VIRTUAL¶
Minskar intervalltimern endast när processen körs och levererar SIGVTALRM när den löper ut.
- signal.ITIMER_PROF¶
Minskar intervalltimern både när processen körs och när systemet körs på uppdrag av processen. Tillsammans med ITIMER_VIRTUAL används den här timern vanligtvis för att profilera den tid som programmet spenderar i användar- och kärnutrymmet. SIGPROF levereras när den löper ut.
- signal.SIG_BLOCK¶
Ett möjligt värde för parametern how i
pthread_sigmask()
som anger att signaler ska blockeras.Tillagd i version 3.3.
- signal.SIG_UNBLOCK¶
Ett möjligt värde för how-parametern till
pthread_sigmask()
som anger att signaler ska avblockeras.Tillagd i version 3.3.
- signal.SIG_SETMASK¶
Ett möjligt värde för how-parametern till
pthread_sigmask()
som anger att signalmasken ska ersättas.Tillagd i version 3.3.
Modulen signal
definierar ett undantag:
- exception signal.ItimerError¶
Utlöses för att signalera ett fel från den underliggande
setitimer()
ellergetitimer()
implementationen. Förvänta dig detta fel om en ogiltig intervalltimer eller en negativ tid skickas tillsetitimer()
. Detta fel är en subtyp avOSError
.
Modulen signal
definierar följande funktioner:
- signal.alarm(time)¶
Om time inte är noll, begär denna funktion att en
SIGALRM
-signal skickas till processen om time sekunder. Eventuella tidigare schemalagda larm avbryts (endast ett larm kan schemaläggas åt gången). Det returnerade värdet är då antalet sekunder innan något tidigare inställt larm skulle ha levererats. Om time är noll är inget larm schemalagt och ett eventuellt schemalagt larm avbryts. Om returvärdet är noll är inget larm för närvarande schemalagt.Tillgänglighet: Unix.
Se manualsidan alarm(2) för ytterligare information.
- signal.getsignal(signalnum)¶
Returnerar den aktuella signalhanteraren för signalen signalnum. Det returnerade värdet kan vara ett anropbart Python-objekt eller ett av specialvärdena
signal.SIG_IGN
,signal.SIG_DFL
ellerNone
. Här betydersignal.SIG_IGN
att signalen tidigare ignorerades,signal.SIG_DFL
betyder att standardmetoden för att hantera signalen tidigare användes ochNone
betyder att den tidigare signalhanteraren inte installerades från Python.
- signal.strsignal(signalnum)¶
Returnerar beskrivningen av signalen signalnum, t.ex. ”Interrupt” för
SIGINT
. ReturnerarNone
om signalnum inte har någon beskrivning. UtlöserValueError
om signalnum är ogiltig.Tillagd i version 3.8.
- signal.valid_signals()¶
Returnerar uppsättningen av giltiga signalnummer på denna plattform. Detta kan vara mindre än
range(1, NSIG)
om vissa signaler är reserverade av systemet för internt bruk.Tillagd i version 3.8.
- signal.pause()¶
Får processen att sova tills en signal tas emot; lämplig hanterare kommer då att anropas. Returnerar ingenting.
Tillgänglighet: Unix.
Se man-sidan signal(2) för ytterligare information.
Se även
sigwait()
,sigwaitinfo()
,sigtimedwait()
ochsigpending()
.
- signal.raise_signal(signum)¶
Skickar en signal till den anropande processen. Returnerar ingenting.
Tillagd i version 3.8.
- signal.pidfd_send_signal(pidfd, sig, siginfo=None, flags=0)¶
Skicka signalen sig till den process som filbeskrivaren pidfd refererar till. Python har för närvarande inte stöd för parametern siginfo; den måste vara
None
. Argumentet flags tillhandahålls för framtida tillägg; inga flaggvärden är för närvarande definierade.Se pidfd_send_signal(2) man page för mer information.
Tillgänglighet: Linux >= 5.1, Android >=
build-time
API level 31Tillagd i version 3.9.
- signal.pthread_kill(thread_id, signalnum)¶
Skicka signalen signalnum till tråden thread_id, en annan tråd i samma process som anroparen. Måltråden kan exekvera vilken kod som helst (Python eller inte). Men om måltråden exekverar Python-tolken kommer Python-signalhanterarna att exekveras av huvudtråden i huvudtolken. Därför skulle den enda poängen med att skicka en signal till en viss Python-tråd vara att tvinga ett pågående systemanrop att misslyckas med
InterruptedError
.Använd
threading.get_ident()
eller attributetident
ithreading.Thread
-objekt för att få ett lämpligt värde för thread_id.Om signalnum är 0 skickas ingen signal, men felkontroll utförs ändå; detta kan användas för att kontrollera om måltråden fortfarande körs.
Utlöser en auditing event
signal.pthread_kill
med argumententhread_id
,signalnum
.Tillgänglighet: Unix.
Se man-sidan pthread_kill(3) för ytterligare information.
Se även
os.kill()
.Tillagd i version 3.3.
- signal.pthread_sigmask(how, mask)¶
Hämta och/eller ändra signalmasken för den anropande tråden. Signalmasken är den uppsättning signaler vars leverans för närvarande är blockerad för den anropande tråden. Returnera den gamla signalmasken som en uppsättning signaler.
Anropets beteende är beroende av värdet på how, enligt följande.
SIG_BLOCK
: Uppsättningen av blockerade signaler är en förening av den aktuella uppsättningen och argumentet mask.SIG_UNBLOCK
: Signalerna i mask tas bort från den aktuella uppsättningen av blockerade signaler. Det är tillåtet att försöka avblockera en signal som inte är blockerad.SIG_SETMASK
: Uppsättningen av blockerade signaler ställs in till argumentet mask.
mask är en uppsättning signalnummer (t.ex. {
signal.SIGINT
,signal.SIGTERM
}). Användvalid_signals()
för en fullständig mask som inkluderar alla signaler.Till exempel läser
signal.pthread_sigmask(signal.SIG_BLOCK, [])
signalmasken för den anropande tråden.SIGKILL
ochSIGSTOP
kan inte blockeras.Tillgänglighet: Unix.
Se man-sidorna sigprocmask(2) och pthread_sigmask(3) för ytterligare information.
Se även
pause()
,sigpending()
ochsigwait()
.Tillagd i version 3.3.
- signal.setitimer(which, seconds, interval=0.0)¶
Ställer in en given intervalltimer (en av
signal.ITIMER_REAL
,signal.ITIMER_VIRTUAL
ellersignal.ITIMER_PROF
) specificerad av which att starta efter seconds (float accepteras, skiljer sig frånalarm()
) och därefter var intervall sekund (om intervall inte är noll). Intervalltimern som anges av which kan rensas genom att seconds sätts till noll.När en intervalltimer startar skickas en signal till processen. Signalen som skickas beror på vilken timer som används;
signal.ITIMER_REAL
kommer att levereraSIGALRM
,signal.ITIMER_VIRTUAL
skickarSIGVTALRM
, ochsignal.ITIMER_PROF
kommer att levereraSIGPROF
.De gamla värdena returneras som en tupel: (delay, interval).
Försök att skicka en ogiltig intervalltimer kommer att orsaka ett
ItimerError
.Tillgänglighet: Unix.
- signal.getitimer(which)¶
Returnerar aktuellt värde för en given intervalltimer som specificeras av which.
Tillgänglighet: Unix.
- signal.set_wakeup_fd(fd, *, warn_on_full_buffer=True)¶
Ställ in wakeup-filens deskriptor till fd. När en signal som ditt program har registrerat en signalhanterare för tas emot, skrivs signalnumret som en enda byte till fd. Om du inte har registrerat en signalhanterare för de signaler du bryr dig om, skrivs ingenting till wakeup fd. Detta kan användas av ett bibliotek för att väcka ett poll- eller select-anrop, så att signalen kan bearbetas fullständigt.
Den gamla wakeup fd returneras (eller -1 om wakeup för filbeskrivare inte var aktiverad). Om fd är -1 inaktiveras väckning av filbeskrivare. Om fd inte är -1 måste den vara icke-blockerande. Det är upp till biblioteket att ta bort eventuella byte från fd innan poll eller select anropas igen.
När trådar är aktiverade kan den här funktionen endast anropas från huvudtråden i huvudtolken; om du försöker anropa den från andra trådar kommer ett
ValueError
-undantag att uppstå.Det finns två vanliga sätt att använda den här funktionen. I båda fallen använder man fd för att vakna när en signal kommer, men sedan skiljer de sig åt i hur de avgör vilken signal eller vilka signaler som har kommit.
I den första metoden läser vi ut data ur fd:ns buffert, och bytevärdena ger dig signalnumren. Det här är enkelt, men i sällsynta fall kan det uppstå problem: i allmänhet har fd en begränsad mängd buffertutrymme, och om för många signaler kommer för snabbt kan bufferten bli full och vissa signaler kan gå förlorade. Om du använder det här tillvägagångssättet bör du ställa in
warn_on_full_buffer=True
, vilket åtminstone kommer att leda till att en varning skrivs ut till stderr när signaler går förlorade.I det andra tillvägagångssättet använder vi wakeup fd endast för väckningar och ignorerar de faktiska bytevärdena. I det här fallet är allt vi bryr oss om huruvida fd:s buffert är tom eller inte; en full buffert indikerar inte alls något problem. Om du använder det här tillvägagångssättet bör du ställa in
warn_on_full_buffer=False
, så att dina användare inte förvirras av falska varningsmeddelanden.Ändrad i version 3.5: I Windows stöder funktionen nu även socket-handtag.
Ändrad i version 3.7: Lagt till parametern
warn_on_full_buffer
.
- signal.siginterrupt(signalnum, flag)¶
Ändra beteende för omstart av systemanrop: om flag är
False
, kommer systemanrop att startas om när de avbryts av signal signalnum, annars kommer systemanrop att avbrytas. Returnerar ingenting.Tillgänglighet: Unix.
Se manualsidan siginterrupt(3) för ytterligare information.
Observera att installation av en signalhanterare med
signal()
kommer att återställa omstartbeteendet till avbrytbart genom att implicit anropasiginterrupt()
med ett sant flagg-värde för den givna signalen.
- signal.signal(signalnum, handler)¶
Ställ in hanteraren för signalen signalnum till funktionen handler. handler kan vara ett anropbart Python-objekt som tar två argument (se nedan), eller ett av specialvärdena
signal.SIG_IGN
ellersignal.SIG_DFL
. Den föregående signalhanteraren kommer att returneras (se beskrivningen avgetsignal()
ovan). (Se Unix man page signal(2) för ytterligare information)När trådar är aktiverade kan den här funktionen endast anropas från huvudtråden i huvudtolken; om du försöker anropa den från andra trådar kommer ett
ValueError
-undantag att uppstå.Handler anropas med två argument: signalnumret och den aktuella stackramen (
None
eller ett ramobjekt; för en beskrivning av ramobjekt, se description i typhierarkin eller se attributbeskrivningarna i moduleninspect
).I Windows kan
signal()
endast anropas medSIGABRT
,SIGFPE
,SIGILL
,SIGINT
,SIGSEGV
,SIGTERM
ellerSIGBREAK
. EttValueError
kommer att uppstå i alla andra fall. Observera att inte alla system definierar samma uppsättning signalnamn; ettAttributeError
kommer att uppstå om ett signalnamn inte är definierat somSIG*
konstant på modulnivå.
- signal.sigpending()¶
Undersök uppsättningen signaler som väntar på att levereras till den anropande tråden (dvs. de signaler som har skapats medan de blockerats). Returnera uppsättningen av de väntande signalerna.
Tillgänglighet: Unix.
Se manualsidan sigpending(2) för ytterligare information.
Se även
pause()
,pthread_sigmask()
ochsigwait()
.Tillagd i version 3.3.
- signal.sigwait(sigset)¶
Avbryter exekveringen av den anropande tråden tills en av de signaler som anges i signaluppsättningen sigset har levererats. Funktionen accepterar signalen (tar bort den från listan över väntande signaler) och returnerar signalnumret.
Tillgänglighet: Unix.
Se manualsidan sigwait(3) för ytterligare information.
Se även
pause()
,pthread_sigmask()
,sigpending()
,sigwaitinfo()
ochsigtimedwait()
.Tillagd i version 3.3.
- signal.sigwaitinfo(sigset)¶
Avbryt exekveringen av den anropande tråden tills en av de signaler som anges i signaluppsättningen sigset har levererats. Funktionen accepterar signalen och tar bort den från listan över väntande signaler. Om en av signalerna i sigset redan är väntande för den anropande tråden, kommer funktionen att returnera omedelbart med information om den signalen. Signalhanteraren anropas inte för den levererade signalen. Funktionen ger upphov till ett
InterruptedError
om den avbryts av en signal som inte finns i sigset.Returvärdet är ett objekt som representerar de data som finns i strukturen
siginfo_t
, nämligen:si_signo
,si_code
,si_errno
,si_pid
,si_uid
,si_status
,si_band
.Tillgänglighet: Unix.
Se manualsidan sigwaitinfo(2) för ytterligare information.
Se även
pause()
,sigwait()
ochsigtimedwait()
.Tillagd i version 3.3.
Ändrad i version 3.5: Funktionen prövas nu igen om den avbryts av en signal som inte finns i sigset och signalhanteraren inte ger upphov till ett undantag (se PEP 475 för motivering).
- signal.sigtimedwait(sigset, timeout)¶
Som
sigwaitinfo()
, men tar ytterligare ett timeout-argument som anger en timeout. Om timeout anges som0
utförs en pollning. ReturnerarNone
om en timeout inträffar.Tillgänglighet: Unix.
Se manualsidan sigtimedwait(2) för ytterligare information.
Se även
pause()
,sigwait()
ochsigwaitinfo()
.Tillagd i version 3.3.
Ändrad i version 3.5: Funktionen försöker nu igen med den omräknade timeout om den avbryts av en signal som inte finns i sigset och signalhanteraren inte gör ett undantag (se PEP 475 för motivering).
Exempel¶
Här är ett minimalt exempelprogram. Det använder funktionen alarm()
för att begränsa väntetiden för att öppna en fil; detta är användbart om filen är för en seriell enhet som kanske inte är påslagen, vilket normalt skulle få os.open()
att hänga sig på obestämd tid. Lösningen är att ställa in ett 5-sekunders alarm innan filen öppnas; om operationen tar för lång tid skickas alarmsignalen och hanteraren gör ett undantag.
import signal, os
def handler(signum, frame):
signame = signal.Signals(signum).name
print(f'Signal handler called with signal {signame} ({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
Anmärkning om SIGPIPE¶
Att pipa utdata från ditt program till verktyg som head(1) kommer att orsaka att en SIGPIPE
-signal skickas till din process när mottagaren av dess standardutdata stängs tidigt. Detta resulterar i ett undantag som BrokenPipeError: [Errno 32] Broken pipe
. För att hantera det här fallet, linda in din ingångspunkt för att fånga det här undantaget enligt följande:
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()
Ställ inte in SIGPIPE
disposition till SIG_DFL
för att undvika BrokenPipeError
. Om du gör det kommer ditt program att avslutas oväntat när en socketanslutning avbryts medan ditt program fortfarande skriver till den.
Anmärkning om signalhanterare och undantag¶
Om en signalhanterare ger upphov till ett undantag, kommer undantaget att spridas till huvudtråden och kan ges upphov till efter varje bytecode-instruktion. Mest anmärkningsvärt är att ett KeyboardInterrupt
kan dyka upp när som helst under exekveringen. Den mesta Python-koden, inklusive standardbiblioteket, kan inte göras robust mot detta, och därför kan ett KeyboardInterrupt
(eller något annat undantag som härrör från en signalhanterare) vid sällsynta tillfällen försätta programmet i ett oväntat tillstånd.
För att illustrera detta problem kan du titta på följande kod:
class SpamContext:
def __init__(self):
self.lock = threading.Lock()
def __enter__(self):
# If KeyboardInterrupt occurs here, everything is fine
self.lock.acquire()
# If KeyboardInterrupt occurs 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()
För många program, speciellt de som bara vill avsluta på KeyboardInterrupt
, är detta inget problem, men applikationer som är komplexa eller kräver hög tillförlitlighet bör undvika att skapa undantag från signalhanterare. De bör också undvika att fånga KeyboardInterrupt
som ett sätt att stänga av på ett elegant sätt. Istället bör de installera sin egen SIGINT
-hanterare. Nedan följer ett exempel på en HTTP-server som undviker 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...")