Handle keyboard interrupts more cleanly
@@ -24,6 +24,7 @@ import warnings
import enum
from multiprocessing.pool import ThreadPool, RUN
from threading import Thread, Event
+import signal
import xml.parsers.expat
from six.moves import zip, queue
import six
@@ -1448,6 +1449,8 @@ class ReplayGainPlugin(BeetsPlugin):
self.pool = ThreadPool(threads)
self.exc_queue = queue.Queue()
+ signal.signal(signal.SIGINT, self._interrupt)
+
self.exc_watcher = ExceptionWatcher(
self.exc_queue, # threads push exceptions here
self.terminate_pool # abort once an exception occurs
@@ -1480,11 +1483,22 @@ class ReplayGainPlugin(BeetsPlugin):
"""Terminate the `ThreadPool` instance in `self.pool`
(e.g. stop execution in case of exception)
"""
- if self._has_pool():
+ # Don't call self._as_pool() here,
+ # self.pool._state may not be == RUN
+ if hasattr(self, 'pool') and isinstance(self.pool, ThreadPool):
self.pool.terminate()
self.pool.join()
self.exc_watcher.join()
+ def _interrupt(self, signal, frame):
+ try:
+ self._log.info('interrupted')
+ self.terminate_pool()
+ exit(0)
+ except SystemExit:
+ # Silence raised SystemExit ~ exit(0)
+ pass
+
def close_pool(self):
"""Close the `ThreadPool` instance in `self.pool` (if there is one)
"""
@@ -1515,6 +1529,7 @@ class ReplayGainPlugin(BeetsPlugin):
"""Return the "replaygain" ui subcommand.
"""
def func(lib, opts, args):
+ try:
write = ui.should_write(opts.write)
force = opts.force
@@ -1541,6 +1556,9 @@ class ReplayGainPlugin(BeetsPlugin):
self.handle_track(item, write, force)
self.close_pool()
+ except (SystemExit, KeyboardInterrupt):
+ # Silence interrupt exceptions
+ pass
cmd = ui.Subcommand('replaygain', help=u'analyze for ReplayGain')
cmd.parser.add_album_option()
