git-repo/color.py
Jason R. Coombs d3bda81f9e cleanup: Update codebase to expect Python 3.6
- Bump minimum version to Python 3.6.
- Ran pyupgrade for Python 3.6, then again, then
  - ran black
  - fixed a lingering unused import, and
  - removed a duplicate exception.

Change-Id: I2aa70197230fcec2eff8e7c8eb754f20c08075bb
2023-10-16 13:27:08 -04:00

218 lines
4.8 KiB
Python

# Copyright (C) 2008 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
import pager
COLORS = {
None: -1,
"normal": -1,
"black": 0,
"red": 1,
"green": 2,
"yellow": 3,
"blue": 4,
"magenta": 5,
"cyan": 6,
"white": 7,
}
ATTRS = {None: -1, "bold": 1, "dim": 2, "ul": 4, "blink": 5, "reverse": 7}
RESET = "\033[m"
def is_color(s):
return s in COLORS
def is_attr(s):
return s in ATTRS
def _Color(fg=None, bg=None, attr=None):
fg = COLORS[fg]
bg = COLORS[bg]
attr = ATTRS[attr]
if attr >= 0 or fg >= 0 or bg >= 0:
need_sep = False
code = "\033["
if attr >= 0:
code += chr(ord("0") + attr)
need_sep = True
if fg >= 0:
if need_sep:
code += ";"
need_sep = True
if fg < 8:
code += "3%c" % (ord("0") + fg)
else:
code += "38;5;%d" % fg
if bg >= 0:
if need_sep:
code += ";"
if bg < 8:
code += "4%c" % (ord("0") + bg)
else:
code += "48;5;%d" % bg
code += "m"
else:
code = ""
return code
DEFAULT = None
def SetDefaultColoring(state):
"""Set coloring behavior to |state|.
This is useful for overriding config options via the command line.
"""
if state is None:
# Leave it alone -- return quick!
return
global DEFAULT
state = state.lower()
if state in ("auto",):
DEFAULT = state
elif state in ("always", "yes", "true", True):
DEFAULT = "always"
elif state in ("never", "no", "false", False):
DEFAULT = "never"
class Coloring:
def __init__(self, config, section_type):
self._section = "color.%s" % section_type
self._config = config
self._out = sys.stdout
on = DEFAULT
if on is None:
on = self._config.GetString(self._section)
if on is None:
on = self._config.GetString("color.ui")
if on == "auto":
if pager.active or os.isatty(1):
self._on = True
else:
self._on = False
elif on in ("true", "always"):
self._on = True
else:
self._on = False
def redirect(self, out):
self._out = out
@property
def is_on(self):
return self._on
def write(self, fmt, *args):
self._out.write(fmt % args)
def flush(self):
self._out.flush()
def nl(self):
self._out.write("\n")
def printer(self, opt=None, fg=None, bg=None, attr=None):
s = self
c = self.colorer(opt, fg, bg, attr)
def f(fmt, *args):
s._out.write(c(fmt, *args))
return f
def nofmt_printer(self, opt=None, fg=None, bg=None, attr=None):
s = self
c = self.nofmt_colorer(opt, fg, bg, attr)
def f(fmt):
s._out.write(c(fmt))
return f
def colorer(self, opt=None, fg=None, bg=None, attr=None):
if self._on:
c = self._parse(opt, fg, bg, attr)
def f(fmt, *args):
output = fmt % args
return "".join([c, output, RESET])
return f
else:
def f(fmt, *args):
return fmt % args
return f
def nofmt_colorer(self, opt=None, fg=None, bg=None, attr=None):
if self._on:
c = self._parse(opt, fg, bg, attr)
def f(fmt):
return "".join([c, fmt, RESET])
return f
else:
def f(fmt):
return fmt
return f
def _parse(self, opt, fg, bg, attr):
if not opt:
return _Color(fg, bg, attr)
v = self._config.GetString(f"{self._section}.{opt}")
if v is None:
return _Color(fg, bg, attr)
v = v.strip().lower()
if v == "reset":
return RESET
elif v == "":
return _Color(fg, bg, attr)
have_fg = False
for a in v.split(" "):
if is_color(a):
if have_fg:
bg = a
else:
fg = a
elif is_attr(a):
attr = a
return _Color(fg, bg, attr)