mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Merge pull request #2464 from pjdelport/bugfix/unhide-cursor-on-sigint
Run the progress bar's cleanup code after keyboard interruptions.
This commit is contained in:
commit
7ef169b37b
1 changed files with 60 additions and 3 deletions
|
@ -3,6 +3,7 @@ from __future__ import division
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
import sys
|
import sys
|
||||||
|
from signal import signal, SIGINT, default_int_handler
|
||||||
|
|
||||||
from pip.compat import WINDOWS
|
from pip.compat import WINDOWS
|
||||||
from pip.utils import format_size
|
from pip.utils import format_size
|
||||||
|
@ -50,6 +51,61 @@ def _select_progress_class(preferred, fallback):
|
||||||
_BaseBar = _select_progress_class(IncrementalBar, Bar)
|
_BaseBar = _select_progress_class(IncrementalBar, Bar)
|
||||||
|
|
||||||
|
|
||||||
|
class InterruptibleMixin(object):
|
||||||
|
"""
|
||||||
|
Helper to ensure that self.finish() gets called on keyboard interrupt.
|
||||||
|
|
||||||
|
This allows downloads to be interrupted without leaving temporary state
|
||||||
|
(like hidden cursors) behind.
|
||||||
|
|
||||||
|
This class is similar to the progress library's existing SigIntMixin
|
||||||
|
helper, but as of version 1.2, that helper has the following problems:
|
||||||
|
|
||||||
|
1. It calls sys.exit().
|
||||||
|
2. It discards the existing SIGINT handler completely.
|
||||||
|
3. It leaves its own handler in place even after an uninterrupted finish,
|
||||||
|
which will have unexpected delayed effects if the user triggers an
|
||||||
|
unrelated keyboard interrupt some time after a progress-displaying
|
||||||
|
download has already completed, for example.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Save the original SIGINT handler for later.
|
||||||
|
"""
|
||||||
|
super(InterruptibleMixin, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.original_handler = signal(SIGINT, self.handle_sigint)
|
||||||
|
|
||||||
|
# If signal() returns None, the previous handler was not installed from
|
||||||
|
# Python, and we cannot restore it. This probably should not happen,
|
||||||
|
# but if it does, we must restore something sensible instead, at least.
|
||||||
|
# The least bad option should be Python's default SIGINT handler, which
|
||||||
|
# just raises KeyboardInterrupt.
|
||||||
|
if self.original_handler is None:
|
||||||
|
self.original_handler = default_int_handler
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
"""
|
||||||
|
Restore the original SIGINT handler after finishing.
|
||||||
|
|
||||||
|
This should happen regardless of whether the progress display finishes
|
||||||
|
normally, or gets interrupted.
|
||||||
|
"""
|
||||||
|
super(InterruptibleMixin, self).finish()
|
||||||
|
signal(SIGINT, self.original_handler)
|
||||||
|
|
||||||
|
def handle_sigint(self, signum, frame):
|
||||||
|
"""
|
||||||
|
Call self.finish() before delegating to the original SIGINT handler.
|
||||||
|
|
||||||
|
This handler should only be in place while the progress display is
|
||||||
|
active.
|
||||||
|
"""
|
||||||
|
self.finish()
|
||||||
|
self.original_handler(signum, frame)
|
||||||
|
|
||||||
|
|
||||||
class DownloadProgressMixin(object):
|
class DownloadProgressMixin(object):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -109,15 +165,16 @@ class WindowsMixin(object):
|
||||||
self.file.flush = lambda: self.file.wrapped.flush()
|
self.file.flush = lambda: self.file.wrapped.flush()
|
||||||
|
|
||||||
|
|
||||||
class DownloadProgressBar(WindowsMixin, DownloadProgressMixin, _BaseBar):
|
class DownloadProgressBar(WindowsMixin, InterruptibleMixin,
|
||||||
|
DownloadProgressMixin, _BaseBar):
|
||||||
|
|
||||||
file = sys.stdout
|
file = sys.stdout
|
||||||
message = "%(percent)d%%"
|
message = "%(percent)d%%"
|
||||||
suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s"
|
suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s"
|
||||||
|
|
||||||
|
|
||||||
class DownloadProgressSpinner(WindowsMixin, DownloadProgressMixin,
|
class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin,
|
||||||
WritelnMixin, Spinner):
|
DownloadProgressMixin, WritelnMixin, Spinner):
|
||||||
|
|
||||||
file = sys.stdout
|
file = sys.stdout
|
||||||
suffix = "%(downloaded)s %(download_speed)s"
|
suffix = "%(downloaded)s %(download_speed)s"
|
||||||
|
|
Loading…
Reference in a new issue