prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Interrupting a pip download with CTRL-C does not unhide the cursor
  Version: pep 6.0.8
  
  Pressing CTRL-C while pip is downloading a package (and displaying its progress bar) shows an "Operation cancelled by user" message and drops the user back to the shell prompt, but does not unhide the cursor that was hidden while displaying the progress bar.
  
  Glancing at the code, it looks like the `progress` library already offers a `SigIntMixin` helper for handling this, and that pip's progress bar isn't using it. Maybe including this mixin in the appropriate place(s) is all that's needed?
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `pip/utils/ui.py`
  Content:
  ```
  1 from __future__ import absolute_import
  2 from __future__ import division
  3 
  4 import itertools
  5 import sys
  6 
  7 from pip.compat import WINDOWS
  8 from pip.utils import format_size
  9 from pip.utils.logging import get_indentation
  10 from pip._vendor import six
  11 from pip._vendor.progress.bar import Bar, IncrementalBar
  12 from pip._vendor.progress.helpers import WritelnMixin
  13 from pip._vendor.progress.spinner import Spinner
  14 
  15 try:
  16     from pip._vendor import colorama
  17 # Lots of different errors can come from this, including SystemError and
  18 # ImportError.
  19 except Exception:
  20     colorama = None
  21 
  22 
  23 def _select_progress_class(preferred, fallback):
  24     encoding = getattr(preferred.file, "encoding", None)
  25 
  26     # If we don't know what encoding this file is in, then we'll just assume
  27     # that it doesn't support unicode and use the ASCII bar.
  28     if not encoding:
  29         return fallback
  30 
  31     # Collect all of the possible characters we want to use with the preferred
  32     # bar.
  33     characters = [
  34         getattr(preferred, "empty_fill", six.text_type()),
  35         getattr(preferred, "fill", six.text_type()),
  36     ]
  37     characters += list(getattr(preferred, "phases", []))
  38 
  39     # Try to decode the characters we're using for the bar using the encoding
  40     # of the given file, if this works then we'll assume that we can use the
  41     # fancier bar and if not we'll fall back to the plaintext bar.
  42     try:
  43         six.text_type().join(characters).encode(encoding)
  44     except UnicodeEncodeError:
  45         return fallback
  46     else:
  47         return preferred
  48 
  49 
  50 _BaseBar = _select_progress_class(IncrementalBar, Bar)
  51 
  52 
  53 class DownloadProgressMixin(object):
  54 
  55     def __init__(self, *args, **kwargs):
  56         super(DownloadProgressMixin, self).__init__(*args, **kwargs)
  57         self.message = (" " * (get_indentation() + 2)) + self.message
  58 
  59     @property
  60     def downloaded(self):
  61         return format_size(self.index)
  62 
  63     @property
  64     def download_speed(self):
  65         # Avoid zero division errors...
  66         if self.avg == 0.0:
  67             return "..."
  68         return format_size(1 / self.avg) + "/s"
  69 
  70     @property
  71     def pretty_eta(self):
  72         if self.eta:
  73             return "eta %s" % self.eta_td
  74         return ""
  75 
  76     def iter(self, it, n=1):
  77         for x in it:
  78             yield x
  79             self.next(n)
  80         self.finish()
  81 
  82 
  83 class WindowsMixin(object):
  84 
  85     def __init__(self, *args, **kwargs):
  86         # The Windows terminal does not support the hide/show cursor ANSI codes
  87         # even with colorama. So we'll ensure that hide_cursor is False on
  88         # Windows.
  89         # This call neds to go before the super() call, so that hide_cursor
  90         # is set in time. The base progress bar class writes the "hide cursor"
  91         # code to the terminal in its init, so if we don't set this soon
  92         # enough, we get a "hide" with no corresponding "show"...
  93         if WINDOWS and self.hide_cursor:
  94             self.hide_cursor = False
  95 
  96         super(WindowsMixin, self).__init__(*args, **kwargs)
  97 
  98         # Check if we are running on Windows and we have the colorama module,
  99         # if we do then wrap our file with it.
  100         if WINDOWS and colorama:
  101             self.file = colorama.AnsiToWin32(self.file)
  102             # The progress code expects to be able to call self.file.isatty()
  103             # but the colorama.AnsiToWin32() object doesn't have that, so we'll
  104             # add it.
  105             self.file.isatty = lambda: self.file.wrapped.isatty()
  106             # The progress code expects to be able to call self.file.flush()
  107             # but the colorama.AnsiToWin32() object doesn't have that, so we'll
  108             # add it.
  109             self.file.flush = lambda: self.file.wrapped.flush()
  110 
  111 
  112 class DownloadProgressBar(WindowsMixin, DownloadProgressMixin, _BaseBar):
  113 
  114     file = sys.stdout
  115     message = "%(percent)d%%"
  116     suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s"
  117 
  118 
  119 class DownloadProgressSpinner(WindowsMixin, DownloadProgressMixin,
  120                               WritelnMixin, Spinner):
  121 
  122     file = sys.stdout
  123     suffix = "%(downloaded)s %(download_speed)s"
  124 
  125     def next_phase(self):
  126         if not hasattr(self, "_phaser"):
  127             self._phaser = itertools.cycle(self.phases)
  128         return next(self._phaser)
  129 
  130     def update(self):
  131         message = self.message % self
  132         phase = self.next_phase()
  133         suffix = self.suffix % self
  134         line = ''.join([
  135             message,
  136             " " if message else "",
  137             phase,
  138             " " if suffix else "",
  139             suffix,
  140         ])
  141 
  142         self.writeln(line)
  143
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/pip/utils/ui.py b/pip/utils/ui.py
  --- a/pip/utils/ui.py
  +++ b/pip/utils/ui.py
  @@ -3,6 +3,7 @@
   
   import itertools
   import sys
  +from signal import signal, SIGINT, default_int_handler
   
   from pip.compat import WINDOWS
   from pip.utils import format_size
  @@ -50,6 +51,61 @@
   _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):
   
       def __init__(self, *args, **kwargs):
  @@ -109,15 +165,16 @@
               self.file.flush = lambda: self.file.wrapped.flush()
   
   
  -class DownloadProgressBar(WindowsMixin, DownloadProgressMixin, _BaseBar):
  +class DownloadProgressBar(WindowsMixin, InterruptibleMixin,
  +                          DownloadProgressMixin, _BaseBar):
   
       file = sys.stdout
       message = "%(percent)d%%"
       suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s"
   
   
  -class DownloadProgressSpinner(WindowsMixin, DownloadProgressMixin,
  -                              WritelnMixin, Spinner):
  +class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin,
  +                              DownloadProgressMixin, WritelnMixin, Spinner):
   
       file = sys.stdout
       suffix = "%(downloaded)s %(download_speed)s"
  
verification_info: {"golden_diff": "diff --git a/pip/utils/ui.py b/pip/utils/ui.py\n--- a/pip/utils/ui.py\n+++ b/pip/utils/ui.py\n@@ -3,6 +3,7 @@\n \n import itertools\n import sys\n+from signal import signal, SIGINT, default_int_handler\n \n from pip.compat import WINDOWS\n from pip.utils import format_size\n@@ -50,6 +51,61 @@\n _BaseBar = _select_progress_class(IncrementalBar, Bar)\n \n \n+class InterruptibleMixin(object):\n+    \"\"\"\n+    Helper to ensure that self.finish() gets called on keyboard interrupt.\n+\n+    This allows downloads to be interrupted without leaving temporary state\n+    (like hidden cursors) behind.\n+\n+    This class is similar to the progress library's existing SigIntMixin\n+    helper, but as of version 1.2, that helper has the following problems:\n+\n+    1. It calls sys.exit().\n+    2. It discards the existing SIGINT handler completely.\n+    3. It leaves its own handler in place even after an uninterrupted finish,\n+       which will have unexpected delayed effects if the user triggers an\n+       unrelated keyboard interrupt some time after a progress-displaying\n+       download has already completed, for example.\n+    \"\"\"\n+\n+    def __init__(self, *args, **kwargs):\n+        \"\"\"\n+        Save the original SIGINT handler for later.\n+        \"\"\"\n+        super(InterruptibleMixin, self).__init__(*args, **kwargs)\n+\n+        self.original_handler = signal(SIGINT, self.handle_sigint)\n+\n+        # If signal() returns None, the previous handler was not installed from\n+        # Python, and we cannot restore it. This probably should not happen,\n+        # but if it does, we must restore something sensible instead, at least.\n+        # The least bad option should be Python's default SIGINT handler, which\n+        # just raises KeyboardInterrupt.\n+        if self.original_handler is None:\n+            self.original_handler = default_int_handler\n+\n+    def finish(self):\n+        \"\"\"\n+        Restore the original SIGINT handler after finishing.\n+\n+        This should happen regardless of whether the progress display finishes\n+        normally, or gets interrupted.\n+        \"\"\"\n+        super(InterruptibleMixin, self).finish()\n+        signal(SIGINT, self.original_handler)\n+\n+    def handle_sigint(self, signum, frame):\n+        \"\"\"\n+        Call self.finish() before delegating to the original SIGINT handler.\n+\n+        This handler should only be in place while the progress display is\n+        active.\n+        \"\"\"\n+        self.finish()\n+        self.original_handler(signum, frame)\n+\n+\n class DownloadProgressMixin(object):\n \n     def __init__(self, *args, **kwargs):\n@@ -109,15 +165,16 @@\n             self.file.flush = lambda: self.file.wrapped.flush()\n \n \n-class DownloadProgressBar(WindowsMixin, DownloadProgressMixin, _BaseBar):\n+class DownloadProgressBar(WindowsMixin, InterruptibleMixin,\n+                          DownloadProgressMixin, _BaseBar):\n \n     file = sys.stdout\n     message = \"%(percent)d%%\"\n     suffix = \"%(downloaded)s %(download_speed)s %(pretty_eta)s\"\n \n \n-class DownloadProgressSpinner(WindowsMixin, DownloadProgressMixin,\n-                              WritelnMixin, Spinner):\n+class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin,\n+                              DownloadProgressMixin, WritelnMixin, Spinner):\n \n     file = sys.stdout\n     suffix = \"%(downloaded)s %(download_speed)s\"\n", "issue": "Interrupting a pip download with CTRL-C does not unhide the cursor\nVersion: pep 6.0.8\n\nPressing CTRL-C while pip is downloading a package (and displaying its progress bar) shows an \"Operation cancelled by user\" message and drops the user back to the shell prompt, but does not unhide the cursor that was hidden while displaying the progress bar.\n\nGlancing at the code, it looks like the `progress` library already offers a `SigIntMixin` helper for handling this, and that pip's progress bar isn't using it. Maybe including this mixin in the appropriate place(s) is all that's needed?\n\n", "before_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import division\n\nimport itertools\nimport sys\n\nfrom pip.compat import WINDOWS\nfrom pip.utils import format_size\nfrom pip.utils.logging import get_indentation\nfrom pip._vendor import six\nfrom pip._vendor.progress.bar import Bar, IncrementalBar\nfrom pip._vendor.progress.helpers import WritelnMixin\nfrom pip._vendor.progress.spinner import Spinner\n\ntry:\n    from pip._vendor import colorama\n# Lots of different errors can come from this, including SystemError and\n# ImportError.\nexcept Exception:\n    colorama = None\n\n\ndef _select_progress_class(preferred, fallback):\n    encoding = getattr(preferred.file, \"encoding\", None)\n\n    # If we don't know what encoding this file is in, then we'll just assume\n    # that it doesn't support unicode and use the ASCII bar.\n    if not encoding:\n        return fallback\n\n    # Collect all of the possible characters we want to use with the preferred\n    # bar.\n    characters = [\n        getattr(preferred, \"empty_fill\", six.text_type()),\n        getattr(preferred, \"fill\", six.text_type()),\n    ]\n    characters += list(getattr(preferred, \"phases\", []))\n\n    # Try to decode the characters we're using for the bar using the encoding\n    # of the given file, if this works then we'll assume that we can use the\n    # fancier bar and if not we'll fall back to the plaintext bar.\n    try:\n        six.text_type().join(characters).encode(encoding)\n    except UnicodeEncodeError:\n        return fallback\n    else:\n        return preferred\n\n\n_BaseBar = _select_progress_class(IncrementalBar, Bar)\n\n\nclass DownloadProgressMixin(object):\n\n    def __init__(self, *args, **kwargs):\n        super(DownloadProgressMixin, self).__init__(*args, **kwargs)\n        self.message = (\" \" * (get_indentation() + 2)) + self.message\n\n    @property\n    def downloaded(self):\n        return format_size(self.index)\n\n    @property\n    def download_speed(self):\n        # Avoid zero division errors...\n        if self.avg == 0.0:\n            return \"...\"\n        return format_size(1 / self.avg) + \"/s\"\n\n    @property\n    def pretty_eta(self):\n        if self.eta:\n            return \"eta %s\" % self.eta_td\n        return \"\"\n\n    def iter(self, it, n=1):\n        for x in it:\n            yield x\n            self.next(n)\n        self.finish()\n\n\nclass WindowsMixin(object):\n\n    def __init__(self, *args, **kwargs):\n        # The Windows terminal does not support the hide/show cursor ANSI codes\n        # even with colorama. So we'll ensure that hide_cursor is False on\n        # Windows.\n        # This call neds to go before the super() call, so that hide_cursor\n        # is set in time. The base progress bar class writes the \"hide cursor\"\n        # code to the terminal in its init, so if we don't set this soon\n        # enough, we get a \"hide\" with no corresponding \"show\"...\n        if WINDOWS and self.hide_cursor:\n            self.hide_cursor = False\n\n        super(WindowsMixin, self).__init__(*args, **kwargs)\n\n        # Check if we are running on Windows and we have the colorama module,\n        # if we do then wrap our file with it.\n        if WINDOWS and colorama:\n            self.file = colorama.AnsiToWin32(self.file)\n            # The progress code expects to be able to call self.file.isatty()\n            # but the colorama.AnsiToWin32() object doesn't have that, so we'll\n            # add it.\n            self.file.isatty = lambda: self.file.wrapped.isatty()\n            # The progress code expects to be able to call self.file.flush()\n            # but the colorama.AnsiToWin32() object doesn't have that, so we'll\n            # add it.\n            self.file.flush = lambda: self.file.wrapped.flush()\n\n\nclass DownloadProgressBar(WindowsMixin, DownloadProgressMixin, _BaseBar):\n\n    file = sys.stdout\n    message = \"%(percent)d%%\"\n    suffix = \"%(downloaded)s %(download_speed)s %(pretty_eta)s\"\n\n\nclass DownloadProgressSpinner(WindowsMixin, DownloadProgressMixin,\n                              WritelnMixin, Spinner):\n\n    file = sys.stdout\n    suffix = \"%(downloaded)s %(download_speed)s\"\n\n    def next_phase(self):\n        if not hasattr(self, \"_phaser\"):\n            self._phaser = itertools.cycle(self.phases)\n        return next(self._phaser)\n\n    def update(self):\n        message = self.message % self\n        phase = self.next_phase()\n        suffix = self.suffix % self\n        line = ''.join([\n            message,\n            \" \" if message else \"\",\n            phase,\n            \" \" if suffix else \"\",\n            suffix,\n        ])\n\n        self.writeln(line)\n", "path": "pip/utils/ui.py"}], "after_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import division\n\nimport itertools\nimport sys\nfrom signal import signal, SIGINT, default_int_handler\n\nfrom pip.compat import WINDOWS\nfrom pip.utils import format_size\nfrom pip.utils.logging import get_indentation\nfrom pip._vendor import six\nfrom pip._vendor.progress.bar import Bar, IncrementalBar\nfrom pip._vendor.progress.helpers import WritelnMixin\nfrom pip._vendor.progress.spinner import Spinner\n\ntry:\n    from pip._vendor import colorama\n# Lots of different errors can come from this, including SystemError and\n# ImportError.\nexcept Exception:\n    colorama = None\n\n\ndef _select_progress_class(preferred, fallback):\n    encoding = getattr(preferred.file, \"encoding\", None)\n\n    # If we don't know what encoding this file is in, then we'll just assume\n    # that it doesn't support unicode and use the ASCII bar.\n    if not encoding:\n        return fallback\n\n    # Collect all of the possible characters we want to use with the preferred\n    # bar.\n    characters = [\n        getattr(preferred, \"empty_fill\", six.text_type()),\n        getattr(preferred, \"fill\", six.text_type()),\n    ]\n    characters += list(getattr(preferred, \"phases\", []))\n\n    # Try to decode the characters we're using for the bar using the encoding\n    # of the given file, if this works then we'll assume that we can use the\n    # fancier bar and if not we'll fall back to the plaintext bar.\n    try:\n        six.text_type().join(characters).encode(encoding)\n    except UnicodeEncodeError:\n        return fallback\n    else:\n        return preferred\n\n\n_BaseBar = _select_progress_class(IncrementalBar, Bar)\n\n\nclass InterruptibleMixin(object):\n    \"\"\"\n    Helper to ensure that self.finish() gets called on keyboard interrupt.\n\n    This allows downloads to be interrupted without leaving temporary state\n    (like hidden cursors) behind.\n\n    This class is similar to the progress library's existing SigIntMixin\n    helper, but as of version 1.2, that helper has the following problems:\n\n    1. It calls sys.exit().\n    2. It discards the existing SIGINT handler completely.\n    3. It leaves its own handler in place even after an uninterrupted finish,\n       which will have unexpected delayed effects if the user triggers an\n       unrelated keyboard interrupt some time after a progress-displaying\n       download has already completed, for example.\n    \"\"\"\n\n    def __init__(self, *args, **kwargs):\n        \"\"\"\n        Save the original SIGINT handler for later.\n        \"\"\"\n        super(InterruptibleMixin, self).__init__(*args, **kwargs)\n\n        self.original_handler = signal(SIGINT, self.handle_sigint)\n\n        # If signal() returns None, the previous handler was not installed from\n        # Python, and we cannot restore it. This probably should not happen,\n        # but if it does, we must restore something sensible instead, at least.\n        # The least bad option should be Python's default SIGINT handler, which\n        # just raises KeyboardInterrupt.\n        if self.original_handler is None:\n            self.original_handler = default_int_handler\n\n    def finish(self):\n        \"\"\"\n        Restore the original SIGINT handler after finishing.\n\n        This should happen regardless of whether the progress display finishes\n        normally, or gets interrupted.\n        \"\"\"\n        super(InterruptibleMixin, self).finish()\n        signal(SIGINT, self.original_handler)\n\n    def handle_sigint(self, signum, frame):\n        \"\"\"\n        Call self.finish() before delegating to the original SIGINT handler.\n\n        This handler should only be in place while the progress display is\n        active.\n        \"\"\"\n        self.finish()\n        self.original_handler(signum, frame)\n\n\nclass DownloadProgressMixin(object):\n\n    def __init__(self, *args, **kwargs):\n        super(DownloadProgressMixin, self).__init__(*args, **kwargs)\n        self.message = (\" \" * (get_indentation() + 2)) + self.message\n\n    @property\n    def downloaded(self):\n        return format_size(self.index)\n\n    @property\n    def download_speed(self):\n        # Avoid zero division errors...\n        if self.avg == 0.0:\n            return \"...\"\n        return format_size(1 / self.avg) + \"/s\"\n\n    @property\n    def pretty_eta(self):\n        if self.eta:\n            return \"eta %s\" % self.eta_td\n        return \"\"\n\n    def iter(self, it, n=1):\n        for x in it:\n            yield x\n            self.next(n)\n        self.finish()\n\n\nclass WindowsMixin(object):\n\n    def __init__(self, *args, **kwargs):\n        # The Windows terminal does not support the hide/show cursor ANSI codes\n        # even with colorama. So we'll ensure that hide_cursor is False on\n        # Windows.\n        # This call neds to go before the super() call, so that hide_cursor\n        # is set in time. The base progress bar class writes the \"hide cursor\"\n        # code to the terminal in its init, so if we don't set this soon\n        # enough, we get a \"hide\" with no corresponding \"show\"...\n        if WINDOWS and self.hide_cursor:\n            self.hide_cursor = False\n\n        super(WindowsMixin, self).__init__(*args, **kwargs)\n\n        # Check if we are running on Windows and we have the colorama module,\n        # if we do then wrap our file with it.\n        if WINDOWS and colorama:\n            self.file = colorama.AnsiToWin32(self.file)\n            # The progress code expects to be able to call self.file.isatty()\n            # but the colorama.AnsiToWin32() object doesn't have that, so we'll\n            # add it.\n            self.file.isatty = lambda: self.file.wrapped.isatty()\n            # The progress code expects to be able to call self.file.flush()\n            # but the colorama.AnsiToWin32() object doesn't have that, so we'll\n            # add it.\n            self.file.flush = lambda: self.file.wrapped.flush()\n\n\nclass DownloadProgressBar(WindowsMixin, InterruptibleMixin,\n                          DownloadProgressMixin, _BaseBar):\n\n    file = sys.stdout\n    message = \"%(percent)d%%\"\n    suffix = \"%(downloaded)s %(download_speed)s %(pretty_eta)s\"\n\n\nclass DownloadProgressSpinner(WindowsMixin, InterruptibleMixin,\n                              DownloadProgressMixin, WritelnMixin, Spinner):\n\n    file = sys.stdout\n    suffix = \"%(downloaded)s %(download_speed)s\"\n\n    def next_phase(self):\n        if not hasattr(self, \"_phaser\"):\n            self._phaser = itertools.cycle(self.phases)\n        return next(self._phaser)\n\n    def update(self):\n        message = self.message % self\n        phase = self.next_phase()\n        suffix = self.suffix % self\n        line = ''.join([\n            message,\n            \" \" if message else \"\",\n            phase,\n            \" \" if suffix else \"\",\n            suffix,\n        ])\n\n        self.writeln(line)\n", "path": "pip/utils/ui.py"}]}