term.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. # -*- coding: utf-8 -*-
  2. """Terminals and colors."""
  3. from __future__ import absolute_import, unicode_literals
  4. import base64
  5. import codecs
  6. import os
  7. import sys
  8. import platform
  9. from functools import reduce
  10. from celery.five import python_2_unicode_compatible, string
  11. from celery.platforms import isatty
  12. __all__ = ['colored']
  13. BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
  14. OP_SEQ = '\033[%dm'
  15. RESET_SEQ = '\033[0m'
  16. COLOR_SEQ = '\033[1;%dm'
  17. IS_WINDOWS = platform.system() == 'Windows'
  18. ITERM_PROFILE = os.environ.get('ITERM_PROFILE')
  19. TERM = os.environ.get('TERM')
  20. TERM_IS_SCREEN = TERM and TERM.startswith('screen')
  21. # tmux requires unrecognized OSC sequences to be wrapped with DCS tmux;
  22. # <sequence> ST, and for all ESCs in <sequence> to be replaced with ESC ESC.
  23. # It only accepts ESC backslash for ST.
  24. _IMG_PRE = '\033Ptmux;\033\033]' if TERM_IS_SCREEN else '\033]'
  25. _IMG_POST = '\a\033\\' if TERM_IS_SCREEN else '\a'
  26. def fg(s):
  27. return COLOR_SEQ % s
  28. @python_2_unicode_compatible
  29. class colored(object):
  30. """Terminal colored text.
  31. Example:
  32. >>> c = colored(enabled=True)
  33. >>> print(str(c.red('the quick '), c.blue('brown ', c.bold('fox ')),
  34. ... c.magenta(c.underline('jumps over')),
  35. ... c.yellow(' the lazy '),
  36. ... c.green('dog ')))
  37. """
  38. def __init__(self, *s, **kwargs):
  39. self.s = s
  40. self.enabled = not IS_WINDOWS and kwargs.get('enabled', True)
  41. self.op = kwargs.get('op', '')
  42. self.names = {
  43. 'black': self.black,
  44. 'red': self.red,
  45. 'green': self.green,
  46. 'yellow': self.yellow,
  47. 'blue': self.blue,
  48. 'magenta': self.magenta,
  49. 'cyan': self.cyan,
  50. 'white': self.white,
  51. }
  52. def _add(self, a, b):
  53. return string(a) + string(b)
  54. def _fold_no_color(self, a, b):
  55. try:
  56. A = a.no_color()
  57. except AttributeError:
  58. A = string(a)
  59. try:
  60. B = b.no_color()
  61. except AttributeError:
  62. B = string(b)
  63. return ''.join((string(A), string(B)))
  64. def no_color(self):
  65. if self.s:
  66. return string(reduce(self._fold_no_color, self.s))
  67. return ''
  68. def embed(self):
  69. prefix = ''
  70. if self.enabled:
  71. prefix = self.op
  72. return ''.join((string(prefix), string(reduce(self._add, self.s))))
  73. def __str__(self):
  74. suffix = ''
  75. if self.enabled:
  76. suffix = RESET_SEQ
  77. return string(''.join((self.embed(), string(suffix))))
  78. def node(self, s, op):
  79. return self.__class__(enabled=self.enabled, op=op, *s)
  80. def black(self, *s):
  81. return self.node(s, fg(30 + BLACK))
  82. def red(self, *s):
  83. return self.node(s, fg(30 + RED))
  84. def green(self, *s):
  85. return self.node(s, fg(30 + GREEN))
  86. def yellow(self, *s):
  87. return self.node(s, fg(30 + YELLOW))
  88. def blue(self, *s):
  89. return self.node(s, fg(30 + BLUE))
  90. def magenta(self, *s):
  91. return self.node(s, fg(30 + MAGENTA))
  92. def cyan(self, *s):
  93. return self.node(s, fg(30 + CYAN))
  94. def white(self, *s):
  95. return self.node(s, fg(30 + WHITE))
  96. def __repr__(self):
  97. return repr(self.no_color())
  98. def bold(self, *s):
  99. return self.node(s, OP_SEQ % 1)
  100. def underline(self, *s):
  101. return self.node(s, OP_SEQ % 4)
  102. def blink(self, *s):
  103. return self.node(s, OP_SEQ % 5)
  104. def reverse(self, *s):
  105. return self.node(s, OP_SEQ % 7)
  106. def bright(self, *s):
  107. return self.node(s, OP_SEQ % 8)
  108. def ired(self, *s):
  109. return self.node(s, fg(40 + RED))
  110. def igreen(self, *s):
  111. return self.node(s, fg(40 + GREEN))
  112. def iyellow(self, *s):
  113. return self.node(s, fg(40 + YELLOW))
  114. def iblue(self, *s):
  115. return self.node(s, fg(40 + BLUE))
  116. def imagenta(self, *s):
  117. return self.node(s, fg(40 + MAGENTA))
  118. def icyan(self, *s):
  119. return self.node(s, fg(40 + CYAN))
  120. def iwhite(self, *s):
  121. return self.node(s, fg(40 + WHITE))
  122. def reset(self, *s):
  123. return self.node(s or [''], RESET_SEQ)
  124. def __add__(self, other):
  125. return string(self) + string(other)
  126. def supports_images():
  127. return isatty(sys.stdin) and ITERM_PROFILE
  128. def _read_as_base64(path):
  129. with codecs.open(path, mode='rb') as fh:
  130. return base64.b64encode(fh.read())
  131. def imgcat(path, inline=1, preserve_aspect_ratio=0, **kwargs):
  132. return '\n%s1337;File=inline=%d;preserveAspectRatio=%d:%s%s' % (
  133. _IMG_PRE, inline, preserve_aspect_ratio,
  134. _read_as_base64(path), _IMG_POST)