Signals handled by Python (especially KeyboardInterrupt)
may discard one level of exception handling.
Consider the following code fragment:
try: # outer exception handling level
try: # inner exception handling level
blocking_external_extension_function()
except:
print "inner exception handling"
except:
print "outer exception handling"
Assume that "blocking_external_extension_function"
is an external function (implemented in "C") that
blocks until interrupted by a signal (EINTR) and
then returns with an exception but without handling
the signal. In our concrete case,
"blocking_external_extension_function" has been
"psycopg.connect" (the database "connect" from the
Python-PostgreSQL bridge "psycopg").
In this case, when you interrupt the execution,
while "blocking_external_extension_function"
waits, the "KeyboardInterrupt" is not caught in
the inner "try: ...except: ..." but only in the outer
"try: ... except: ..."
The following happens in detail in Python's main
interpreter loop:
* "blocking_external_extension_function" returns with
an exception.
* Python sets the next instruction to the exception
handler of the inner exception handling
* Python begins a new run through its main loop.
At the start, it detects the "KeyboardInterrupt"
signal. It now thinks, the "KeyboardInterrupt" were
caused inside the inner exception handler
and pops this exception handling level without
executing it.
The interpreter has lost one level of exception
handling.
The behaviour would probably better fit with expectations,
when the interpreter would check for signals at the end of
its main loop and before it sets its instruction pointer to the
exception handler.
|