Repository: pallets/click
Pull request #1299 (2019-05-06 17:39:19)

Update regular expression for strip_ansi to handle edge cases
#1216
Given this issue fixes an edge case in a regular expression, I wasn't sure how to add docs or testing.

sekenned: Copied over from https://github.com/pallets/click/pull/1297 on Master. 

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index ac1c51b..217f705 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -9,6 +9,8 @@ Unreleased
 -   Fix link in ``unicode_literals`` error message. :pr:`1151`
 -   Add support for colored output on UNIX Jupyter notebooks.
     :issue:`1185`
+-   Operations that strip ANSI controls will strip the cursor hide/show
+    sequences. :issue:`1216`
 -   Remove unused compat shim for ``bytes``. :pr:`1195`
 -   Expand testing around termui, especially getchar on Windows.
     :issue:`1116`
diff --git a/click/_compat.py b/click/_compat.py
index cf6e1c9..4709b88 100644
--- a/click/_compat.py
+++ b/click/_compat.py
@@ -16,7 +16,7 @@ WIN = sys.platform.startswith('win') and not APP_ENGINE and not MSYS2
 DEFAULT_COLUMNS = 80
 
 
-_ansi_re = re.compile(r'\033\[((?:\d|;)*)([a-zA-Z])')
+_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]")
 
 
 def get_filesystem_encoding():


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 4fd7cbb..2741261 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -50,32 +50,45 @@ def test_echo_custom_file():
     assert f.getvalue() == u'hello\n'
 
 
-def test_styling():
-    examples = [
-        ('x', dict(fg='black'), '\x1b[30mx\x1b[0m'),
-        ('x', dict(fg='red'), '\x1b[31mx\x1b[0m'),
-        ('x', dict(fg='green'), '\x1b[32mx\x1b[0m'),
-        ('x', dict(fg='yellow'), '\x1b[33mx\x1b[0m'),
-        ('x', dict(fg='blue'), '\x1b[34mx\x1b[0m'),
-        ('x', dict(fg='magenta'), '\x1b[35mx\x1b[0m'),
-        ('x', dict(fg='cyan'), '\x1b[36mx\x1b[0m'),
-        ('x', dict(fg='white'), '\x1b[37mx\x1b[0m'),
-        ('x', dict(bg='black'), '\x1b[40mx\x1b[0m'),
-        ('x', dict(bg='red'), '\x1b[41mx\x1b[0m'),
-        ('x', dict(bg='green'), '\x1b[42mx\x1b[0m'),
-        ('x', dict(bg='yellow'), '\x1b[43mx\x1b[0m'),
-        ('x', dict(bg='blue'), '\x1b[44mx\x1b[0m'),
-        ('x', dict(bg='magenta'), '\x1b[45mx\x1b[0m'),
-        ('x', dict(bg='cyan'), '\x1b[46mx\x1b[0m'),
-        ('x', dict(bg='white'), '\x1b[47mx\x1b[0m'),
-        ('foo bar', dict(blink=True), '\x1b[5mfoo bar\x1b[0m'),
-        ('foo bar', dict(underline=True), '\x1b[4mfoo bar\x1b[0m'),
-        ('foo bar', dict(bold=True), '\x1b[1mfoo bar\x1b[0m'),
-        ('foo bar', dict(dim=True), '\x1b[2mfoo bar\x1b[0m'),
+@pytest.mark.parametrize(
+    ("styles", "ref"),
+    [
+        ({"fg": "black"}, "\x1b[30mx y\x1b[0m"),
+        ({"fg": "red"}, "\x1b[31mx y\x1b[0m"),
+        ({"fg": "green"}, "\x1b[32mx y\x1b[0m"),
+        ({"fg": "yellow"}, "\x1b[33mx y\x1b[0m"),
+        ({"fg": "blue"}, "\x1b[34mx y\x1b[0m"),
+        ({"fg": "magenta"}, "\x1b[35mx y\x1b[0m"),
+        ({"fg": "cyan"}, "\x1b[36mx y\x1b[0m"),
+        ({"fg": "white"}, "\x1b[37mx y\x1b[0m"),
+        ({"bg": "black"}, "\x1b[40mx y\x1b[0m"),
+        ({"bg": "red"}, "\x1b[41mx y\x1b[0m"),
+        ({"bg": "green"}, "\x1b[42mx y\x1b[0m"),
+        ({"bg": "yellow"}, "\x1b[43mx y\x1b[0m"),
+        ({"bg": "blue"}, "\x1b[44mx y\x1b[0m"),
+        ({"bg": "magenta"}, "\x1b[45mx y\x1b[0m"),
+        ({"bg": "cyan"}, "\x1b[46mx y\x1b[0m"),
+        ({"bg": "white"}, "\x1b[47mx y\x1b[0m"),
+        ({"blink": True}, "\x1b[5mx y\x1b[0m"),
+        ({"underline": True}, "\x1b[4mx y\x1b[0m"),
+        ({"bold": True}, "\x1b[1mx y\x1b[0m"),
+        ({"dim": True}, "\x1b[2mx y\x1b[0m"),
+    ],
+)
+def test_styling(styles, ref):
+    assert click.style("x y", **styles) == ref
+    assert click.unstyle(ref) == "x y"
+
+
+@pytest.mark.parametrize(
+    ("text", "expect"),
+    [
+        ("\x1b[?25lx y\x1b[?25h", "x y"),
     ]
-    for text, styles, ref in examples:
-        assert click.style(text, **styles) == ref
-        assert click.unstyle(ref) == text
+)
+def test_unstyle_other_ansi(text, expect):
+    assert click.unstyle(text) == expect
+
 
 
 def test_filename_formatting():