@@ -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
@@ -712,3 +715,70 @@ Do not set :const:`SIGPIPE`'s disposition to :const:`SIG_DFL`
712
715
in order to avoid :exc: `BrokenPipeError `. Doing that would cause
713
716
your program to exit unexpectedly also whenever any socket connection
714
717
is interrupted while your program is still writing to it.
718
+
719
+ .. _handlers-and-exceptions :
720
+
721
+ Note on Signal Handlers and Exceptions
722
+ --------------------------------------
723
+
724
+ If a signal handler raises an exception, the exception will be propagated to
725
+ the main thread and may be raised after any :term: `bytecode ` instruction. Most
726
+ notably, a :exc: `KeyboardInterrupt ` may appear at any point during execution.
727
+ Most Python code, including the standard library, cannot be made robust against
728
+ this, and so a :exc: `KeyboardInterrupt ` (or any other exception resulting from
729
+ a signal handler) may on rare occasions put the program in an unexpected state.
730
+
731
+ To illustrate this issue, consider the following code::
732
+
733
+ class SpamContext:
734
+ def __init__(self):
735
+ self.lock = threading.Lock()
736
+
737
+ def __enter__(self):
738
+ # If KeyboardInterrupt occurs here, everything is fine
739
+ self.lock.acquire()
740
+ # If KeyboardInterrupt occcurs here, __exit__ will not be called
741
+ ...
742
+ # KeyboardInterrupt could occur just before the function returns
743
+
744
+ def __exit__(self, exc_type, exc_val, exc_tb):
745
+ ...
746
+ self.lock.release()
747
+
748
+ For many programs, especially those that merely want to exit on
749
+ :exc: `KeyboardInterrupt `, this is not a problem, but applications that are
750
+ complex or require high reliability should avoid raising exceptions from signal
751
+ handlers. They should also avoid catching :exc: `KeyboardInterrupt ` as a means
752
+ of gracefully shutting down. Instead, they should install their own
753
+ :const: `SIGINT ` handler. Below is an example of an HTTP server that avoids
754
+ :exc: `KeyboardInterrupt `::
755
+
756
+ import signal
757
+ import socket
758
+ from selectors import DefaultSelector, EVENT_READ
759
+ from http.server import HTTPServer, SimpleHTTPRequestHandler
760
+
761
+ interrupt_read, interrupt_write = socket.socketpair()
762
+
763
+ def handler(signum, frame):
764
+ print('Signal handler called with signal', signum)
765
+ interrupt_write.send(b'\0')
766
+ signal.signal(signal.SIGINT, handler)
767
+
768
+ def serve_forever(httpd):
769
+ sel = DefaultSelector()
770
+ sel.register(interrupt_read, EVENT_READ)
771
+ sel.register(httpd, EVENT_READ)
772
+
773
+ while True:
774
+ for key, _ in sel.select():
775
+ if key.fileobj == interrupt_read:
776
+ interrupt_read.recv(1)
777
+ return
778
+ if key.fileobj == httpd:
779
+ httpd.handle_request()
780
+
781
+ print("Serving on port 8000")
782
+ httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler)
783
+ serve_forever(httpd)
784
+ print("Shutdown...")
0 commit comments