Commit 1577d688ac355fcf2c62214c8f104835376e96b9

Authored by Philippe Lagadec
1 parent c5566974

added colorclass into thirdparty folder

oletools/thirdparty/colorclass/LICENSE.txt 0 โ†’ 100644
  1 +The MIT License (MIT)
  2 +
  3 +Copyright (c) 2014 Robpol86
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining a copy
  6 +of this software and associated documentation files (the "Software"), to deal
  7 +in the Software without restriction, including without limitation the rights
  8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 +copies of the Software, and to permit persons to whom the Software is
  10 +furnished to do so, subject to the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be included in all
  13 +copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21 +SOFTWARE.
... ...
oletools/thirdparty/colorclass/__init__.py 0 โ†’ 100644
oletools/thirdparty/colorclass/colorclass.py 0 โ†’ 100644
  1 +"""Colorful worry-free console applications for Linux, Mac OS X, and Windows.
  2 +
  3 +Supported natively on Linux and Mac OSX (Just Works), and on Windows it works the same if Windows.enable() is called.
  4 +
  5 +Gives you expected and sane results from methods like len() and .capitalize().
  6 +
  7 +https://github.com/Robpol86/colorclass
  8 +https://pypi.python.org/pypi/colorclass
  9 +"""
  10 +
  11 +import atexit
  12 +from collections import Mapping
  13 +import ctypes
  14 +import os
  15 +import re
  16 +import sys
  17 +
  18 +if os.name == 'nt':
  19 + import ctypes.wintypes
  20 +
  21 +__author__ = '@Robpol86'
  22 +__license__ = 'MIT'
  23 +__version__ = '1.2.0'
  24 +_BASE_CODES = {
  25 + '/all': 0, 'b': 1, 'f': 2, 'i': 3, 'u': 4, 'flash': 5, 'outline': 6, 'negative': 7, 'invis': 8, 'strike': 9,
  26 + '/b': 22, '/f': 22, '/i': 23, '/u': 24, '/flash': 25, '/outline': 26, '/negative': 27, '/invis': 28,
  27 + '/strike': 29, '/fg': 39, '/bg': 49,
  28 +
  29 + 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37,
  30 +
  31 + 'bgblack': 40, 'bgred': 41, 'bggreen': 42, 'bgyellow': 43, 'bgblue': 44, 'bgmagenta': 45, 'bgcyan': 46,
  32 + 'bgwhite': 47,
  33 +
  34 + 'hiblack': 90, 'hired': 91, 'higreen': 92, 'hiyellow': 93, 'hiblue': 94, 'himagenta': 95, 'hicyan': 96,
  35 + 'hiwhite': 97,
  36 +
  37 + 'hibgblack': 100, 'hibgred': 101, 'hibggreen': 102, 'hibgyellow': 103, 'hibgblue': 104, 'hibgmagenta': 105,
  38 + 'hibgcyan': 106, 'hibgwhite': 107,
  39 +
  40 + 'autored': None, 'autoblack': None, 'automagenta': None, 'autowhite': None, 'autoblue': None, 'autoyellow': None,
  41 + 'autogreen': None, 'autocyan': None,
  42 +
  43 + 'autobgred': None, 'autobgblack': None, 'autobgmagenta': None, 'autobgwhite': None, 'autobgblue': None,
  44 + 'autobgyellow': None, 'autobggreen': None, 'autobgcyan': None,
  45 +
  46 + '/black': 39, '/red': 39, '/green': 39, '/yellow': 39, '/blue': 39, '/magenta': 39, '/cyan': 39, '/white': 39,
  47 + '/hiblack': 39, '/hired': 39, '/higreen': 39, '/hiyellow': 39, '/hiblue': 39, '/himagenta': 39, '/hicyan': 39,
  48 + '/hiwhite': 39,
  49 +
  50 + '/bgblack': 49, '/bgred': 49, '/bggreen': 49, '/bgyellow': 49, '/bgblue': 49, '/bgmagenta': 49, '/bgcyan': 49,
  51 + '/bgwhite': 49, '/hibgblack': 49, '/hibgred': 49, '/hibggreen': 49, '/hibgyellow': 49, '/hibgblue': 49,
  52 + '/hibgmagenta': 49, '/hibgcyan': 49, '/hibgwhite': 49,
  53 +
  54 + '/autored': 39, '/autoblack': 39, '/automagenta': 39, '/autowhite': 39, '/autoblue': 39, '/autoyellow': 39,
  55 + '/autogreen': 39, '/autocyan': 39,
  56 +
  57 + '/autobgred': 49, '/autobgblack': 49, '/autobgmagenta': 49, '/autobgwhite': 49, '/autobgblue': 49,
  58 + '/autobgyellow': 49, '/autobggreen': 49, '/autobgcyan': 49,
  59 +}
  60 +_WINDOWS_CODES = {
  61 + '/all': -33, '/fg': -39, '/bg': -49,
  62 +
  63 + 'black': 0, 'red': 4, 'green': 2, 'yellow': 6, 'blue': 1, 'magenta': 5, 'cyan': 3, 'white': 7,
  64 +
  65 + 'bgblack': -8, 'bgred': 64, 'bggreen': 32, 'bgyellow': 96, 'bgblue': 16, 'bgmagenta': 80, 'bgcyan': 48,
  66 + 'bgwhite': 112,
  67 +
  68 + 'hiblack': 8, 'hired': 12, 'higreen': 10, 'hiyellow': 14, 'hiblue': 9, 'himagenta': 13, 'hicyan': 11, 'hiwhite': 15,
  69 +
  70 + 'hibgblack': 128, 'hibgred': 192, 'hibggreen': 160, 'hibgyellow': 224, 'hibgblue': 144, 'hibgmagenta': 208,
  71 + 'hibgcyan': 176, 'hibgwhite': 240,
  72 +
  73 + '/black': -39, '/red': -39, '/green': -39, '/yellow': -39, '/blue': -39, '/magenta': -39, '/cyan': -39,
  74 + '/white': -39, '/hiblack': -39, '/hired': -39, '/higreen': -39, '/hiyellow': -39, '/hiblue': -39, '/himagenta': -39,
  75 + '/hicyan': -39, '/hiwhite': -39,
  76 +
  77 + '/bgblack': -49, '/bgred': -49, '/bggreen': -49, '/bgyellow': -49, '/bgblue': -49, '/bgmagenta': -49,
  78 + '/bgcyan': -49, '/bgwhite': -49, '/hibgblack': -49, '/hibgred': -49, '/hibggreen': -49, '/hibgyellow': -49,
  79 + '/hibgblue': -49, '/hibgmagenta': -49, '/hibgcyan': -49, '/hibgwhite': -49,
  80 +}
  81 +_RE_GROUP_SEARCH = re.compile(r'(?:\033\[[\d;]+m)+')
  82 +_RE_NUMBER_SEARCH = re.compile(r'\033\[([\d;]+)m')
  83 +_RE_SPLIT = re.compile(r'(\033\[[\d;]+m)')
  84 +PARENT_CLASS = type(u'')
  85 +
  86 +
  87 +class _AutoCodes(Mapping):
  88 + """Read-only subclass of dict, resolves closing tags (based on colorclass.CODES) and automatic colors."""
  89 + DISABLE_COLORS = False
  90 + LIGHT_BACKGROUND = False
  91 +
  92 + def __init__(self):
  93 + self.__dict = _BASE_CODES.copy()
  94 +
  95 + def __getitem__(self, item):
  96 + if item == 'autoblack':
  97 + answer = self.autoblack
  98 + elif item == 'autored':
  99 + answer = self.autored
  100 + elif item == 'autogreen':
  101 + answer = self.autogreen
  102 + elif item == 'autoyellow':
  103 + answer = self.autoyellow
  104 + elif item == 'autoblue':
  105 + answer = self.autoblue
  106 + elif item == 'automagenta':
  107 + answer = self.automagenta
  108 + elif item == 'autocyan':
  109 + answer = self.autocyan
  110 + elif item == 'autowhite':
  111 + answer = self.autowhite
  112 + elif item == 'autobgblack':
  113 + answer = self.autobgblack
  114 + elif item == 'autobgred':
  115 + answer = self.autobgred
  116 + elif item == 'autobggreen':
  117 + answer = self.autobggreen
  118 + elif item == 'autobgyellow':
  119 + answer = self.autobgyellow
  120 + elif item == 'autobgblue':
  121 + answer = self.autobgblue
  122 + elif item == 'autobgmagenta':
  123 + answer = self.autobgmagenta
  124 + elif item == 'autobgcyan':
  125 + answer = self.autobgcyan
  126 + elif item == 'autobgwhite':
  127 + answer = self.autobgwhite
  128 + else:
  129 + answer = self.__dict[item]
  130 + return answer
  131 +
  132 + def __iter__(self):
  133 + return iter(self.__dict)
  134 +
  135 + def __len__(self):
  136 + return len(self.__dict)
  137 +
  138 + @property
  139 + def autoblack(self):
  140 + """Returns automatic black foreground color depending on background color."""
  141 + return self.__dict['black' if _AutoCodes.LIGHT_BACKGROUND else 'hiblack']
  142 +
  143 + @property
  144 + def autored(self):
  145 + """Returns automatic red foreground color depending on background color."""
  146 + return self.__dict['red' if _AutoCodes.LIGHT_BACKGROUND else 'hired']
  147 +
  148 + @property
  149 + def autogreen(self):
  150 + """Returns automatic green foreground color depending on background color."""
  151 + return self.__dict['green' if _AutoCodes.LIGHT_BACKGROUND else 'higreen']
  152 +
  153 + @property
  154 + def autoyellow(self):
  155 + """Returns automatic yellow foreground color depending on background color."""
  156 + return self.__dict['yellow' if _AutoCodes.LIGHT_BACKGROUND else 'hiyellow']
  157 +
  158 + @property
  159 + def autoblue(self):
  160 + """Returns automatic blue foreground color depending on background color."""
  161 + return self.__dict['blue' if _AutoCodes.LIGHT_BACKGROUND else 'hiblue']
  162 +
  163 + @property
  164 + def automagenta(self):
  165 + """Returns automatic magenta foreground color depending on background color."""
  166 + return self.__dict['magenta' if _AutoCodes.LIGHT_BACKGROUND else 'himagenta']
  167 +
  168 + @property
  169 + def autocyan(self):
  170 + """Returns automatic cyan foreground color depending on background color."""
  171 + return self.__dict['cyan' if _AutoCodes.LIGHT_BACKGROUND else 'hicyan']
  172 +
  173 + @property
  174 + def autowhite(self):
  175 + """Returns automatic white foreground color depending on background color."""
  176 + return self.__dict['white' if _AutoCodes.LIGHT_BACKGROUND else 'hiwhite']
  177 +
  178 + @property
  179 + def autobgblack(self):
  180 + """Returns automatic black background color depending on background color."""
  181 + return self.__dict['bgblack' if _AutoCodes.LIGHT_BACKGROUND else 'hibgblack']
  182 +
  183 + @property
  184 + def autobgred(self):
  185 + """Returns automatic red background color depending on background color."""
  186 + return self.__dict['bgred' if _AutoCodes.LIGHT_BACKGROUND else 'hibgred']
  187 +
  188 + @property
  189 + def autobggreen(self):
  190 + """Returns automatic green background color depending on background color."""
  191 + return self.__dict['bggreen' if _AutoCodes.LIGHT_BACKGROUND else 'hibggreen']
  192 +
  193 + @property
  194 + def autobgyellow(self):
  195 + """Returns automatic yellow background color depending on background color."""
  196 + return self.__dict['bgyellow' if _AutoCodes.LIGHT_BACKGROUND else 'hibgyellow']
  197 +
  198 + @property
  199 + def autobgblue(self):
  200 + """Returns automatic blue background color depending on background color."""
  201 + return self.__dict['bgblue' if _AutoCodes.LIGHT_BACKGROUND else 'hibgblue']
  202 +
  203 + @property
  204 + def autobgmagenta(self):
  205 + """Returns automatic magenta background color depending on background color."""
  206 + return self.__dict['bgmagenta' if _AutoCodes.LIGHT_BACKGROUND else 'hibgmagenta']
  207 +
  208 + @property
  209 + def autobgcyan(self):
  210 + """Returns automatic cyan background color depending on background color."""
  211 + return self.__dict['bgcyan' if _AutoCodes.LIGHT_BACKGROUND else 'hibgcyan']
  212 +
  213 + @property
  214 + def autobgwhite(self):
  215 + """Returns automatic white background color depending on background color."""
  216 + return self.__dict['bgwhite' if _AutoCodes.LIGHT_BACKGROUND else 'hibgwhite']
  217 +
  218 +
  219 +def _pad_input(incoming):
  220 + """Avoid IndexError and KeyError by ignoring un-related fields.
  221 +
  222 + Example: '{0}{autored}' becomes '{{0}}{autored}'.
  223 +
  224 + Positional arguments:
  225 + incoming -- the input unicode value.
  226 +
  227 + Returns:
  228 + Padded unicode value.
  229 + """
  230 + incoming_expanded = incoming.replace('{', '{{').replace('}', '}}')
  231 + for key in _BASE_CODES:
  232 + before, after = '{{%s}}' % key, '{%s}' % key
  233 + if before in incoming_expanded:
  234 + incoming_expanded = incoming_expanded.replace(before, after)
  235 + return incoming_expanded
  236 +
  237 +
  238 +def _parse_input(incoming):
  239 + """Performs the actual conversion of tags to ANSI escaped codes.
  240 +
  241 + Provides a version of the input without any colors for len() and other methods.
  242 +
  243 + Positional arguments:
  244 + incoming -- the input unicode value.
  245 +
  246 + Returns:
  247 + 2-item tuple. First item is the parsed output. Second item is a version of the input without any colors.
  248 + """
  249 + codes = dict((k, v) for k, v in _AutoCodes().items() if '{%s}' % k in incoming)
  250 + color_codes = dict((k, '' if _AutoCodes.DISABLE_COLORS else '\033[{0}m'.format(v)) for k, v in codes.items())
  251 + incoming_padded = _pad_input(incoming)
  252 + output_colors = incoming_padded.format(**color_codes)
  253 +
  254 + # Simplify: '{b}{red}' -> '\033[1m\033[31m' -> '\033[1;31m'
  255 + groups = sorted(set(_RE_GROUP_SEARCH.findall(output_colors)), key=len, reverse=True) # Get codes, grouped adjacent.
  256 + groups_simplified = [[x for n in _RE_NUMBER_SEARCH.findall(i) for x in n.split(';')] for i in groups]
  257 + groups_compiled = ['\033[{0}m'.format(';'.join(g)) for g in groups_simplified] # Final codes.
  258 + assert len(groups_compiled) == len(groups) # For testing.
  259 + output_colors_simplified = output_colors
  260 + for i in range(len(groups)):
  261 + output_colors_simplified = output_colors_simplified.replace(groups[i], groups_compiled[i])
  262 + output_no_colors = _RE_SPLIT.sub('', output_colors_simplified)
  263 +
  264 + # Strip any remaining color codes.
  265 + if _AutoCodes.DISABLE_COLORS:
  266 + output_colors_simplified = _RE_NUMBER_SEARCH.sub('', output_colors_simplified)
  267 +
  268 + return output_colors_simplified, output_no_colors
  269 +
  270 +
  271 +def disable_all_colors():
  272 + """Disable all colors. Strips any color tags or codes."""
  273 + _AutoCodes.DISABLE_COLORS = True
  274 +
  275 +
  276 +def set_light_background():
  277 + """Chooses dark colors for all 'auto'-prefixed codes for readability on light backgrounds."""
  278 + _AutoCodes.DISABLE_COLORS = False
  279 + _AutoCodes.LIGHT_BACKGROUND = True
  280 +
  281 +
  282 +def set_dark_background():
  283 + """Chooses dark colors for all 'auto'-prefixed codes for readability on light backgrounds."""
  284 + _AutoCodes.DISABLE_COLORS = False
  285 + _AutoCodes.LIGHT_BACKGROUND = False
  286 +
  287 +
  288 +def list_tags():
  289 + """Lists the available tags.
  290 +
  291 + Returns:
  292 + Tuple of tuples. Child tuples are four items: ('opening tag', 'closing tag', main ansi value, closing ansi value).
  293 + """
  294 + codes = _AutoCodes()
  295 + grouped = set([(k, '/{0}'.format(k), codes[k], codes['/{0}'.format(k)]) for k in codes if not k.startswith('/')])
  296 +
  297 + # Add half-tags like /all.
  298 + found = [c for r in grouped for c in r[:2]]
  299 + missing = set([('', r[0], None, r[1]) if r[0].startswith('/') else (r[0], '', r[1], None)
  300 + for r in _AutoCodes().items() if r[0] not in found])
  301 + grouped |= missing
  302 +
  303 + # Sort.
  304 + payload = sorted([i for i in grouped if i[2] is None], key=lambda x: x[3]) # /all /fg /bg
  305 + grouped -= set(payload)
  306 + payload.extend(sorted([i for i in grouped if i[2] < 10], key=lambda x: x[2])) # b i u flash
  307 + grouped -= set(payload)
  308 + payload.extend(sorted([i for i in grouped if i[0].startswith('auto')], key=lambda x: x[2])) # auto colors
  309 + grouped -= set(payload)
  310 + payload.extend(sorted([i for i in grouped if not i[0].startswith('hi')], key=lambda x: x[2])) # dark colors
  311 + grouped -= set(payload)
  312 + payload.extend(sorted(grouped, key=lambda x: x[2])) # light colors
  313 + return tuple(payload)
  314 +
  315 +
  316 +class ColorBytes(bytes):
  317 + """Str (bytes in Python3) subclass, .decode() overridden to return Color() instance."""
  318 +
  319 + def decode(*args, **kwargs):
  320 + return Color(super(ColorBytes, args[0]).decode(*args[1:], **kwargs))
  321 +
  322 +
  323 +class Color(PARENT_CLASS):
  324 + """Unicode (str in Python3) subclass with ANSI terminal text color support.
  325 +
  326 + Example syntax: Color('{red}Sample Text{/red}')
  327 +
  328 + For a list of codes, call: colorclass.list_tags()
  329 + """
  330 +
  331 + @classmethod
  332 + def red(cls, s, auto=False):
  333 + return cls.colorize('red', s, auto=auto)
  334 +
  335 + @classmethod
  336 + def bgred(cls, s, auto=False):
  337 + return cls.colorize('bgred', s, auto=auto)
  338 +
  339 + @classmethod
  340 + def green(cls, s, auto=False):
  341 + return cls.colorize('green', s, auto=auto)
  342 +
  343 + @classmethod
  344 + def bggreen(cls, s, auto=False):
  345 + return cls.colorize('bggreen', s, auto=auto)
  346 +
  347 + @classmethod
  348 + def blue(cls, s, auto=False):
  349 + return cls.colorize('blue', s, auto=auto)
  350 +
  351 + @classmethod
  352 + def bgblue(cls, s, auto=False):
  353 + return cls.colorize('bgblue', s, auto=auto)
  354 +
  355 + @classmethod
  356 + def yellow(cls, s, auto=False):
  357 + return cls.colorize('yellow', s, auto=auto)
  358 +
  359 + @classmethod
  360 + def bgyellow(cls, s, auto=False):
  361 + return cls.colorize('bgyellow', s, auto=auto)
  362 +
  363 + @classmethod
  364 + def cyan(cls, s, auto=False):
  365 + return cls.colorize('cyan', s, auto=auto)
  366 +
  367 + @classmethod
  368 + def bgcyan(cls, s, auto=False):
  369 + return cls.colorize('bgcyan', s, auto=auto)
  370 +
  371 + @classmethod
  372 + def magenta(cls, s, auto=False):
  373 + return cls.colorize('magenta', s, auto=auto)
  374 +
  375 + @classmethod
  376 + def bgmagenta(cls, s, auto=False):
  377 + return cls.colorize('bgmagenta', s, auto=auto)
  378 +
  379 + @classmethod
  380 + def colorize(cls, color, s, auto=False):
  381 + tag = '{0}{1}'.format('auto' if auto else '', color)
  382 + return cls('{%s}%s{/%s}' % (tag, s, tag))
  383 +
  384 + def __new__(cls, *args, **kwargs):
  385 + parent_class = cls.__bases__[0]
  386 + value_markup = args[0] if args else parent_class()
  387 + value_colors, value_no_colors = _parse_input(value_markup)
  388 + if args:
  389 + args = [value_colors] + list(args[1:])
  390 +
  391 + obj = parent_class.__new__(cls, *args, **kwargs)
  392 + obj.value_colors, obj.value_no_colors = value_colors, value_no_colors
  393 + obj.has_colors = bool(_RE_NUMBER_SEARCH.match(value_colors))
  394 + return obj
  395 +
  396 + def __len__(self):
  397 + return self.value_no_colors.__len__()
  398 +
  399 + def capitalize(self):
  400 + split = _RE_SPLIT.split(self.value_colors)
  401 + for i in range(len(split)):
  402 + if _RE_SPLIT.match(split[i]):
  403 + continue
  404 + split[i] = PARENT_CLASS(split[i]).capitalize()
  405 + return Color().join(split)
  406 +
  407 + def center(self, width, fillchar=None):
  408 + if fillchar is not None:
  409 + result = PARENT_CLASS(self.value_no_colors).center(width, fillchar)
  410 + else:
  411 + result = PARENT_CLASS(self.value_no_colors).center(width)
  412 + return result.replace(self.value_no_colors, self.value_colors)
  413 +
  414 + def count(self, *args, **kwargs):
  415 + return PARENT_CLASS(self.value_no_colors).count(*args, **kwargs)
  416 +
  417 + def endswith(self, *args, **kwargs):
  418 + return PARENT_CLASS(self.value_no_colors).endswith(*args, **kwargs)
  419 +
  420 + def encode(*args, **kwargs):
  421 + return ColorBytes(super(Color, args[0]).encode(*args[1:], **kwargs))
  422 +
  423 + def decode(*args, **kwargs):
  424 + return Color(super(Color, args[0]).decode(*args[1:], **kwargs))
  425 +
  426 + def find(self, *args, **kwargs):
  427 + return PARENT_CLASS(self.value_no_colors).find(*args, **kwargs)
  428 +
  429 + def format(*args, **kwargs):
  430 + return Color(super(Color, args[0]).format(*args[1:], **kwargs))
  431 +
  432 + def index(self, *args, **kwargs):
  433 + return PARENT_CLASS(self.value_no_colors).index(*args, **kwargs)
  434 +
  435 + def isalnum(self):
  436 + return PARENT_CLASS(self.value_no_colors).isalnum()
  437 +
  438 + def isalpha(self):
  439 + return PARENT_CLASS(self.value_no_colors).isalpha()
  440 +
  441 + def isdecimal(self):
  442 + return PARENT_CLASS(self.value_no_colors).isdecimal()
  443 +
  444 + def isdigit(self):
  445 + return PARENT_CLASS(self.value_no_colors).isdigit()
  446 +
  447 + def isnumeric(self):
  448 + return PARENT_CLASS(self.value_no_colors).isnumeric()
  449 +
  450 + def isspace(self):
  451 + return PARENT_CLASS(self.value_no_colors).isspace()
  452 +
  453 + def istitle(self):
  454 + return PARENT_CLASS(self.value_no_colors).istitle()
  455 +
  456 + def isupper(self):
  457 + return PARENT_CLASS(self.value_no_colors).isupper()
  458 +
  459 + def ljust(self, width, fillchar=None):
  460 + if fillchar is not None:
  461 + result = PARENT_CLASS(self.value_no_colors).ljust(width, fillchar)
  462 + else:
  463 + result = PARENT_CLASS(self.value_no_colors).ljust(width)
  464 + return result.replace(self.value_no_colors, self.value_colors)
  465 +
  466 + def rfind(self, *args, **kwargs):
  467 + return PARENT_CLASS(self.value_no_colors).rfind(*args, **kwargs)
  468 +
  469 + def rindex(self, *args, **kwargs):
  470 + return PARENT_CLASS(self.value_no_colors).rindex(*args, **kwargs)
  471 +
  472 + def rjust(self, width, fillchar=None):
  473 + if fillchar is not None:
  474 + result = PARENT_CLASS(self.value_no_colors).rjust(width, fillchar)
  475 + else:
  476 + result = PARENT_CLASS(self.value_no_colors).rjust(width)
  477 + return result.replace(self.value_no_colors, self.value_colors)
  478 +
  479 + def splitlines(self):
  480 + return [Color(l) for l in PARENT_CLASS(self.value_colors).splitlines()]
  481 +
  482 + def startswith(self, *args, **kwargs):
  483 + return PARENT_CLASS(self.value_no_colors).startswith(*args, **kwargs)
  484 +
  485 + def swapcase(self):
  486 + split = _RE_SPLIT.split(self.value_colors)
  487 + for i in range(len(split)):
  488 + if _RE_SPLIT.match(split[i]):
  489 + continue
  490 + split[i] = PARENT_CLASS(split[i]).swapcase()
  491 + return Color().join(split)
  492 +
  493 + def title(self):
  494 + split = _RE_SPLIT.split(self.value_colors)
  495 + for i in range(len(split)):
  496 + if _RE_SPLIT.match(split[i]):
  497 + continue
  498 + split[i] = PARENT_CLASS(split[i]).title()
  499 + return Color().join(split)
  500 +
  501 + def translate(self, table):
  502 + split = _RE_SPLIT.split(self.value_colors)
  503 + for i in range(len(split)):
  504 + if _RE_SPLIT.match(split[i]):
  505 + continue
  506 + split[i] = PARENT_CLASS(split[i]).translate(table)
  507 + return Color().join(split)
  508 +
  509 + def upper(self):
  510 + split = _RE_SPLIT.split(self.value_colors)
  511 + for i in range(len(split)):
  512 + if _RE_SPLIT.match(split[i]):
  513 + continue
  514 + split[i] = PARENT_CLASS(split[i]).upper()
  515 + return Color().join(split)
  516 +
  517 + def zfill(self, width):
  518 + if not self.value_no_colors:
  519 + return PARENT_CLASS().zfill(width)
  520 +
  521 + split = _RE_SPLIT.split(self.value_colors)
  522 + filled = PARENT_CLASS(self.value_no_colors).zfill(width)
  523 + if len(split) == 1:
  524 + return filled
  525 +
  526 + padding = filled.replace(self.value_no_colors, '')
  527 + if not split[0]:
  528 + split[2] = padding + split[2]
  529 + else:
  530 + split[0] = padding + split[0]
  531 +
  532 + return Color().join(split)
  533 +
  534 +
  535 +class Windows(object):
  536 + """Enable and disable Windows support for ANSI color character codes.
  537 +
  538 + Call static method Windows.enable() to enable color support for the remainder of the process' lifetime.
  539 +
  540 + This class is also a context manager. You can do this:
  541 + with Windows():
  542 + print(Color('{autored}Test{/autored}'))
  543 +
  544 + Or this:
  545 + with Windows(auto_colors=True):
  546 + print(Color('{autored}Test{/autored}'))
  547 + """
  548 +
  549 + @staticmethod
  550 + def disable():
  551 + """Restore sys.stderr and sys.stdout to their original objects. Resets colors to their original values."""
  552 + if os.name != 'nt' or not Windows.is_enabled():
  553 + return False
  554 +
  555 + getattr(sys.stderr, '_reset_colors', lambda: False)()
  556 + getattr(sys.stdout, '_reset_colors', lambda: False)()
  557 +
  558 + if hasattr(sys.stderr, 'ORIGINAL_STREAM'):
  559 + sys.stderr = getattr(sys.stderr, 'ORIGINAL_STREAM')
  560 + if hasattr(sys.stdout, 'ORIGINAL_STREAM'):
  561 + sys.stdout = getattr(sys.stdout, 'ORIGINAL_STREAM')
  562 +
  563 + return True
  564 +
  565 + @staticmethod
  566 + def is_enabled():
  567 + """Returns True if either stderr or stdout has colors enabled."""
  568 + return hasattr(sys.stderr, 'ORIGINAL_STREAM') or hasattr(sys.stdout, 'ORIGINAL_STREAM')
  569 +
  570 + @staticmethod
  571 + def enable(auto_colors=False, reset_atexit=False):
  572 + """Enables color text with print() or sys.stdout.write() (stderr too).
  573 +
  574 + Keyword arguments:
  575 + auto_colors -- automatically selects dark or light colors based on current terminal's background color. Only
  576 + works with {autored} and related tags.
  577 + reset_atexit -- resets original colors upon Python exit (in case you forget to reset it yourself with a closing
  578 + tag).
  579 + """
  580 + if os.name != 'nt':
  581 + return False
  582 +
  583 + # Overwrite stream references.
  584 + if not hasattr(sys.stderr, 'ORIGINAL_STREAM'):
  585 + sys.stderr.flush()
  586 + sys.stderr = _WindowsStreamStdErr()
  587 + if not hasattr(sys.stdout, 'ORIGINAL_STREAM'):
  588 + sys.stdout.flush()
  589 + sys.stdout = _WindowsStreamStdOut()
  590 + if not hasattr(sys.stderr, 'ORIGINAL_STREAM') and not hasattr(sys.stdout, 'ORIGINAL_STREAM'):
  591 + return False
  592 +
  593 + # Automatically select which colors to display.
  594 + bg_color = getattr(sys.stdout, 'default_bg', getattr(sys.stderr, 'default_bg', None))
  595 + if auto_colors and bg_color is not None:
  596 + set_light_background() if bg_color in (112, 96, 240, 176, 224, 208, 160) else set_dark_background()
  597 +
  598 + # Reset on exit if requested.
  599 + if reset_atexit:
  600 + atexit.register(lambda: Windows.disable())
  601 +
  602 + return True
  603 +
  604 + def __init__(self, auto_colors=False):
  605 + self.auto_colors = auto_colors
  606 +
  607 + def __enter__(self):
  608 + Windows.enable(auto_colors=self.auto_colors)
  609 +
  610 + def __exit__(self, *_):
  611 + Windows.disable()
  612 +
  613 +
  614 +class _WindowsCSBI(object):
  615 + """Interfaces with Windows CONSOLE_SCREEN_BUFFER_INFO API/DLL calls. Gets info for stderr and stdout.
  616 +
  617 + References:
  618 + http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231
  619 + https://code.google.com/p/colorama/issues/detail?id=47.
  620 + pytest's py project: py/_io/terminalwriter.py.
  621 +
  622 + Class variables:
  623 + CSBI -- ConsoleScreenBufferInfo class/struct (not instance, the class definition itself) defined in _define_csbi().
  624 + HANDLE_STDERR -- GetStdHandle() return integer for stderr.
  625 + HANDLE_STDOUT -- GetStdHandle() return integer for stdout.
  626 + WINDLL -- my own loaded instance of ctypes.WinDLL.
  627 + """
  628 +
  629 + CSBI = None
  630 + HANDLE_STDERR = None
  631 + HANDLE_STDOUT = None
  632 + WINDLL = ctypes.LibraryLoader(getattr(ctypes, 'WinDLL', None))
  633 +
  634 + @staticmethod
  635 + def _define_csbi():
  636 + """Defines structs and populates _WindowsCSBI.CSBI."""
  637 + if _WindowsCSBI.CSBI is not None:
  638 + return
  639 +
  640 + class COORD(ctypes.Structure):
  641 + """Windows COORD structure. http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119"""
  642 + _fields_ = [('X', ctypes.c_short), ('Y', ctypes.c_short)]
  643 +
  644 + class SmallRECT(ctypes.Structure):
  645 + """Windows SMALL_RECT structure. http://msdn.microsoft.com/en-us/library/windows/desktop/ms686311"""
  646 + _fields_ = [('Left', ctypes.c_short), ('Top', ctypes.c_short), ('Right', ctypes.c_short),
  647 + ('Bottom', ctypes.c_short)]
  648 +
  649 + class ConsoleScreenBufferInfo(ctypes.Structure):
  650 + """Windows CONSOLE_SCREEN_BUFFER_INFO structure.
  651 + http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093
  652 + """
  653 + _fields_ = [
  654 + ('dwSize', COORD),
  655 + ('dwCursorPosition', COORD),
  656 + ('wAttributes', ctypes.wintypes.WORD),
  657 + ('srWindow', SmallRECT),
  658 + ('dwMaximumWindowSize', COORD)
  659 + ]
  660 +
  661 + _WindowsCSBI.CSBI = ConsoleScreenBufferInfo
  662 +
  663 + @staticmethod
  664 + def initialize():
  665 + """Initializes the WINDLL resource and populated the CSBI class variable."""
  666 + _WindowsCSBI._define_csbi()
  667 + _WindowsCSBI.HANDLE_STDERR = _WindowsCSBI.HANDLE_STDERR or _WindowsCSBI.WINDLL.kernel32.GetStdHandle(-12)
  668 + _WindowsCSBI.HANDLE_STDOUT = _WindowsCSBI.HANDLE_STDOUT or _WindowsCSBI.WINDLL.kernel32.GetStdHandle(-11)
  669 + if _WindowsCSBI.WINDLL.kernel32.GetConsoleScreenBufferInfo.argtypes:
  670 + return
  671 +
  672 + _WindowsCSBI.WINDLL.kernel32.GetStdHandle.argtypes = [ctypes.wintypes.DWORD]
  673 + _WindowsCSBI.WINDLL.kernel32.GetStdHandle.restype = ctypes.wintypes.HANDLE
  674 + _WindowsCSBI.WINDLL.kernel32.GetConsoleScreenBufferInfo.restype = ctypes.wintypes.BOOL
  675 + _WindowsCSBI.WINDLL.kernel32.GetConsoleScreenBufferInfo.argtypes = [
  676 + ctypes.wintypes.HANDLE, ctypes.POINTER(_WindowsCSBI.CSBI)
  677 + ]
  678 +
  679 + @staticmethod
  680 + def get_info(handle):
  681 + """Get information about this current console window (for Microsoft Windows only).
  682 +
  683 + Raises IOError if attempt to get information fails (if there is no console window).
  684 +
  685 + Don't forget to call _WindowsCSBI.initialize() once in your application before calling this method.
  686 +
  687 + Positional arguments:
  688 + handle -- either _WindowsCSBI.HANDLE_STDERR or _WindowsCSBI.HANDLE_STDOUT.
  689 +
  690 + Returns:
  691 + Dictionary with different integer values. Keys are:
  692 + buffer_width -- width of the buffer (Screen Buffer Size in cmd.exe layout tab).
  693 + buffer_height -- height of the buffer (Screen Buffer Size in cmd.exe layout tab).
  694 + terminal_width -- width of the terminal window.
  695 + terminal_height -- height of the terminal window.
  696 + bg_color -- current background color (http://msdn.microsoft.com/en-us/library/windows/desktop/ms682088).
  697 + fg_color -- current text color code.
  698 + """
  699 + # Query Win32 API.
  700 + csbi = _WindowsCSBI.CSBI()
  701 + try:
  702 + if not _WindowsCSBI.WINDLL.kernel32.GetConsoleScreenBufferInfo(handle, ctypes.byref(csbi)):
  703 + raise IOError('Unable to get console screen buffer info from win32 API.')
  704 + except ctypes.ArgumentError:
  705 + raise IOError('Unable to get console screen buffer info from win32 API.')
  706 +
  707 + # Parse data.
  708 + result = dict(
  709 + buffer_width=int(csbi.dwSize.X - 1),
  710 + buffer_height=int(csbi.dwSize.Y),
  711 + terminal_width=int(csbi.srWindow.Right - csbi.srWindow.Left),
  712 + terminal_height=int(csbi.srWindow.Bottom - csbi.srWindow.Top),
  713 + bg_color=int(csbi.wAttributes & 240),
  714 + fg_color=int(csbi.wAttributes % 16),
  715 + )
  716 + return result
  717 +
  718 +
  719 +class _WindowsStreamStdOut(object):
  720 + """Replacement stream which overrides sys.stdout. When writing or printing, ANSI codes are converted.
  721 +
  722 + ANSI (Linux/Unix) color codes are converted into win32 system calls, changing the next character's color before
  723 + printing it. Resources referenced:
  724 + https://github.com/tartley/colorama
  725 + http://www.cplusplus.com/articles/2ywTURfi/
  726 + http://thomasfischer.biz/python-and-windows-terminal-colors/
  727 + http://stackoverflow.com/questions/17125440/c-win32-console-color
  728 + http://www.tysos.org/svn/trunk/mono/corlib/System/WindowsConsoleDriver.cs
  729 + http://stackoverflow.com/questions/287871/print-in-terminal-with-colors-using-python
  730 + http://msdn.microsoft.com/en-us/library/windows/desktop/ms682088#_win32_character_attributes
  731 +
  732 + Class variables:
  733 + ALL_BG_CODES -- list of background Windows codes. Used to determine if requested color is foreground or background.
  734 + COMPILED_CODES -- 'translation' dictionary. Keys are ANSI codes (values of _BASE_CODES), values are Windows codes.
  735 + ORIGINAL_STREAM -- the original stream to write non-code text to.
  736 + WIN32_STREAM_HANDLE -- handle to the Windows stdout device. Used by other Windows functions.
  737 +
  738 + Instance variables:
  739 + default_fg -- the foreground Windows color code at the time of instantiation.
  740 + default_bg -- the background Windows color code at the time of instantiation.
  741 + """
  742 +
  743 + ALL_BG_CODES = [v for k, v in _WINDOWS_CODES.items() if k.startswith('bg') or k.startswith('hibg')]
  744 + COMPILED_CODES = dict((v, _WINDOWS_CODES[k]) for k, v in _BASE_CODES.items() if k in _WINDOWS_CODES)
  745 + ORIGINAL_STREAM = sys.stdout
  746 + WIN32_STREAM_HANDLE = _WindowsCSBI.HANDLE_STDOUT
  747 +
  748 + def __init__(self):
  749 + _WindowsCSBI.initialize()
  750 + self.default_fg, self.default_bg = self._get_colors()
  751 + for attr in dir(self.ORIGINAL_STREAM):
  752 + if hasattr(self, attr):
  753 + continue
  754 + setattr(self, attr, getattr(self.ORIGINAL_STREAM, attr))
  755 +
  756 + def __getattr__(self, item):
  757 + """If an attribute/function/etc is not defined in this function, retrieve the one from the original stream.
  758 +
  759 + Fixes ipython arrow key presses.
  760 + """
  761 + return getattr(self.ORIGINAL_STREAM, item)
  762 +
  763 + def _get_colors(self):
  764 + """Returns a tuple of two integers representing current colors: (foreground, background)."""
  765 + try:
  766 + csbi = _WindowsCSBI.get_info(self.WIN32_STREAM_HANDLE)
  767 + return csbi['fg_color'], csbi['bg_color']
  768 + except IOError:
  769 + return 7, 0
  770 +
  771 + def _reset_colors(self):
  772 + """Sets the foreground and background colors to their original values (when class was instantiated)."""
  773 + self._set_color(-33)
  774 +
  775 + def _set_color(self, color_code):
  776 + """Changes the foreground and background colors for subsequently printed characters.
  777 +
  778 + Since setting a color requires including both foreground and background codes (merged), setting just the
  779 + foreground color resets the background color to black, and vice versa.
  780 +
  781 + This function first gets the current background and foreground colors, merges in the requested color code, and
  782 + sets the result.
  783 +
  784 + However if we need to remove just the foreground color but leave the background color the same (or vice versa)
  785 + such as when {/red} is used, we must merge the default foreground color with the current background color. This
  786 + is the reason for those negative values.
  787 +
  788 + Positional arguments:
  789 + color_code -- integer color code from _WINDOWS_CODES.
  790 + """
  791 + # Get current color code.
  792 + current_fg, current_bg = self._get_colors()
  793 +
  794 + # Handle special negative codes. Also determine the final color code.
  795 + if color_code == -39:
  796 + final_color_code = self.default_fg | current_bg # Reset the foreground only.
  797 + elif color_code == -49:
  798 + final_color_code = current_fg | self.default_bg # Reset the background only.
  799 + elif color_code == -33:
  800 + final_color_code = self.default_fg | self.default_bg # Reset both.
  801 + elif color_code == -8:
  802 + final_color_code = current_fg # Black background.
  803 + else:
  804 + new_is_bg = color_code in self.ALL_BG_CODES
  805 + final_color_code = color_code | (current_fg if new_is_bg else current_bg)
  806 +
  807 + # Set new code.
  808 + _WindowsCSBI.WINDLL.kernel32.SetConsoleTextAttribute(self.WIN32_STREAM_HANDLE, final_color_code)
  809 +
  810 + def write(self, p_str):
  811 + for segment in _RE_SPLIT.split(p_str):
  812 + if not segment:
  813 + # Empty string. p_str probably starts with colors so the first item is always ''.
  814 + continue
  815 + if not _RE_SPLIT.match(segment):
  816 + # No color codes, print regular text.
  817 + self.ORIGINAL_STREAM.write(segment)
  818 + self.ORIGINAL_STREAM.flush()
  819 + continue
  820 + for color_code in (int(c) for c in _RE_NUMBER_SEARCH.findall(segment)[0].split(';')):
  821 + if color_code in self.COMPILED_CODES:
  822 + self._set_color(self.COMPILED_CODES[color_code])
  823 +
  824 +
  825 +class _WindowsStreamStdErr(_WindowsStreamStdOut):
  826 + """Replacement stream which overrides sys.stderr. Subclasses _WindowsStreamStdOut."""
  827 +
  828 + ORIGINAL_STREAM = sys.stderr
  829 + WIN32_STREAM_HANDLE = _WindowsCSBI.HANDLE_STDERR
... ...