Python masking exception?

It is typical to use a statement withto open a file so that the file descriptor cannot leak:

with open("myfile") as f:

But what if an exception occurs somewhere inside the call open? The function open, most likely, is not an atomic instruction in the Python interpreter, so it is quite possible that an asynchronous exception, such as KeyboardInterrupt, will be thrown * at some point before the call ends open, but after the system the call is already completed.

The usual way to control this (in, for example, POSIX signals) is to use a masking mechanism: when masking, the delivery of exceptions is suspended until they are removed without masking. This allows you to implement operations such as openin atomic mode. Is there such a primitive in Python?


[*] We can say that it does not matter for KeyboardInterrupt, since the program is still going to die, but this does not apply to all programs. It can be assumed that the program may prefer to catch KeyboardInterruptat the top level and continue execution, in which case the missing file descriptor may add up over time.

+4
source share
2 answers

, exceptions, signals, exceptions. KeyboardInterrupt - , signal.SIGINT ( Ctrl + C).

exceptions , , ? , ('', 'r'), file , , open IOError Exception, mask . , .

- ,

KeyboardInterrupt , , , signal, KeyboardInterrupt.

mask Unix, Python 3.3, signal.pthread_sigmask []

context expression , - mask , , , unmask , ( , ) -

import signal
signal.pthread_sigmask(signal.SIG_BLOCK,[signal.SIGINT])
with <context expression> as variable:  # in your case ,open('filename','r')

    signal.pthread_sigmask(signal.SIG_UNBLOCK,[signal.SIGINT])
...
+1

: , Python. KeyboardInterrupt AFAIK. , ( ?), , , , .

, :

try:
    handle = acquire_resource(…)
except BaseException as e:
    handle.release()
    raise e
else:
    with handle:
        
  • - : a KeyboardInterrupt , release.

  • "" try with, .

, , .

, , , , - . , @AnandSKumar. , pthreads.

, :

def masking_handler(queue, prev_handler):
    def handler(*args):
        queue.append(lambda: prev_handler[0](*args))
    return handler

mask_stack = []

def mask():
    queue = []
    prev_handler = []
    new_handler = masking_handler(queue, prev_handler)
    # swap out the signal handler with our own
    prev_handler[0] = signal.signal(signal.SIGINT, new_handler)
    mask_stack.append((prev_handler[0], queue))

def unmask():
    prev_handler, queue = mask_stack.pop()
    # restore the original signal handler
    signal.signal(signal.SIGINT, prev_handler)
    # the remaining part may never occur if a signal happens right now
    # but that fine
    for event in queue:
        event()

mask()
with acquire_resource(…) as handle:
    unmask()

, SIGINT - , . , , , , , , !

0

All Articles