libcopp  2.2.0
print_color.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 
4 import sys
5 import os
6 import ctypes
7 import platform
8 import re
9 
10 console_encoding = sys.getfilesystemencoding()
11 
12 
14  version = "1.0.2.0"
15  engine = None
16  theme = None
17 
18  FC_BLACK = 0
19  FC_BLUE = 1
20  FC_GREEN = 2
21  FC_CYAN = 3
22  FC_RED = 4
23  FC_MAGENTA = 5
24  FC_YELLOW = 6
25  FC_WHITE = 7
26 
27  BC_BLACK = 8
28  BC_BLUE = 9
29  BC_GREEN = 10
30  BC_CYAN = 11
31  BC_RED = 12
32  BC_MAGENTA = 13
33  BC_YELLOW = 14
34  BC_WHITE = 15
35 
36  FW_BOLD = 16
37 
38  def __contains__(self, value):
39  return False
40 
41 
42 """
43 See https://msdn.microsoft.com/zh-cn/windows/apps/ms682088%28v=vs.100%29#_win32_character_attributes
44  for color codes
45 """
46 
47 
49  name = "windows console"
50  STD_INPUT_HANDLE = -10
51  STD_OUTPUT_HANDLE = -11
52  STD_ERROR_HANDLE = -12
53 
54  FOREGROUND_BLACK = 0x0
55  FOREGROUND_BLUE = 0x01 # text color contains blue.
56  FOREGROUND_GREEN = 0x02 # text color contains green.
57  FOREGROUND_RED = 0x04 # text color contains red.
58  FOREGROUND_INTENSITY = 0x08 # text color is intensified.
59 
60  BACKGROUND_BLUE = 0x10 # background color contains blue.
61  BACKGROUND_GREEN = 0x20 # background color contains green.
62  BACKGROUND_RED = 0x40 # background color contains red.
63  BACKGROUND_INTENSITY = 0x80 # background color is intensified.
64 
65  COLOR_MAP = {
66  print_style.FC_BLACK: FOREGROUND_BLACK,
67  print_style.FC_BLUE: FOREGROUND_BLUE | FOREGROUND_INTENSITY,
68  print_style.FC_GREEN: FOREGROUND_GREEN | FOREGROUND_INTENSITY,
69  print_style.FC_CYAN:
70  FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
71  print_style.FC_RED: FOREGROUND_RED | FOREGROUND_INTENSITY,
72  print_style.FC_MAGENTA:
73  FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
74  print_style.FC_YELLOW:
75  FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
76  print_style.FC_WHITE:
77  FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED,
78  print_style.BC_BLACK: FOREGROUND_BLACK,
79  print_style.BC_BLUE: BACKGROUND_BLUE,
80  print_style.BC_GREEN: BACKGROUND_GREEN,
81  print_style.BC_CYAN: BACKGROUND_BLUE | BACKGROUND_GREEN,
82  print_style.BC_RED: BACKGROUND_RED,
83  print_style.BC_MAGENTA: BACKGROUND_RED | BACKGROUND_BLUE,
84  print_style.BC_YELLOW: BACKGROUND_RED | BACKGROUND_GREEN,
85  print_style.BC_WHITE:
86  BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE,
87  print_style.FW_BOLD: BACKGROUND_INTENSITY,
88  }
89 
90  std_out_handle = None
91  std_err_handle = None
92 
93  def get_cmd_color(self, handle=std_out_handle):
94  return (Win32ConsoleColor.FOREGROUND_RED
95  | Win32ConsoleColor.FOREGROUND_GREEN
96  | Win32ConsoleColor.FOREGROUND_BLUE)
97 
98  def set_cmd_color(self, color, handle=std_out_handle):
99  """(color) -> bit
100  Example: set_cmd_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY)
101  """
102  bool = ctypes.windll.kernel32.SetConsoleTextAttribute(handle, color)
103  return bool
104 
105  def stdout_with_color(self, options, text):
106  style = Win32ConsoleColor.FOREGROUND_BLACK
107  for opt in options:
108  style = style | Win32ConsoleColor.COLOR_MAP[opt]
109  if style == Win32ConsoleColor.FOREGROUND_BLACK:
110  sys.stdout.write(text)
111  else:
112  old_style = self.get_cmd_colorget_cmd_color()
113  self.set_cmd_colorset_cmd_color(style, self.std_out_handlestd_out_handle)
114  sys.stdout.write(text)
115  self.set_cmd_colorset_cmd_color(old_style, self.std_out_handlestd_out_handle)
116 
117  def stderr_with_color(self, options, text):
118  style = Win32ConsoleColor.FOREGROUND_BLACK
119  for opt in options:
120  style = style | Win32ConsoleColor.COLOR_MAP[opt]
121  if style == Win32ConsoleColor.FOREGROUND_BLACK:
122  sys.stderr.write(text)
123  else:
124  old_style = self.get_cmd_colorget_cmd_color()
125  self.set_cmd_colorset_cmd_color(style, self.std_err_handlestd_err_handle)
126  sys.stderr.write(text)
127  self.set_cmd_colorset_cmd_color(old_style, self.std_err_handlestd_err_handle)
128 
129 
130 class TermColor:
131  name = "terminal"
132  COLOR_MAP = {
133  print_style.FC_BLACK: "30",
134  print_style.FC_BLUE: "34",
135  print_style.FC_GREEN: "32",
136  print_style.FC_CYAN: "36",
137  print_style.FC_RED: "31",
138  print_style.FC_MAGENTA: "35",
139  print_style.FC_YELLOW: "33",
140  print_style.FC_WHITE: "37",
141  print_style.BC_BLACK: "40",
142  print_style.BC_BLUE: "44",
143  print_style.BC_GREEN: "42",
144  print_style.BC_CYAN: "46",
145  print_style.BC_RED: "41",
146  print_style.BC_MAGENTA: "45",
147  print_style.BC_YELLOW: "43",
148  print_style.BC_WHITE: "47",
149  print_style.FW_BOLD: "1",
150  }
151 
152  def stdout_with_color(self, options, text):
153  style = []
154  for opt in options:
155  style.append(TermColor.COLOR_MAP[opt])
156 
157  if len(style) > 0:
158  sys.stdout.write("\033[" + ";".join(style) + "m" + text +
159  "\033[0m")
160  else:
161  sys.stdout.write(text)
162 
163  def stderr_with_color(self, options, text):
164  style = []
165  for opt in options:
166  style.append(TermColor.COLOR_MAP[opt])
167 
168  if len(style) > 0:
169  sys.stderr.write("\033[" + ";".join(style) + "m" + text +
170  "\033[0m")
171  else:
172  sys.stderr.write(text)
173 
174 
175 class HtmlColor:
176  name = "html css"
177  COLOR_MAP = {
178  print_style.FC_BLACK: "color: {0}Black;",
179  print_style.FC_BLUE: "color: {0}Blue;",
180  print_style.FC_GREEN: "color: {0}Green;",
181  print_style.FC_CYAN: "color: {0}Cyan;",
182  print_style.FC_RED: "color: {0}Red;",
183  print_style.FC_MAGENTA: "color: {0}Magenta;",
184  print_style.FC_YELLOW: "color: {0}Yellow;",
185  print_style.FC_WHITE: "color: {0}White;",
186  print_style.BC_BLACK: "background-color: {0}Black;",
187  print_style.BC_BLUE: "background-color: {0}Blue;",
188  print_style.BC_GREEN: "background-color: {0}Green;",
189  print_style.BC_CYAN: "background-color: {0}Cyan;",
190  print_style.BC_RED: "background-color: {0}Red;",
191  print_style.BC_MAGENTA: "background-color: {0}Magenta;",
192  print_style.BC_YELLOW: "background-color: {0}Yellow;",
193  print_style.BC_WHITE: "background-color: {0}White;",
194  print_style.FW_BOLD: "font-weight: bold;",
195  }
196 
197  def stdout_with_color(self, options, text):
198  style = []
199  for opt in options:
200  if print_style.theme:
201  style.append(
202  HtmlColor.COLOR_MAP[opt].format(print_style.theme + "-"))
203  else:
204  style.append(HtmlColor.COLOR_MAP[opt].format(""))
205 
206  if len(style) > 0:
207  sys.stdout.write('<span style="' + " ".join(style) + '">' +
208  text.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;') + "</span>")
209  else:
210  sys.stdout.write(text.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;'))
211 
212  def stderr_with_color(self, options, text):
213  style = []
214  for opt in options:
215  if print_style.theme:
216  style.append(
217  HtmlColor.COLOR_MAP[opt].format(print_style.theme + "-"))
218  else:
219  style.append(HtmlColor.COLOR_MAP[opt].format(""))
220 
221  if len(style) > 0:
222  sys.stderr.write('<span style="' + " ".join(style) + '">' +
223  text.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;') + "</span>")
224  else:
225  sys.stderr.write(text.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;'))
226 
227 
228 class NoneColor:
229  name = "none"
230 
231  def stdout_with_color(self, options, text):
232  sys.stdout.write(text)
233 
234  def stderr_with_color(self, options, text):
235  sys.stderr.write(text)
236 
237 
239  # set by environment variable
240  if os.getenv("CPRINTF_MODE"):
241  return os.getenv("CPRINTF_MODE")
242  term_name = os.getenv("TERM")
243  if not term_name:
244  term_name = ""
245  if "dump" == term_name:
246  return "none"
247  if re.search('-256(color)?$', term_name, re.IGNORECASE):
248  return "term"
249  if re.search('^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux',
250  term_name, re.IGNORECASE):
251  return "term"
252  if os.getenv("COLORTERM"):
253  return "term"
254  if os.getenv("TF_BUILD") and os.getenv("AGENT_NAME"):
255  return "term"
256  if os.getenv("CI"):
257  for known_ci in [
258  'TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI',
259  'GITHUB_ACTIONS', 'BUILDKITE', 'DRONE'
260  ]:
261  if os.getenv(known_ci):
262  return "term"
263  ci_name = os.getenv("CI_NAME")
264  if ci_name and ci_name.lower() == "codeship":
265  return "term"
266  return "none"
267  if os.getenv("TEAMCITY_VERSION"):
268  TEAMCITY_VERSION = os.getenv("TEAMCITY_VERSION")
269  if re.search('^(9\.(0*[1-9]\d*)\.|\d{2,}\.)', TEAMCITY_VERSION,
270  re.IGNORECASE):
271  return "term"
272  else:
273  return "none"
274  if os.getenv("TERM_PROGRAM"):
275  if re.search('(iTerm.app|Apple_Terminal)', os.getenv("TERM_PROGRAM"),
276  re.IGNORECASE):
277  return "term"
278 
279  if "windows" == platform.system().lower():
280  ostype_name = os.getenv("OSTYPE")
281  if ostype_name:
282  ostype_name = ostype_name.lower()
283  if "msys" == ostype_name or "cygwin" == ostype_name:
284  return "none"
285  return "win32_console"
286  return "none"
287 
288 
289 def cprintf_set_mode(mode_name="auto"):
290  mode_name = mode_name.lower()
291  if not mode_name or mode_name == "auto":
293 
294  elif mode_name == "none":
295  print_style.engine = NoneColor
296 
297  elif mode_name == "term":
298  print_style.engine = TermColor
299 
300  elif mode_name == "win32_console":
301  """
302  See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winprog/winprog/windows_api_reference.asp
303  for information on Windows APIs.
304  """
305  Win32ConsoleColor.std_out_handle = ctypes.windll.kernel32.GetStdHandle(
306  Win32ConsoleColor.STD_OUTPUT_HANDLE)
307  Win32ConsoleColor.std_err_handle = ctypes.windll.kernel32.GetStdHandle(
308  Win32ConsoleColor.STD_ERROR_HANDLE)
309 
310  print_style.engine = Win32ConsoleColor
311 
312  elif mode_name == "html":
313  print_style.engine = HtmlColor
314 
315  else:
316  print_style.engine = NoneColor
317 
318 
319 def cprintf_set_theme(theme_name=None):
320  if theme_name is None:
321  if not os.getenv("CPRINTF_THEME") is None:
322  cprintf_set_theme(os.getenv("CPRINTF_THEME"))
323  else:
324  print_style.theme = theme_name
325 
326 
327 def cprintf_unpack_text(fmt, text):
328  if len(text) > 0:
329  try:
330  ret = fmt.format(*text)
331  return ret
332  except TypeError:
333  ret = fmt.decode("utf-8").encode(console_encoding).format(*text)
334  return ret
335  except EnvironmentError:
336  ret = fmt.decode("utf-8").encode(console_encoding).format(*text)
337  return ret
338  else:
339  return fmt
340 
341 
342 def cprintf_stdout(options, fmt, *text):
343  cp = print_style.engine()
344  cp.stdout_with_color(options, cprintf_unpack_text(fmt, text))
345  sys.stdout.flush()
346 
347 
348 def cprintf_stderr(options, fmt, *text):
349  cp = print_style.engine()
350  cp.stderr_with_color(options, cprintf_unpack_text(fmt, text))
351  sys.stderr.flush()
352 
353 
354 cprintf_set_mode("auto")
355 """ run as a executable """
356 if __name__ == "__main__":
357  from optparse import OptionParser
358 
359  usage = "usage: %prog [options...] <format message> [format parameters...]"
360  parser = OptionParser(usage)
361  parser.disable_interspersed_args()
362 
363  parser.add_option(
364  "-v",
365  "--version",
366  action="store_true",
367  help="show version and exit",
368  dest="version",
369  )
370  parser.add_option(
371  "-c",
372  "--color",
373  action="append",
374  help=
375  "set font color.(any of: black, blue, green, cyan, red, magenta, yellow, white)",
376  metavar="<color>",
377  dest="color",
378  )
379  parser.add_option(
380  "-b",
381  "--background-color",
382  action="append",
383  help=
384  "set background color.(any of: black, blue, green, cyan, red, magenta, yellow, white)",
385  metavar="<background color>",
386  dest="background_color",
387  )
388  parser.add_option(
389  "-B",
390  "--bold",
391  action="append_const",
392  help="set font weight to bold",
393  const=print_style.FW_BOLD,
394  dest="style",
395  )
396  parser.add_option(
397  "-m",
398  "--mode",
399  action="store",
400  help="set mode.(any of: auto, term, win32_console, none, html)",
401  metavar="<output mode>",
402  dest="mode",
403  )
404  parser.add_option(
405  "-s",
406  "--output-stream",
407  action="store",
408  help="set output stream.(any of: stdout, stderr)",
409  metavar="<ostream>",
410  dest="ostream",
411  default="stdout",
412  )
413  parser.add_option(
414  "-e",
415  action="store_true",
416  help=
417  "enable interpretation of backslash escapes(just like echo command in unix like system)",
418  dest="interp_bse",
419  default=False,
420  )
421  parser.add_option(
422  "-E",
423  action="store_false",
424  help=
425  "disable interpretation of backslash escapes(just like echo command in unix like system)",
426  dest="interp_bse",
427  )
428  parser.add_option(
429  "-t",
430  "--theme",
431  action="store",
432  help="set theme in html mode(light or dark)",
433  metavar="<theme>",
434  dest="theme",
435  default=None,
436  )
437 
438  (options, left_args) = parser.parse_args()
439 
440  print_stream = "stdout"
441  print_options = []
442 
443  fc_list = ["FC_" + x.upper() for x in options.color or []]
444  bk_list = ["BC_" + y.upper() for y in options.background_color or []]
445  for style_list in [fc_list, bk_list]:
446  for style_name in style_list:
447  if style_name in print_style.__dict__:
448  print_options.append(print_style.__dict__[style_name])
449 
450  for style_code in options.style or []:
451  print_options.append(style_code)
452 
453  if options.mode:
454  cprintf_set_mode(options.mode)
455 
456  if options.theme:
457  cprintf_set_theme(options.theme)
458  else:
459  cprintf_set_theme(None)
460 
461  if options.version:
462  print(print_style.version)
463  print("Color Engine: " + print_style.engine.name)
464  exit(0)
465 
466  if len(left_args) > 0:
467  if options.interp_bse:
468  for i in range(0, len(left_args)):
469  left_args[i] = eval(repr(left_args[i]).replace("\\\\", "\\"))
470  if "stdout" == options.ostream:
471  cprintf_stdout(print_options, *left_args)
472  else:
473  cprintf_stderr(print_options, *left_args)
def stdout_with_color(self, options, text)
Definition: print_color.py:197
def stderr_with_color(self, options, text)
Definition: print_color.py:212
def stdout_with_color(self, options, text)
Definition: print_color.py:231
def stderr_with_color(self, options, text)
Definition: print_color.py:234
def stdout_with_color(self, options, text)
Definition: print_color.py:152
def stderr_with_color(self, options, text)
Definition: print_color.py:163
def stdout_with_color(self, options, text)
Definition: print_color.py:105
def get_cmd_color(self, handle=std_out_handle)
Definition: print_color.py:93
def set_cmd_color(self, color, handle=std_out_handle)
Definition: print_color.py:98
def stderr_with_color(self, options, text)
Definition: print_color.py:117
def __contains__(self, value)
Definition: print_color.py:38
def cprintf_unpack_text(fmt, text)
Definition: print_color.py:327
def cprintf_stdout(options, fmt, *text)
Definition: print_color.py:342
def cprintf_resolve_auto_mode()
Definition: print_color.py:238
def cprintf_set_theme(theme_name=None)
Definition: print_color.py:319
def cprintf_set_mode(mode_name="auto")
Definition: print_color.py:289
def cprintf_stderr(options, fmt, *text)
Definition: print_color.py:348