Repository: pypa/pip

Issue:
pip 6.0.3 weird symbols shown with download progress bar
with pip 6.0.3 on Windows with cmd.exe

```
py -mpip install --upgrade setuptools

Collecting setuptools from https://pypi.python.org/packages/3.4/s/setuptools/set
uptools-8.3-py2.py3-none-any.whl#md5=a6c2914e2ae62227a5dfb6e908475b02
  Downloading setuptools-8.3-py2.py3-none-any.whl (552kB)
←[K    100% |################################| 552kB 835kB/s ta 0:00:01
←[?25hInstalling collected packages: setuptools
  Found existing installation: setuptools 7.0
    Uninstalling setuptools-7.0:
      Successfully uninstalled setuptools-7.0

Successfully installed setuptools-8.3
```

There's weird stuff with the progress bar, possibly control characers that cmd.exe terminal can't handle



Discussion:
The progress bar currently uses 3 different ANSI escape codes:
- `\x1b[?25l` to hide the cursor while the progress bar is being rendered.
- `\x1b[?25h` to show the cursor once the progress bar is done being rendered.
- `\x1b[K` following a `\r` (carriage return) to clear the entire line.

The cursor ones are easy to turn off, it'll just show the cursor at the end of the line which is slightly ugly but if Windows doesn't have a way to do that then there's not much we can do about it. The third one I'm not sure what the right answer is. If we don't clear the line then if the line gets shorter in an update it will probably cause a visual glitch where the last characters in the line are from a previous update.

I wonder if there is anything like colorama which adds support for ANSI escape codes in the Windows terminal as well?

/cc @pfmoore 

Hmm, I wonder if colorama actually does this and it's just not working for us because we didn't replace `sys.stdout` and `sys.stderr`, we only replaced the thing that the logger writes to?


Resolving patch:
diff --git a/pip/utils/ui.py b/pip/utils/ui.py
--- a/pip/utils/ui.py
+++ b/pip/utils/ui.py
@@ -4,12 +4,20 @@
 import itertools
 import sys
 
+from pip.compat import WINDOWS
 from pip.utils import format_size
 from pip.utils.logging import get_indentation
 from pip._vendor.progress.bar import Bar
 from pip._vendor.progress.helpers import WritelnMixin
 from pip._vendor.progress.spinner import Spinner
 
+try:
+    from pip._vendor import colorama
+# Lots of different errors can come from this, including SystemError and
+# ImportError.
+except Exception:
+    colorama = None
+
 
 class DownloadProgressMixin(object):
 
@@ -41,14 +49,40 @@ def iter(self, it, n=1):
         self.finish()
 
 
-class DownloadProgressBar(DownloadProgressMixin, Bar):
+class WindowsMixin(object):
+
+    def __init__(self, *args, **kwargs):
+        super(WindowsMixin, self).__init__(*args, **kwargs)
+
+        # Check if we are running on Windows and we have the colorama module,
+        # if we do then wrap our file with it.
+        if WINDOWS and colorama:
+            self.file = colorama.AnsiToWin32(self.file)
+            # The progress code expects to be able to call self.file.isatty()
+            # but the colorama.AnsiToWin32() object doesn't have that, so we'll
+            # add it.
+            self.file.isatty = lambda: self.file.wrapped.isatty()
+            # The progress code expects to be able to call self.file.flush()
+            # but the colorama.AnsiToWin32() object doesn't have that, so we'll
+            # add it.
+            self.file.flush = lambda: self.file.wrapped.flush()
+
+        # The Windows terminal does not support the hide/show cursor ANSI codes
+        # even with colorama. So we'll ensure that hide_cursor is False on
+        # Windows.
+        if WINDOWS and self.hide_cursor:
+            self.hide_cursor = False
+
+
+class DownloadProgressBar(WindowsMixin, DownloadProgressMixin, Bar):
 
     file = sys.stdout
     message = "%(percent)d%%"
     suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s"
 
 
-class DownloadProgressSpinner(DownloadProgressMixin, WritelnMixin, Spinner):
+class DownloadProgressSpinner(WindowsMixin, DownloadProgressMixin,
+                              WritelnMixin, Spinner):
 
     file = sys.stdout
     suffix = "%(downloaded)s %(download_speed)s"


Test patch: