@@ -0,0 +1,2569 @@
+# -*- coding: utf-8 -*-%0A%0Afrom __future__ import (division, print_function, absolute_import,%0A                        unicode_literals)%0A%0A__all__ = %5B%22InterruptiblePool%22%5D%0A%0Aimport signal%0Aimport functools%0Afrom multiprocessing.pool import Pool%0Afrom multiprocessing import TimeoutError%0A%0A%0Adef _initializer_wrapper(actual_initializer, *rest):%0A    %22%22%22%0A    We ignore SIGINT. It's up to our parent to kill us in the typical%0A    condition of this arising from %60%60%5EC%60%60 on a terminal. If someone is%0A    manually killing us with that signal, well... nothing will happen.%0A    %22%22%22%0A    signal.signal(signal.SIGINT, signal.SIG_IGN)%0A    if actual_initializer is not None:%0A        actual_initializer(*rest)%0A%0A%0Aclass InterruptiblePool(Pool):%0A    %22%22%22%0A    A modified version of :class:%60multiprocessing.pool.Pool%60 that has better%0A    behavior with regard to %60%60KeyboardInterrupts%60%60 in the :func:%60map%60 method.%0A    :param processes: (optional)%0A        The number of worker processes to use; defaults to the number of CPUs.%0A    :param initializer: (optional)%0A        Either %60%60None%60%60, or a callable that will be invoked by each worker%0A        process when it starts.%0A    :param initargs: (optional)%0A        Arguments for *initializer*; it will be called as%0A        %60%60initializer(*initargs)%60%60.%0A    :param kwargs: (optional)%0A        Extra arguments. Python 2.7 supports a %60%60maxtasksperchild%60%60 parameter.%0A    %22%22%22%0A    wait_timeout = 3600%0A%0A    def __init__(self, processes=None, initializer=None, initargs=(),%0A                 **kwargs):%0A        new_initializer = functools.partial(_initializer_wrapper, initializer)%0A        super(InterruptiblePool, self).__init__(processes, new_initializer,%0A                                                initargs, **kwargs)%0A%0A    def map(self, func, iterable, chunksize=None):%0A        %22%22%22%0A        Equivalent of %60%60map()%60%60 built-in, without swallowing%0A        %60%60KeyboardInterrupt%60%60.%0A        :param func:%0A            The function to apply to the items.%0A        :param iterable:%0A            An iterable of items that will have %60func%60 applied to them.%0A        %22%22%22%0A        # The key magic is that we must call r.get() with a timeout, because%0A        # a Condition.wait() without a timeout swallows KeyboardInterrupts.%0A        r = self.map_async(func, iterable, chunksize)%0A%0A        while True:%0A            try:%0A                return r.get(self.wait_timeout)%0A            except TimeoutError:%0A                pass%0A            except KeyboardInterrupt:%0A                self.terminate()%0A                self.join()%0A                raise%0A            # Other exceptions propagate up.%0A

Create interruptible_pool.py