docs: document BaseException limitation in _suppress_output finally block
A KeyboardInterrupt raised inside the saved_out cleanup block would propagate past the saved_err and devnull_fd blocks, leaking those fds. In CPython this race is not realistically triggerable — KI is delivered between bytecodes and os.dup2 is a single atomic C syscall — so we accept the theoretical risk rather than silencing BaseException in a finally block.
This commit is contained in:
@@ -37,6 +37,11 @@ def _suppress_output():
|
|||||||
os.dup2(devnull_fd, 2)
|
os.dup2(devnull_fd, 2)
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
|
# Each block is a separate sequential statement. A BaseException (e.g.
|
||||||
|
# KeyboardInterrupt) raised inside block N would propagate past blocks N+1
|
||||||
|
# and N+2, leaving saved_err or devnull_fd unclosed. In CPython, KI is
|
||||||
|
# delivered between bytecodes, not mid-syscall; os.dup2 is a single C call
|
||||||
|
# and completes atomically, so this race is not realistically triggerable.
|
||||||
if saved_out is not None:
|
if saved_out is not None:
|
||||||
try:
|
try:
|
||||||
os.dup2(saved_out, 1)
|
os.dup2(saved_out, 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user