aboutsummaryrefslogtreecommitdiff
path: root/rh/terminal.py
diff options
context:
space:
mode:
Diffstat (limited to 'rh/terminal.py')
-rw-r--r--rh/terminal.py90
1 files changed, 42 insertions, 48 deletions
diff --git a/rh/terminal.py b/rh/terminal.py
index c549f12..a6f31d9 100644
--- a/rh/terminal.py
+++ b/rh/terminal.py
@@ -1,4 +1,3 @@
-# -*- coding:utf-8 -*-
# Copyright 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,10 +17,9 @@
This module handles terminal interaction including ANSI color codes.
"""
-from __future__ import print_function
-
import os
import sys
+from typing import List, Optional
_path = os.path.realpath(__file__ + '/../..')
if sys.path[0] != _path:
@@ -32,6 +30,12 @@ del _path
import rh.shell
+# This will erase all content in the current line after the cursor. This is
+# useful for partial updates & progress messages as the terminal can display
+# it better.
+CSI_ERASE_LINE_AFTER = '\x1b[K'
+
+
class Color(object):
"""Conditionally wraps text in ANSI color escape sequences."""
@@ -39,7 +43,7 @@ class Color(object):
BOLD = -1
COLOR_START = '\033[1;%dm'
BOLD_START = '\033[1m'
- RESET = '\033[0m'
+ RESET = '\033[m'
def __init__(self, enabled=None):
"""Create a new Color object, optionally disabling color output.
@@ -54,7 +58,7 @@ class Color(object):
"""Returns a start color code.
Args:
- color: Color to use, .e.g BLACK, RED, etc.
+ color: Color to use, e.g. BLACK, RED, etc...
Returns:
If color is enabled, returns an ANSI sequence to start the given
@@ -102,25 +106,10 @@ class Color(object):
self._enabled = not rh.shell.boolean_shell_value(
os.environ['NOCOLOR'], False)
else:
- self._enabled = is_tty(sys.stderr)
+ self._enabled = sys.stderr.isatty()
return self._enabled
-def is_tty(fh):
- """Returns whether the specified file handle is a TTY.
-
- Args:
- fh: File handle to check.
-
- Returns:
- True if |fh| is a TTY
- """
- try:
- return os.isatty(fh.fileno())
- except IOError:
- return False
-
-
def print_status_line(line, print_newline=False):
"""Clears the current terminal line, and prints |line|.
@@ -128,8 +117,8 @@ def print_status_line(line, print_newline=False):
line: String to print.
print_newline: Print a newline at the end, if sys.stderr is a TTY.
"""
- if is_tty(sys.stderr):
- output = '\r' + line + '\x1B[K'
+ if sys.stderr.isatty():
+ output = '\r' + line + CSI_ERASE_LINE_AFTER
if print_newline:
output += '\n'
else:
@@ -139,16 +128,32 @@ def print_status_line(line, print_newline=False):
sys.stderr.flush()
-def get_input(prompt):
- """Python 2/3 glue for raw_input/input differences."""
+def str_prompt(
+ prompt: str,
+ choices: List[str],
+ lower: bool = True,
+) -> Optional[str]:
+ """Helper function for processing user input.
+
+ Args:
+ prompt: The question to present to the user.
+ lower: Whether to lowercase the response.
+
+ Returns:
+ The string the user entered, or None if EOF (e.g. Ctrl+D).
+ """
+ prompt = f'{prompt} ({"/".join(choices)})? '
try:
- # pylint: disable=raw_input-builtin
- return raw_input(prompt)
- except NameError:
- # Python 3 renamed raw_input() to input(), which is safe to call since
- # it does not evaluate the input.
- # pylint: disable=bad-builtin,input-builtin
- return input(prompt)
+ result = input(prompt)
+ return result.lower() if lower else result
+ except EOFError:
+ # If the user hits Ctrl+D, or stdin is disabled, use the default.
+ print()
+ return None
+ except KeyboardInterrupt:
+ # If the user hits Ctrl+C, just exit the process.
+ print()
+ raise
def boolean_prompt(prompt='Do you want to continue?', default=True,
@@ -168,31 +173,20 @@ def boolean_prompt(prompt='Do you want to continue?', default=True,
true_value, false_value = true_value.lower(), false_value.lower()
true_text, false_text = true_value, false_value
if true_value == false_value:
- raise ValueError('true_value and false_value must differ: got %r'
- % true_value)
+ raise ValueError(
+ f'true_value and false_value must differ: got {true_value!r}')
if default:
true_text = true_text[0].upper() + true_text[1:]
else:
false_text = false_text[0].upper() + false_text[1:]
- prompt = ('\n%s (%s/%s)? ' % (prompt, true_text, false_text))
-
if prolog:
- prompt = ('\n%s\n%s' % (prolog, prompt))
+ prompt = f'\n{prolog}\n{prompt}'
+ prompt = '\n' + prompt
while True:
- try:
- response = get_input(prompt).lower()
- except EOFError:
- # If the user hits CTRL+D, or stdin is disabled, use the default.
- print()
- response = None
- except KeyboardInterrupt:
- # If the user hits CTRL+C, just exit the process.
- print()
- raise
-
+ response = str_prompt(prompt, choices=(true_text, false_text))
if not response:
return default
if true_value.startswith(response):