summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrett Smith <brettcsmith@brettcsmith.org>2021-08-21 09:39:07 -0400
committerGitHub <noreply@github.com>2021-08-21 14:39:07 +0100
commit8f5ca1165749efd71d4d3afc19285a9257fe1279 (patch)
treec4460a2ed2b848f0184feb83b964b4a963b03ad1
parente1f1826d0533c3b45cbe72c9210ae0cca3ad2181 (diff)
downloadtox-git-8f5ca1165749efd71d4d3afc19285a9257fe1279.tar.gz
ASCII output for --parallel report lines (#2164)
* Add SpinnerMessage helper class This class provides a single interface for spinner strings where we ideally want to report Unicode but want to fall back to ASCII when the terminal doesn't support it. * Add ASCII messages for OK/FAIL/SKIP lines Fixes #1421.
-rw-r--r--CONTRIBUTORS1
-rw-r--r--docs/changelog/1421.bugfix.rst1
-rw-r--r--src/tox/util/spinner.py40
-rw-r--r--tests/unit/util/test_spinner.py25
4 files changed, 46 insertions, 21 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 14d9bbad..d91bef34 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -20,6 +20,7 @@ Bastien Vallet
Benoit Pierre
Bernat Gabor
Brett Langdon
+Brett Smith
Bruno Oliveira
Carl Meyer
Charles Brunet
diff --git a/docs/changelog/1421.bugfix.rst b/docs/changelog/1421.bugfix.rst
new file mode 100644
index 00000000..8e0a5ff5
--- /dev/null
+++ b/docs/changelog/1421.bugfix.rst
@@ -0,0 +1 @@
+``--parallel`` reports now show ASCII OK/FAIL/SKIP lines when full Unicode output is not available - by :user:`brettcs`
diff --git a/src/tox/util/spinner.py b/src/tox/util/spinner.py
index 523a0c49..ee225895 100644
--- a/src/tox/util/spinner.py
+++ b/src/tox/util/spinner.py
@@ -5,7 +5,7 @@ from __future__ import absolute_import, unicode_literals
import os
import sys
import threading
-from collections import OrderedDict
+from collections import OrderedDict, namedtuple
from datetime import datetime
import py
@@ -19,34 +19,32 @@ if os.name == "nt":
_fields_ = [("size", ctypes.c_int), ("visible", ctypes.c_byte)]
-def _file_support_encoding(chars, file):
- encoding = getattr(file, "encoding", None)
- if encoding is not None:
- for char in chars:
- try:
- char.encode(encoding)
- except UnicodeEncodeError:
- break
+_BaseMessage = namedtuple("_BaseMessage", ["unicode_msg", "ascii_msg"])
+
+
+class SpinnerMessage(_BaseMessage):
+ def for_file(self, file):
+ try:
+ self.unicode_msg.encode(file.encoding)
+ except (AttributeError, TypeError, UnicodeEncodeError):
+ return self.ascii_msg
else:
- return True
- return False
+ return self.unicode_msg
class Spinner(object):
CLEAR_LINE = "\033[K"
max_width = 120
- UNICODE_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
- ASCII_FRAMES = ["|", "-", "+", "x", "*"]
+ FRAMES = SpinnerMessage("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏", "|-+x*")
+ OK_FLAG = SpinnerMessage("✔ OK", "[ OK ]")
+ FAIL_FLAG = SpinnerMessage("✖ FAIL", "[FAIL]")
+ SKIP_FLAG = SpinnerMessage("⚠ SKIP", "[SKIP]")
def __init__(self, enabled=True, refresh_rate=0.1):
self.refresh_rate = refresh_rate
self.enabled = enabled
self._file = sys.stdout
- self.frames = (
- self.UNICODE_FRAMES
- if _file_support_encoding(self.UNICODE_FRAMES, sys.stdout)
- else self.ASCII_FRAMES
- )
+ self.frames = self.FRAMES.for_file(self._file)
self.stream = py.io.TerminalWriter(file=self._file)
self._envs = OrderedDict()
self._frame_index = 0
@@ -105,13 +103,13 @@ class Spinner(object):
self._envs[name] = datetime.now()
def succeed(self, key):
- self.finalize(key, "✔ OK", green=True)
+ self.finalize(key, self.OK_FLAG.for_file(self._file), green=True)
def fail(self, key):
- self.finalize(key, "✖ FAIL", red=True)
+ self.finalize(key, self.FAIL_FLAG.for_file(self._file), red=True)
def skip(self, key):
- self.finalize(key, "⚠ SKIP", white=True)
+ self.finalize(key, self.SKIP_FLAG.for_file(self._file), white=True)
def finalize(self, key, status, **kwargs):
start_at = self._envs[key]
diff --git a/tests/unit/util/test_spinner.py b/tests/unit/util/test_spinner.py
index 7c64ac45..2511d584 100644
--- a/tests/unit/util/test_spinner.py
+++ b/tests/unit/util/test_spinner.py
@@ -113,6 +113,31 @@ def test_spinner_stdout_not_unicode(mocker, capfd):
assert all(f in written for f in spin.frames)
+@freeze_time("2012-01-14")
+def test_spinner_report_not_unicode(mocker, capfd):
+ stdout = mocker.patch("tox.util.spinner.sys.stdout")
+ stdout.encoding = "ascii"
+ # Disable color to simplify parsing output strings
+ stdout.isatty = lambda: False
+ with spinner.Spinner(refresh_rate=100) as spin:
+ spin.stream.write(os.linesep)
+ spin.add("ok!")
+ spin.add("fail!")
+ spin.add("skip!")
+ spin.succeed("ok!")
+ spin.fail("fail!")
+ spin.skip("skip!")
+ lines = "".join(args[0] for args, _ in stdout.write.call_args_list).split(os.linesep)
+ del lines[0]
+ expected = [
+ "\r{}[ OK ] ok! in 0.0 seconds".format(spin.CLEAR_LINE),
+ "\r{}[FAIL] fail! in 0.0 seconds".format(spin.CLEAR_LINE),
+ "\r{}[SKIP] skip! in 0.0 seconds".format(spin.CLEAR_LINE),
+ "\r{}".format(spin.CLEAR_LINE),
+ ]
+ assert lines == expected
+
+
@pytest.mark.parametrize(
"seconds, expected",
[