1# Copyright 2020 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""Color codes for use by rest of pw_cli.""" 15 16import ctypes 17import os 18import sys 19from typing import Optional, Union 20 21import pw_cli.env 22 23 24def _make_color(*codes): 25 # Apply all the requested ANSI color codes. Note that this is unbalanced 26 # with respect to the reset, which only requires a '0' to erase all codes. 27 start = ''.join(f'\033[{code}m' for code in codes) 28 reset = '\033[0m' 29 30 return lambda msg: f'{start}{msg}{reset}' 31 32 33# TODO(keir): Replace this with something like the 'colorful' module. 34class _Color: 35 # pylint: disable=too-few-public-methods 36 # pylint: disable=too-many-instance-attributes 37 """Helpers to surround text with ASCII color escapes""" 38 def __init__(self): 39 self.none = str 40 self.red = _make_color(31, 1) 41 self.bold_red = _make_color(30, 41) 42 self.yellow = _make_color(33, 1) 43 self.bold_yellow = _make_color(30, 43, 1) 44 self.green = _make_color(32) 45 self.bold_green = _make_color(30, 42) 46 self.blue = _make_color(34, 1) 47 self.cyan = _make_color(36, 1) 48 self.magenta = _make_color(35, 1) 49 self.bold_white = _make_color(37, 1) 50 self.black_on_white = _make_color(30, 47) # black fg white bg 51 52 53class _NoColor: 54 """Fake version of the _Color class that doesn't colorize.""" 55 def __getattr__(self, _): 56 return str 57 58 59def colors(enabled: Optional[bool] = None) -> Union[_Color, _NoColor]: 60 """Returns an object for colorizing strings. 61 62 By default, the object only colorizes if both stderr and stdout are TTYs. 63 """ 64 if enabled is None: 65 env = pw_cli.env.pigweed_environment() 66 enabled = env.PW_USE_COLOR or (sys.stdout.isatty() 67 and sys.stderr.isatty()) 68 69 if enabled and os.name == 'nt': 70 # Enable ANSI color codes in Windows cmd.exe. 71 kernel32 = ctypes.windll.kernel32 # type: ignore 72 kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7) 73 74 return _Color() if enabled else _NoColor() 75