Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assume a TTY for interactive commands #923

Merged
merged 1 commit into from
Mar 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion esrally/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import thespian.system.messages.status

from esrally import exceptions, log
from esrally.utils import net
from esrally.utils import console, net


class BenchmarkFailure:
Expand Down Expand Up @@ -101,6 +101,7 @@ def __init__(self, *args, **kw):
self.status = None
log.post_configure_actor_logging()
self.logger = logging.getLogger(__name__)
console.set_assume_tty(assume_tty=False)

# The method name is required by the actor framework
# noinspection PyPep8Naming
Expand Down
2 changes: 1 addition & 1 deletion esrally/rallyd.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def main():
check_python_version()
log.install_default_log_config()
log.configure_logging()
console.init()
console.init(assume_tty=False)

parser = argparse.ArgumentParser(prog=PROGRAM_NAME,
description=BANNER + "\n\n Rally daemon to support remote benchmarks",
Expand Down
30 changes: 24 additions & 6 deletions esrally/utils/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
PLAIN = False
QUIET = False
RALLY_RUNNING_IN_DOCKER = False
ASSUME_TTY = True


class PlainFormat:
Expand Down Expand Up @@ -87,10 +88,17 @@ def underline_for(cls, message, underline_symbol="*"):
format = PlainFormat


def init(quiet=False):
global QUIET, RALLY_RUNNING_IN_DOCKER, PLAIN, format
def init(quiet=False, assume_tty=True):
"""
Initialize console out.

:param quiet: Flag indicating whether Rally should not print anything except when forced explicitly. Default: False.
:param assume_tty: Flag indicating whether to assume a tty is attached without checking. Default: True.
"""
global QUIET, ASSUME_TTY, RALLY_RUNNING_IN_DOCKER, PLAIN, format

QUIET = quiet
ASSUME_TTY = assume_tty
RALLY_RUNNING_IN_DOCKER = os.environ.get("RALLY_RUNNING_IN_DOCKER", "").upper() == "TRUE"
if os.environ.get("TERM") == "dumb" or sys.platform == "win32":
PLAIN = True
Expand All @@ -112,6 +120,17 @@ def init(quiet=False):
pass


def set_assume_tty(assume_tty):
"""
Change whether Rally should assume a tty. If ``True`` is provided, output will be printed. If ``False`` is provided,
Rally will explicitly check whether it is attached to a tty before attempting to print anything.

:param assume_tty: Flag indicating whether to assume a tty is attached without checking.
"""
global ASSUME_TTY
ASSUME_TTY = assume_tty


def info(msg, end="\n", flush=False, force=False, logger=None, overline=None, underline=None):
println(msg, console_prefix="[INFO]", end=end, flush=flush, force=force, overline=overline, underline=underline,
logger=logger.info if logger else None)
Expand All @@ -128,8 +147,7 @@ def error(msg, end="\n", flush=False, force=False, logger=None, overline=None, u


def println(msg, console_prefix=None, end="\n", flush=False, force=False, logger=None, overline=None, underline=None):
# TODO: Checking for sys.stdout.isatty() prevents shell redirections and pipes (useful for list commands). Can we remove this check?
allow_print = force or (not QUIET and (RALLY_RUNNING_IN_DOCKER or sys.stdout.isatty()))
allow_print = force or (not QUIET and (RALLY_RUNNING_IN_DOCKER or ASSUME_TTY or sys.stdout.isatty()))
if allow_print:
complete_msg = "%s %s" % (console_prefix, msg) if console_prefix else msg
if overline:
Expand Down Expand Up @@ -169,7 +187,7 @@ def print(self, message, progress):
:param message: A message to display (will be left-aligned)
:param progress: A progress indication (will be right-aligned)
"""
if QUIET or (not RALLY_RUNNING_IN_DOCKER and not sys.stdout.isatty()):
if QUIET or (not RALLY_RUNNING_IN_DOCKER and not ASSUME_TTY and not sys.stdout.isatty()):
return
w = self._width
if self._first_print:
Expand All @@ -192,7 +210,7 @@ def _truncate(self, text, max_length, omission="..."):
return "%s%s" % (text[0:max_length - len(omission) - 5], omission)

def finish(self):
if QUIET or (not RALLY_RUNNING_IN_DOCKER and not sys.stdout.isatty()):
if QUIET or (not RALLY_RUNNING_IN_DOCKER and not ASSUME_TTY and not sys.stdout.isatty()):
return
# print a final statement in order to end the progress line
self._printer("")
73 changes: 62 additions & 11 deletions tests/utils/console_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,19 @@
class ConsoleFunctionTests(TestCase):
oldconsole_quiet = None
oldconsole_rally_running_in_docker = None
oldconsole_rally_assume_tty = None

@classmethod
def setUpClass(cls):
cls.oldconsole_quiet = console.QUIET
cls.oldconsole_rally_running_in_docker = console.RALLY_RUNNING_IN_DOCKER
cls.oldconsole_rally_assume_tty = console.ASSUME_TTY

@classmethod
def tearDownClass(cls):
console.QUIET = cls.oldconsole_quiet
console.RALLY_RUNNING_IN_DOCKER = cls.oldconsole_rally_running_in_docker
console.ASSUME_TTY = cls.oldconsole_rally_assume_tty

@mock.patch.dict(os.environ, {"RALLY_RUNNING_IN_DOCKER": random.choice(["false", "False", "FALSE", ""])})
def test_global_rally_running_in_docker_is_false(self):
Expand All @@ -57,8 +60,7 @@ def test_global_rally_running_in_docker_is_true(self):
@mock.patch("sys.stdout.isatty")
@mock.patch("builtins.print")
def test_println_randomized_dockertrue_or_istty_and_isnotquiet(self, patched_print, patched_isatty):
console.init()
console.QUIET = False
console.init(quiet=False, assume_tty=False)
random_boolean = random.choice([True, False])
patched_isatty.return_value = random_boolean
console.RALLY_RUNNING_IN_DOCKER = not random_boolean
Expand All @@ -70,10 +72,20 @@ def test_println_randomized_dockertrue_or_istty_and_isnotquiet(self, patched_pri

@mock.patch("sys.stdout.isatty")
@mock.patch("builtins.print")
def test_println_isquiet_and_randomized_docker_or_istty(self, patched_print, patched_isatty):
console.init()
console.QUIET = True
def test_println_randomized_assume_tty_or_istty_and_isnotquiet(self, patched_print, patched_isatty):
random_boolean = random.choice([True, False])
console.init(quiet=False, assume_tty=not random_boolean)
patched_isatty.return_value = random_boolean
console.println(msg="Unittest message")
patched_print.assert_called_once_with(
"Unittest message", end="\n", flush=False
)

@mock.patch("sys.stdout.isatty")
@mock.patch("builtins.print")
def test_println_isquiet_and_randomized_docker_assume_tty_or_istty(self, patched_print, patched_isatty):
random_boolean = random.choice([True, False])
console.init(quiet=True, assume_tty=not random_boolean)
patched_isatty.return_value = random_boolean
console.RALLY_RUNNING_IN_DOCKER = not random_boolean
console.println(msg="Unittest message")
Expand All @@ -95,22 +107,25 @@ def test_println_force_prints_even_when_quiet(self, patched_print, patched_isatt
class TestCmdLineProgressReporter:
oldconsole_quiet = None
oldconsole_rally_running_in_docker = None
oldconsole_rally_assume_tty = None

@classmethod
def setup_class(cls):
cls.oldconsole_quiet = console.QUIET
cls.oldconsole_rally_running_in_docker = console.RALLY_RUNNING_IN_DOCKER
cls.oldconsole_rally_assume_tty = console.ASSUME_TTY

@classmethod
def teardown_class(cls):
console.QUIET = cls.oldconsole_quiet
console.RALLY_RUNNING_IN_DOCKER = cls.oldconsole_rally_running_in_docker
console.ASSUME_TTY = cls.oldconsole_rally_assume_tty

@mock.patch("sys.stdout.flush")
@mock.patch("sys.stdout.isatty")
@pytest.mark.parametrize("seed", range(20))
def test_print_when_isquiet_and_any_docker_or_istty(self, patched_isatty, patched_flush, seed):
console.QUIET = True
console.init(quiet=True, assume_tty=False)
random.seed(seed)
patched_isatty.return_value = random.choice([True, False])
console.RALLY_RUNNING_IN_DOCKER = random.choice([True, False])
Expand All @@ -127,7 +142,7 @@ def test_print_when_isquiet_and_any_docker_or_istty(self, patched_isatty, patche
@mock.patch("sys.stdout.flush")
@mock.patch("sys.stdout.isatty")
def test_prints_when_isnotquiet_and_nodocker_and_isnotty(self, patched_isatty, patched_flush):
console.QUIET = False
console.init(quiet=False, assume_tty=False)
patched_isatty.return_value = False
console.RALLY_RUNNING_IN_DOCKER = False

Expand All @@ -143,7 +158,7 @@ def test_prints_when_isnotquiet_and_nodocker_and_isnotty(self, patched_isatty, p
@mock.patch("sys.stdout.isatty")
@pytest.mark.parametrize("seed", range(20))
def test_prints_when_isnotquiet_and_randomized_docker_or_istty(self, patched_isatty, patched_flush, seed):
console.QUIET = False
console.init(quiet=False, assume_tty=False)
random.seed(seed)
random_boolean = random.choice([True, False])
patched_isatty.return_value = random_boolean
Expand All @@ -160,11 +175,32 @@ def test_prints_when_isnotquiet_and_randomized_docker_or_istty(self, patched_isa
])
patched_flush.assert_called_once_with()

@mock.patch("sys.stdout.flush")
@mock.patch("sys.stdout.isatty")
@pytest.mark.parametrize("seed", range(20))
def test_prints_when_isnotquiet_and_randomized_assume_tty_or_istty(self, patched_isatty, patched_flush, seed):
random.seed(seed)
random_boolean = random.choice([True, False])
console.init(quiet=False, assume_tty=random_boolean)
console.RALLY_RUNNING_IN_DOCKER = False
patched_isatty.return_value = not random_boolean

message = "Unit test message"
width = random.randint(20, 140)
mock_printer = mock.Mock()
progress_reporter = console.CmdLineProgressReporter(width=width, printer=mock_printer)
progress_reporter.print(message=message, progress=".")
mock_printer.assert_has_calls([
mock.call(" " * width, end=""),
mock.call("\x1b[{}D{}{}.".format(width, message, " "*(width-len(message)-1)), end="")
])
patched_flush.assert_called_once_with()

@mock.patch("sys.stdout.flush")
@mock.patch("sys.stdout.isatty")
def test_noprint_when_isnotquiet_and_nodocker_and_noistty(self, patched_isatty, patched_flush):
console.init(quiet=False, assume_tty=False)
patched_isatty.return_value = False
console.QUIET = False
console.RALLY_RUNNING_IN_DOCKER = False

message = "Unit test message"
Expand All @@ -178,7 +214,7 @@ def test_noprint_when_isnotquiet_and_nodocker_and_noistty(self, patched_isatty,
@mock.patch("sys.stdout.isatty")
@pytest.mark.parametrize("seed", range(10))
def test_finish_noprint_when_isquiet_and_randomized_docker_or_istty(self, patched_isatty, seed):
console.QUIET = True
console.init(quiet=True, assume_tty=False)
random.seed(seed)
patched_isatty.return_value = random.choice([True, False])
console.RALLY_RUNNING_IN_DOCKER = random.choice([True, False])
Expand All @@ -192,7 +228,7 @@ def test_finish_noprint_when_isquiet_and_randomized_docker_or_istty(self, patche
@mock.patch("sys.stdout.isatty")
@pytest.mark.parametrize("seed", range(30))
def test_finish_prints_when_isnotquiet_and_randomized_docker_or_istty(self, patched_isatty, seed):
console.QUIET = False
console.init(quiet=False, assume_tty=False)
random.seed(seed)
random_boolean = random.choice([True, False])
patched_isatty.return_value = random_boolean
Expand All @@ -203,3 +239,18 @@ def test_finish_prints_when_isnotquiet_and_randomized_docker_or_istty(self, patc
progress_reporter = console.CmdLineProgressReporter(width=width, printer=mock_printer)
progress_reporter.finish()
mock_printer.assert_called_once_with("")

@mock.patch("sys.stdout.isatty")
@pytest.mark.parametrize("seed", range(30))
def test_finish_prints_when_isnotquiet_and_randomized_assume_tty_or_istty(self, patched_isatty, seed):
random.seed(seed)
random_boolean = random.choice([True, False])
console.init(quiet=False, assume_tty=random_boolean)
console.RALLY_RUNNING_IN_DOCKER = False
patched_isatty.return_value = not random_boolean

width = random.randint(20, 140)
mock_printer = mock.Mock()
progress_reporter = console.CmdLineProgressReporter(width=width, printer=mock_printer)
progress_reporter.finish()
mock_printer.assert_called_once_with("")