Repository: tqdm/tqdm (Python)

Issue:
Bar length not calculated correctly when ANSI color codes are used
I am using ANSI color codes in the progress bar description string to emphasize success, failure etc.

Unfortunately, the bar length calculation doesn't work as expected with these color codes.

In the example below, the bar stops short of the terminal width by 13 characters, which is the combined length of the ANSI control sequences.

```python
from tqdm import tqdm

ansi = {'BOLD': '\033[1m',
        'RED': '\033[91m',
        'END': '\033[0m'}

text = '{BOLD}{RED}Failed{END}: ...'.format(**ansi)

pbar = tqdm(total=100, desc=text)
pbar.close()
```

Output (example):
```
Failed: ...:   0%|                                  | 0/100 [00:00<?, ?it/s]
```
Expected:
```
Failed: ...:   0%|                                               | 0/100 [00:00<?, ?it/s]
```

This should be as easy to fix as stripping the ANSI codes away as in the code below when doing the length calculations here: https://github.com/tqdm/tqdm/blob/master/tqdm/_tqdm.py#L365

```python
import re
re_ansi = re.compile(r"\x1b\[[;\d]*[A-Za-z]")
stripped = re_ansi.sub('', text)
print(len(text))  # 24
print(len(stripped))  # 11
```

I'd be happy to submit a PR, but I don't know if this is a desired feature. Performance should not be an issue, as the stripping executes in < 1 µs.

---

```
 >>> print(tqdm.__version__)
4.24.0
>>> print(sys.version)
3.7.0 (default, Jun 28 2018, 07:39:16) 
[Clang 4.0.1 (tags/RELEASE_401/final)]
>>> print(sys.platform)
darwin
```


Hints:
Thanks. Maybe we should strip escape sequences... related to #450

Patch:
diff --git a/tqdm/_tqdm.py b/tqdm/_tqdm.py
--- a/tqdm/_tqdm.py
+++ b/tqdm/_tqdm.py
@@ -13,7 +13,7 @@
 # compatibility functions and utilities
 from ._utils import _supports_unicode, _environ_cols_wrapper, _range, _unich, \
     _term_move_up, _unicode, WeakSet, _basestring, _OrderedDict, \
-    Comparable
+    Comparable, RE_ANSI
 from ._monitor import TMonitor
 # native libraries
 import sys
@@ -360,10 +360,11 @@ def format_meter(n, total, elapsed, ncols=None, prefix='', ascii=False,
                     # Else no progress bar, we can just format and return
                     return bar_format.format(**bar_args)
 
-            # Formatting progress bar
-            # space available for bar's display
-            N_BARS = max(1, ncols - len(l_bar) - len(r_bar)) if ncols \
-                else 10
+            # Formatting progress bar space available for bar's display
+            if ncols:
+                N_BARS = max(1, ncols - len(RE_ANSI.sub('', l_bar + r_bar)))
+            else:
+                N_BARS = 10
 
             # format bar depending on availability of unicode/ascii chars
             if ascii:
diff --git a/tqdm/_utils.py b/tqdm/_utils.py
--- a/tqdm/_utils.py
+++ b/tqdm/_utils.py
@@ -1,11 +1,13 @@
 import os
 import subprocess
 from platform import system as _curos
+import re
 CUR_OS = _curos()
 IS_WIN = CUR_OS in ['Windows', 'cli']
 IS_NIX = (not IS_WIN) and any(
     CUR_OS.startswith(i) for i in
     ['CYGWIN', 'MSYS', 'Linux', 'Darwin', 'SunOS', 'FreeBSD', 'NetBSD'])
+RE_ANSI = re.compile(r"\x1b\[[;\d]*[A-Za-z]")
 
 
 # Py2/3 compat. Empty conditional to avoid coverage


Test patch:
diff --git a/tqdm/tests/tests_tqdm.py b/tqdm/tests/tests_tqdm.py
--- a/tqdm/tests/tests_tqdm.py
+++ b/tqdm/tests/tests_tqdm.py
@@ -255,6 +255,19 @@ def test_format_meter():
         unich(0x258f) + "|test"
 
 
+def test_ansi_escape_codes():
+    """Test stripping of ANSI escape codes"""
+    format_meter = tqdm.format_meter
+    ansi = {'BOLD': '\033[1m',
+            'RED': '\033[91m',
+            'END': '\033[0m'}
+    desc = '{BOLD}{RED}Colored{END} description'.format(**ansi)
+    ncols = 123
+    ansi_len = sum([len(code) for code in ansi.values()])
+    meter = format_meter(0, 100, 0, ncols=ncols, prefix=desc)
+    assert len(meter) == ncols + ansi_len
+
+
 def test_si_format():
     """Test SI unit prefixes"""
     format_meter = tqdm.format_meter