@@ -46,6 +46,9 @@ This has consequences:
46
46
arbitrary amount of time, regardless of any signals received. The Python
47
47
signal handlers will be called when the calculation finishes.
48
48
49
+ * If the handler raises an exception, it will be raised "out of thin air" in
50
+ the main thread. See the :ref: `note below <handlers-and-exceptions >` for a
51
+ discussion.
49
52
50
53
.. _signals-and-threads :
51
54
@@ -677,3 +680,70 @@ Do not set :const:`SIGPIPE`'s disposition to :const:`SIG_DFL`
677
680
in order to avoid :exc: `BrokenPipeError `. Doing that would cause
678
681
your program to exit unexpectedly also whenever any socket connection
679
682
is interrupted while your program is still writing to it.
683
+
684
+ .. _handlers-and-exceptions :
685
+
686
+ Note on Signal Handlers and Exceptions
687
+ --------------------------------------
688
+
689
+ If a signal handler raises an exception, the exception will be propagated to
690
+ the main thread and may be raised after any :term: `bytecode ` instruction. Most
691
+ notably, a :exc: `KeyboardInterrupt ` may appear at any point during execution.
692
+ Most Python code, including the standard library, cannot be made robust against
693
+ this, and so a :exc: `KeyboardInterrupt ` (or any other exception resulting from
694
+ a signal handler) may on rare occasions put the program in an unexpected state.
695
+
696
+ To illustrate this issue, consider the following code::
697
+
698
+ class SpamContext:
699
+ def __init__(self):
700
+ self.lock = threading.Lock()
701
+
702
+ def __enter__(self):
703
+ # If KeyboardInterrupt occurs here, everything is fine
704
+ self.lock.acquire()
705
+ # If KeyboardInterrupt occcurs here, __exit__ will not be called
706
+ ...
707
+ # KeyboardInterrupt could occur just before the function returns
708
+
709
+ def __exit__(self, exc_type, exc_val, exc_tb):
710
+ ...
711
+ self.lock.release()
712
+
713
+ For many programs, especially those that merely want to exit on
714
+ :exc: `KeyboardInterrupt `, this is not a problem, but applications that are
715
+ complex or require high reliability should avoid raising exceptions from signal
716
+ handlers. They should also avoid catching :exc: `KeyboardInterrupt ` as a means
717
+ of gracefully shutting down. Instead, they should install their own
718
+ :const: `SIGINT ` handler. Below is an example of an HTTP server that avoids
719
+ :exc: `KeyboardInterrupt `::
720
+
721
+ import signal
722
+ import socket
723
+ from selectors import DefaultSelector, EVENT_READ
724
+ from http.server import HTTPServer, SimpleHTTPRequestHandler
725
+
726
+ interrupt_read, interrupt_write = socket.socketpair()
727
+
728
+ def handler(signum, frame):
729
+ print('Signal handler called with signal', signum)
730
+ interrupt_write.send(b'\0')
731
+ signal.signal(signal.SIGINT, handler)
732
+
733
+ def serve_forever(httpd):
734
+ sel = DefaultSelector()
735
+ sel.register(interrupt_read, EVENT_READ)
736
+ sel.register(httpd, EVENT_READ)
737
+
738
+ while True:
739
+ for key, _ in sel.select():
740
+ if key.fileobj == interrupt_read:
741
+ interrupt_read.recv(1)
742
+ return
743
+ if key.fileobj == httpd:
744
+ httpd.handle_request()
745
+
746
+ print("Serving on port 8000")
747
+ httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler)
748
+ serve_forever(httpd)
749
+ print("Shutdown...")
0 commit comments