Commit 5f8e0b8842692eeb83879771df36384ce7d2d10d
1 parent
f3d51f29
updated colorclass to v2.2.0, updated tablestream
Showing
11 changed files
with
1440 additions
and
831 deletions
oletools/thirdparty/colorclass/__init__.py
| 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 | +from colorclass.codes import list_tags # noqa | ||
| 12 | +from colorclass.color import Color # noqa | ||
| 13 | +from colorclass.toggles import disable_all_colors # noqa | ||
| 14 | +from colorclass.toggles import disable_if_no_tty # noqa | ||
| 15 | +from colorclass.toggles import enable_all_colors # noqa | ||
| 16 | +from colorclass.toggles import is_enabled # noqa | ||
| 17 | +from colorclass.toggles import is_light # noqa | ||
| 18 | +from colorclass.toggles import set_dark_background # noqa | ||
| 19 | +from colorclass.toggles import set_light_background # noqa | ||
| 20 | +from colorclass.windows import Windows # noqa | ||
| 21 | + | ||
| 22 | + | ||
| 23 | +__all__ = ( | ||
| 24 | + 'Color', | ||
| 25 | + 'disable_all_colors', | ||
| 26 | + 'enable_all_colors', | ||
| 27 | + 'is_enabled', | ||
| 28 | + 'is_light', | ||
| 29 | + 'list_tags', | ||
| 30 | + 'set_dark_background', | ||
| 31 | + 'set_light_background', | ||
| 32 | + 'Windows', | ||
| 33 | +) | ||
| 34 | + | ||
| 35 | + | ||
| 36 | +__author__ = '@Robpol86' | ||
| 37 | +__license__ = 'MIT' | ||
| 38 | +__version__ = '2.2.0' |
oletools/thirdparty/colorclass/__main__.py
0 → 100644
| 1 | +"""Called by "python -m". Allows package to be used as a script. | ||
| 2 | + | ||
| 3 | +Example usage: | ||
| 4 | +echo "{red}Red{/red}" |python -m colorclass | ||
| 5 | +""" | ||
| 6 | + | ||
| 7 | +from __future__ import print_function | ||
| 8 | + | ||
| 9 | +import fileinput | ||
| 10 | +import os | ||
| 11 | + | ||
| 12 | +from colorclass.color import Color | ||
| 13 | +from colorclass.toggles import disable_all_colors | ||
| 14 | +from colorclass.toggles import enable_all_colors | ||
| 15 | +from colorclass.toggles import set_dark_background | ||
| 16 | +from colorclass.toggles import set_light_background | ||
| 17 | +from colorclass.windows import Windows | ||
| 18 | + | ||
| 19 | +TRUTHY = ('true', '1', 'yes', 'on') | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +if __name__ == '__main__': | ||
| 23 | + if os.environ.get('COLOR_ENABLE', '').lower() in TRUTHY: | ||
| 24 | + enable_all_colors() | ||
| 25 | + elif os.environ.get('COLOR_DISABLE', '').lower() in TRUTHY: | ||
| 26 | + disable_all_colors() | ||
| 27 | + if os.environ.get('COLOR_LIGHT', '').lower() in TRUTHY: | ||
| 28 | + set_light_background() | ||
| 29 | + elif os.environ.get('COLOR_DARK', '').lower() in TRUTHY: | ||
| 30 | + set_dark_background() | ||
| 31 | + Windows.enable() | ||
| 32 | + for LINE in fileinput.input(): | ||
| 33 | + print(Color(LINE)) |
oletools/thirdparty/colorclass/codes.py
0 → 100644
| 1 | +"""Handles mapping between color names and ANSI codes and determining auto color codes.""" | ||
| 2 | + | ||
| 3 | +import sys | ||
| 4 | +from collections import Mapping | ||
| 5 | + | ||
| 6 | +BASE_CODES = { | ||
| 7 | + '/all': 0, 'b': 1, 'f': 2, 'i': 3, 'u': 4, 'flash': 5, 'outline': 6, 'negative': 7, 'invis': 8, 'strike': 9, | ||
| 8 | + '/b': 22, '/f': 22, '/i': 23, '/u': 24, '/flash': 25, '/outline': 26, '/negative': 27, '/invis': 28, | ||
| 9 | + '/strike': 29, '/fg': 39, '/bg': 49, | ||
| 10 | + | ||
| 11 | + 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, | ||
| 12 | + | ||
| 13 | + 'bgblack': 40, 'bgred': 41, 'bggreen': 42, 'bgyellow': 43, 'bgblue': 44, 'bgmagenta': 45, 'bgcyan': 46, | ||
| 14 | + 'bgwhite': 47, | ||
| 15 | + | ||
| 16 | + 'hiblack': 90, 'hired': 91, 'higreen': 92, 'hiyellow': 93, 'hiblue': 94, 'himagenta': 95, 'hicyan': 96, | ||
| 17 | + 'hiwhite': 97, | ||
| 18 | + | ||
| 19 | + 'hibgblack': 100, 'hibgred': 101, 'hibggreen': 102, 'hibgyellow': 103, 'hibgblue': 104, 'hibgmagenta': 105, | ||
| 20 | + 'hibgcyan': 106, 'hibgwhite': 107, | ||
| 21 | + | ||
| 22 | + 'autored': None, 'autoblack': None, 'automagenta': None, 'autowhite': None, 'autoblue': None, 'autoyellow': None, | ||
| 23 | + 'autogreen': None, 'autocyan': None, | ||
| 24 | + | ||
| 25 | + 'autobgred': None, 'autobgblack': None, 'autobgmagenta': None, 'autobgwhite': None, 'autobgblue': None, | ||
| 26 | + 'autobgyellow': None, 'autobggreen': None, 'autobgcyan': None, | ||
| 27 | + | ||
| 28 | + '/black': 39, '/red': 39, '/green': 39, '/yellow': 39, '/blue': 39, '/magenta': 39, '/cyan': 39, '/white': 39, | ||
| 29 | + '/hiblack': 39, '/hired': 39, '/higreen': 39, '/hiyellow': 39, '/hiblue': 39, '/himagenta': 39, '/hicyan': 39, | ||
| 30 | + '/hiwhite': 39, | ||
| 31 | + | ||
| 32 | + '/bgblack': 49, '/bgred': 49, '/bggreen': 49, '/bgyellow': 49, '/bgblue': 49, '/bgmagenta': 49, '/bgcyan': 49, | ||
| 33 | + '/bgwhite': 49, '/hibgblack': 49, '/hibgred': 49, '/hibggreen': 49, '/hibgyellow': 49, '/hibgblue': 49, | ||
| 34 | + '/hibgmagenta': 49, '/hibgcyan': 49, '/hibgwhite': 49, | ||
| 35 | + | ||
| 36 | + '/autored': 39, '/autoblack': 39, '/automagenta': 39, '/autowhite': 39, '/autoblue': 39, '/autoyellow': 39, | ||
| 37 | + '/autogreen': 39, '/autocyan': 39, | ||
| 38 | + | ||
| 39 | + '/autobgred': 49, '/autobgblack': 49, '/autobgmagenta': 49, '/autobgwhite': 49, '/autobgblue': 49, | ||
| 40 | + '/autobgyellow': 49, '/autobggreen': 49, '/autobgcyan': 49, | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | + | ||
| 44 | +class ANSICodeMapping(Mapping): | ||
| 45 | + """Read-only dictionary, resolves closing tags and automatic colors. Iterates only used color tags. | ||
| 46 | + | ||
| 47 | + :cvar bool DISABLE_COLORS: Disable colors (strip color codes). | ||
| 48 | + :cvar bool LIGHT_BACKGROUND: Use low intensity color codes. | ||
| 49 | + """ | ||
| 50 | + | ||
| 51 | + DISABLE_COLORS = False | ||
| 52 | + LIGHT_BACKGROUND = False | ||
| 53 | + | ||
| 54 | + def __init__(self, value_markup): | ||
| 55 | + """Constructor. | ||
| 56 | + | ||
| 57 | + :param str value_markup: String with {color} tags. | ||
| 58 | + """ | ||
| 59 | + self.whitelist = [k for k in BASE_CODES if '{' + k + '}' in value_markup] | ||
| 60 | + | ||
| 61 | + def __getitem__(self, item): | ||
| 62 | + """Return value for key or None if colors are disabled. | ||
| 63 | + | ||
| 64 | + :param str item: Key. | ||
| 65 | + | ||
| 66 | + :return: Color code integer. | ||
| 67 | + :rtype: int | ||
| 68 | + """ | ||
| 69 | + if item not in self.whitelist: | ||
| 70 | + raise KeyError(item) | ||
| 71 | + if self.DISABLE_COLORS: | ||
| 72 | + return None | ||
| 73 | + return getattr(self, item, BASE_CODES[item]) | ||
| 74 | + | ||
| 75 | + def __iter__(self): | ||
| 76 | + """Iterate dictionary.""" | ||
| 77 | + return iter(self.whitelist) | ||
| 78 | + | ||
| 79 | + def __len__(self): | ||
| 80 | + """Dictionary length.""" | ||
| 81 | + return len(self.whitelist) | ||
| 82 | + | ||
| 83 | + @classmethod | ||
| 84 | + def disable_all_colors(cls): | ||
| 85 | + """Disable all colors. Strips any color tags or codes.""" | ||
| 86 | + cls.DISABLE_COLORS = True | ||
| 87 | + | ||
| 88 | + @classmethod | ||
| 89 | + def enable_all_colors(cls): | ||
| 90 | + """Enable all colors. Strips any color tags or codes.""" | ||
| 91 | + cls.DISABLE_COLORS = False | ||
| 92 | + | ||
| 93 | + @classmethod | ||
| 94 | + def disable_if_no_tty(cls): | ||
| 95 | + """Disable all colors only if there is no TTY available. | ||
| 96 | + | ||
| 97 | + :return: True if colors are disabled, False if stderr or stdout is a TTY. | ||
| 98 | + :rtype: bool | ||
| 99 | + """ | ||
| 100 | + if sys.stdout.isatty() or sys.stderr.isatty(): | ||
| 101 | + return False | ||
| 102 | + cls.disable_all_colors() | ||
| 103 | + return True | ||
| 104 | + | ||
| 105 | + @classmethod | ||
| 106 | + def set_dark_background(cls): | ||
| 107 | + """Choose dark colors for all 'auto'-prefixed codes for readability on light backgrounds.""" | ||
| 108 | + cls.LIGHT_BACKGROUND = False | ||
| 109 | + | ||
| 110 | + @classmethod | ||
| 111 | + def set_light_background(cls): | ||
| 112 | + """Choose dark colors for all 'auto'-prefixed codes for readability on light backgrounds.""" | ||
| 113 | + cls.LIGHT_BACKGROUND = True | ||
| 114 | + | ||
| 115 | + @property | ||
| 116 | + def autoblack(self): | ||
| 117 | + """Return automatic black foreground color depending on background color.""" | ||
| 118 | + return BASE_CODES['black' if ANSICodeMapping.LIGHT_BACKGROUND else 'hiblack'] | ||
| 119 | + | ||
| 120 | + @property | ||
| 121 | + def autored(self): | ||
| 122 | + """Return automatic red foreground color depending on background color.""" | ||
| 123 | + return BASE_CODES['red' if ANSICodeMapping.LIGHT_BACKGROUND else 'hired'] | ||
| 124 | + | ||
| 125 | + @property | ||
| 126 | + def autogreen(self): | ||
| 127 | + """Return automatic green foreground color depending on background color.""" | ||
| 128 | + return BASE_CODES['green' if ANSICodeMapping.LIGHT_BACKGROUND else 'higreen'] | ||
| 129 | + | ||
| 130 | + @property | ||
| 131 | + def autoyellow(self): | ||
| 132 | + """Return automatic yellow foreground color depending on background color.""" | ||
| 133 | + return BASE_CODES['yellow' if ANSICodeMapping.LIGHT_BACKGROUND else 'hiyellow'] | ||
| 134 | + | ||
| 135 | + @property | ||
| 136 | + def autoblue(self): | ||
| 137 | + """Return automatic blue foreground color depending on background color.""" | ||
| 138 | + return BASE_CODES['blue' if ANSICodeMapping.LIGHT_BACKGROUND else 'hiblue'] | ||
| 139 | + | ||
| 140 | + @property | ||
| 141 | + def automagenta(self): | ||
| 142 | + """Return automatic magenta foreground color depending on background color.""" | ||
| 143 | + return BASE_CODES['magenta' if ANSICodeMapping.LIGHT_BACKGROUND else 'himagenta'] | ||
| 144 | + | ||
| 145 | + @property | ||
| 146 | + def autocyan(self): | ||
| 147 | + """Return automatic cyan foreground color depending on background color.""" | ||
| 148 | + return BASE_CODES['cyan' if ANSICodeMapping.LIGHT_BACKGROUND else 'hicyan'] | ||
| 149 | + | ||
| 150 | + @property | ||
| 151 | + def autowhite(self): | ||
| 152 | + """Return automatic white foreground color depending on background color.""" | ||
| 153 | + return BASE_CODES['white' if ANSICodeMapping.LIGHT_BACKGROUND else 'hiwhite'] | ||
| 154 | + | ||
| 155 | + @property | ||
| 156 | + def autobgblack(self): | ||
| 157 | + """Return automatic black background color depending on background color.""" | ||
| 158 | + return BASE_CODES['bgblack' if ANSICodeMapping.LIGHT_BACKGROUND else 'hibgblack'] | ||
| 159 | + | ||
| 160 | + @property | ||
| 161 | + def autobgred(self): | ||
| 162 | + """Return automatic red background color depending on background color.""" | ||
| 163 | + return BASE_CODES['bgred' if ANSICodeMapping.LIGHT_BACKGROUND else 'hibgred'] | ||
| 164 | + | ||
| 165 | + @property | ||
| 166 | + def autobggreen(self): | ||
| 167 | + """Return automatic green background color depending on background color.""" | ||
| 168 | + return BASE_CODES['bggreen' if ANSICodeMapping.LIGHT_BACKGROUND else 'hibggreen'] | ||
| 169 | + | ||
| 170 | + @property | ||
| 171 | + def autobgyellow(self): | ||
| 172 | + """Return automatic yellow background color depending on background color.""" | ||
| 173 | + return BASE_CODES['bgyellow' if ANSICodeMapping.LIGHT_BACKGROUND else 'hibgyellow'] | ||
| 174 | + | ||
| 175 | + @property | ||
| 176 | + def autobgblue(self): | ||
| 177 | + """Return automatic blue background color depending on background color.""" | ||
| 178 | + return BASE_CODES['bgblue' if ANSICodeMapping.LIGHT_BACKGROUND else 'hibgblue'] | ||
| 179 | + | ||
| 180 | + @property | ||
| 181 | + def autobgmagenta(self): | ||
| 182 | + """Return automatic magenta background color depending on background color.""" | ||
| 183 | + return BASE_CODES['bgmagenta' if ANSICodeMapping.LIGHT_BACKGROUND else 'hibgmagenta'] | ||
| 184 | + | ||
| 185 | + @property | ||
| 186 | + def autobgcyan(self): | ||
| 187 | + """Return automatic cyan background color depending on background color.""" | ||
| 188 | + return BASE_CODES['bgcyan' if ANSICodeMapping.LIGHT_BACKGROUND else 'hibgcyan'] | ||
| 189 | + | ||
| 190 | + @property | ||
| 191 | + def autobgwhite(self): | ||
| 192 | + """Return automatic white background color depending on background color.""" | ||
| 193 | + return BASE_CODES['bgwhite' if ANSICodeMapping.LIGHT_BACKGROUND else 'hibgwhite'] | ||
| 194 | + | ||
| 195 | + | ||
| 196 | +def list_tags(): | ||
| 197 | + """List the available tags. | ||
| 198 | + | ||
| 199 | + :return: List of 4-item tuples: opening tag, closing tag, main ansi value, closing ansi value. | ||
| 200 | + :rtype: list | ||
| 201 | + """ | ||
| 202 | + # Build reverse dictionary. Keys are closing tags, values are [closing ansi, opening tag, opening ansi]. | ||
| 203 | + reverse_dict = dict() | ||
| 204 | + for tag, ansi in sorted(BASE_CODES.items()): | ||
| 205 | + if tag.startswith('/'): | ||
| 206 | + reverse_dict[tag] = [ansi, None, None] | ||
| 207 | + else: | ||
| 208 | + reverse_dict['/' + tag][1:] = [tag, ansi] | ||
| 209 | + | ||
| 210 | + # Collapse | ||
| 211 | + four_item_tuples = [(v[1], k, v[2], v[0]) for k, v in reverse_dict.items()] | ||
| 212 | + | ||
| 213 | + # Sort. | ||
| 214 | + def sorter(four_item): | ||
| 215 | + """Sort /all /fg /bg first, then b i u flash, then auto colors, then dark colors, finally light colors. | ||
| 216 | + | ||
| 217 | + :param iter four_item: [opening tag, closing tag, main ansi value, closing ansi value] | ||
| 218 | + | ||
| 219 | + :return Sorting weight. | ||
| 220 | + :rtype: int | ||
| 221 | + """ | ||
| 222 | + if not four_item[2]: # /all /fg /bg | ||
| 223 | + return four_item[3] - 200 | ||
| 224 | + if four_item[2] < 10 or four_item[0].startswith('auto'): # b f i u or auto colors | ||
| 225 | + return four_item[2] - 100 | ||
| 226 | + return four_item[2] | ||
| 227 | + four_item_tuples.sort(key=sorter) | ||
| 228 | + | ||
| 229 | + return four_item_tuples |
oletools/thirdparty/colorclass/color.py
0 → 100644
| 1 | +"""Color class used by library users.""" | ||
| 2 | + | ||
| 3 | +from colorclass.core import ColorStr | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +class Color(ColorStr): | ||
| 7 | + """Unicode (str in Python3) subclass with ANSI terminal text color support. | ||
| 8 | + | ||
| 9 | + Example syntax: Color('{red}Sample Text{/red}') | ||
| 10 | + | ||
| 11 | + Example without parsing logic: Color('{red}Sample Text{/red}', keep_tags=True) | ||
| 12 | + | ||
| 13 | + For a list of codes, call: colorclass.list_tags() | ||
| 14 | + """ | ||
| 15 | + | ||
| 16 | + @classmethod | ||
| 17 | + def colorize(cls, color, string, auto=False): | ||
| 18 | + """Color-code entire string using specified color. | ||
| 19 | + | ||
| 20 | + :param str color: Color of string. | ||
| 21 | + :param str string: String to colorize. | ||
| 22 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 23 | + | ||
| 24 | + :return: Class instance for colorized string. | ||
| 25 | + :rtype: Color | ||
| 26 | + """ | ||
| 27 | + tag = '{0}{1}'.format('auto' if auto else '', color) | ||
| 28 | + return cls('{%s}%s{/%s}' % (tag, string, tag)) | ||
| 29 | + | ||
| 30 | + @classmethod | ||
| 31 | + def black(cls, string, auto=False): | ||
| 32 | + """Color-code entire string. | ||
| 33 | + | ||
| 34 | + :param str string: String to colorize. | ||
| 35 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 36 | + | ||
| 37 | + :return: Class instance for colorized string. | ||
| 38 | + :rtype: Color | ||
| 39 | + """ | ||
| 40 | + return cls.colorize('black', string, auto=auto) | ||
| 41 | + | ||
| 42 | + @classmethod | ||
| 43 | + def bgblack(cls, string, auto=False): | ||
| 44 | + """Color-code entire string. | ||
| 45 | + | ||
| 46 | + :param str string: String to colorize. | ||
| 47 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 48 | + | ||
| 49 | + :return: Class instance for colorized string. | ||
| 50 | + :rtype: Color | ||
| 51 | + """ | ||
| 52 | + return cls.colorize('bgblack', string, auto=auto) | ||
| 53 | + | ||
| 54 | + @classmethod | ||
| 55 | + def red(cls, string, auto=False): | ||
| 56 | + """Color-code entire string. | ||
| 57 | + | ||
| 58 | + :param str string: String to colorize. | ||
| 59 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 60 | + | ||
| 61 | + :return: Class instance for colorized string. | ||
| 62 | + :rtype: Color | ||
| 63 | + """ | ||
| 64 | + return cls.colorize('red', string, auto=auto) | ||
| 65 | + | ||
| 66 | + @classmethod | ||
| 67 | + def bgred(cls, string, auto=False): | ||
| 68 | + """Color-code entire string. | ||
| 69 | + | ||
| 70 | + :param str string: String to colorize. | ||
| 71 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 72 | + | ||
| 73 | + :return: Class instance for colorized string. | ||
| 74 | + :rtype: Color | ||
| 75 | + """ | ||
| 76 | + return cls.colorize('bgred', string, auto=auto) | ||
| 77 | + | ||
| 78 | + @classmethod | ||
| 79 | + def green(cls, string, auto=False): | ||
| 80 | + """Color-code entire string. | ||
| 81 | + | ||
| 82 | + :param str string: String to colorize. | ||
| 83 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 84 | + | ||
| 85 | + :return: Class instance for colorized string. | ||
| 86 | + :rtype: Color | ||
| 87 | + """ | ||
| 88 | + return cls.colorize('green', string, auto=auto) | ||
| 89 | + | ||
| 90 | + @classmethod | ||
| 91 | + def bggreen(cls, string, auto=False): | ||
| 92 | + """Color-code entire string. | ||
| 93 | + | ||
| 94 | + :param str string: String to colorize. | ||
| 95 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 96 | + | ||
| 97 | + :return: Class instance for colorized string. | ||
| 98 | + :rtype: Color | ||
| 99 | + """ | ||
| 100 | + return cls.colorize('bggreen', string, auto=auto) | ||
| 101 | + | ||
| 102 | + @classmethod | ||
| 103 | + def yellow(cls, string, auto=False): | ||
| 104 | + """Color-code entire string. | ||
| 105 | + | ||
| 106 | + :param str string: String to colorize. | ||
| 107 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 108 | + | ||
| 109 | + :return: Class instance for colorized string. | ||
| 110 | + :rtype: Color | ||
| 111 | + """ | ||
| 112 | + return cls.colorize('yellow', string, auto=auto) | ||
| 113 | + | ||
| 114 | + @classmethod | ||
| 115 | + def bgyellow(cls, string, auto=False): | ||
| 116 | + """Color-code entire string. | ||
| 117 | + | ||
| 118 | + :param str string: String to colorize. | ||
| 119 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 120 | + | ||
| 121 | + :return: Class instance for colorized string. | ||
| 122 | + :rtype: Color | ||
| 123 | + """ | ||
| 124 | + return cls.colorize('bgyellow', string, auto=auto) | ||
| 125 | + | ||
| 126 | + @classmethod | ||
| 127 | + def blue(cls, string, auto=False): | ||
| 128 | + """Color-code entire string. | ||
| 129 | + | ||
| 130 | + :param str string: String to colorize. | ||
| 131 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 132 | + | ||
| 133 | + :return: Class instance for colorized string. | ||
| 134 | + :rtype: Color | ||
| 135 | + """ | ||
| 136 | + return cls.colorize('blue', string, auto=auto) | ||
| 137 | + | ||
| 138 | + @classmethod | ||
| 139 | + def bgblue(cls, string, auto=False): | ||
| 140 | + """Color-code entire string. | ||
| 141 | + | ||
| 142 | + :param str string: String to colorize. | ||
| 143 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 144 | + | ||
| 145 | + :return: Class instance for colorized string. | ||
| 146 | + :rtype: Color | ||
| 147 | + """ | ||
| 148 | + return cls.colorize('bgblue', string, auto=auto) | ||
| 149 | + | ||
| 150 | + @classmethod | ||
| 151 | + def magenta(cls, string, auto=False): | ||
| 152 | + """Color-code entire string. | ||
| 153 | + | ||
| 154 | + :param str string: String to colorize. | ||
| 155 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 156 | + | ||
| 157 | + :return: Class instance for colorized string. | ||
| 158 | + :rtype: Color | ||
| 159 | + """ | ||
| 160 | + return cls.colorize('magenta', string, auto=auto) | ||
| 161 | + | ||
| 162 | + @classmethod | ||
| 163 | + def bgmagenta(cls, string, auto=False): | ||
| 164 | + """Color-code entire string. | ||
| 165 | + | ||
| 166 | + :param str string: String to colorize. | ||
| 167 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 168 | + | ||
| 169 | + :return: Class instance for colorized string. | ||
| 170 | + :rtype: Color | ||
| 171 | + """ | ||
| 172 | + return cls.colorize('bgmagenta', string, auto=auto) | ||
| 173 | + | ||
| 174 | + @classmethod | ||
| 175 | + def cyan(cls, string, auto=False): | ||
| 176 | + """Color-code entire string. | ||
| 177 | + | ||
| 178 | + :param str string: String to colorize. | ||
| 179 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 180 | + | ||
| 181 | + :return: Class instance for colorized string. | ||
| 182 | + :rtype: Color | ||
| 183 | + """ | ||
| 184 | + return cls.colorize('cyan', string, auto=auto) | ||
| 185 | + | ||
| 186 | + @classmethod | ||
| 187 | + def bgcyan(cls, string, auto=False): | ||
| 188 | + """Color-code entire string. | ||
| 189 | + | ||
| 190 | + :param str string: String to colorize. | ||
| 191 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 192 | + | ||
| 193 | + :return: Class instance for colorized string. | ||
| 194 | + :rtype: Color | ||
| 195 | + """ | ||
| 196 | + return cls.colorize('bgcyan', string, auto=auto) | ||
| 197 | + | ||
| 198 | + @classmethod | ||
| 199 | + def white(cls, string, auto=False): | ||
| 200 | + """Color-code entire string. | ||
| 201 | + | ||
| 202 | + :param str string: String to colorize. | ||
| 203 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 204 | + | ||
| 205 | + :return: Class instance for colorized string. | ||
| 206 | + :rtype: Color | ||
| 207 | + """ | ||
| 208 | + return cls.colorize('white', string, auto=auto) | ||
| 209 | + | ||
| 210 | + @classmethod | ||
| 211 | + def bgwhite(cls, string, auto=False): | ||
| 212 | + """Color-code entire string. | ||
| 213 | + | ||
| 214 | + :param str string: String to colorize. | ||
| 215 | + :param bool auto: Enable auto-color (dark/light terminal). | ||
| 216 | + | ||
| 217 | + :return: Class instance for colorized string. | ||
| 218 | + :rtype: Color | ||
| 219 | + """ | ||
| 220 | + return cls.colorize('bgwhite', string, auto=auto) |
oletools/thirdparty/colorclass/colorclass.py deleted
| 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 |
oletools/thirdparty/colorclass/core.py
0 → 100644
| 1 | +"""String subclass that handles ANSI color codes.""" | ||
| 2 | + | ||
| 3 | +from colorclass.codes import ANSICodeMapping | ||
| 4 | +from colorclass.parse import parse_input, RE_SPLIT | ||
| 5 | +from colorclass.search import build_color_index, find_char_color | ||
| 6 | + | ||
| 7 | +PARENT_CLASS = type(u'') | ||
| 8 | + | ||
| 9 | + | ||
| 10 | +def apply_text(incoming, func): | ||
| 11 | + """Call `func` on text portions of incoming color string. | ||
| 12 | + | ||
| 13 | + :param iter incoming: Incoming string/ColorStr/string-like object to iterate. | ||
| 14 | + :param func: Function to call with string portion as first and only parameter. | ||
| 15 | + | ||
| 16 | + :return: Modified string, same class type as incoming string. | ||
| 17 | + """ | ||
| 18 | + split = RE_SPLIT.split(incoming) | ||
| 19 | + for i, item in enumerate(split): | ||
| 20 | + if not item or RE_SPLIT.match(item): | ||
| 21 | + continue | ||
| 22 | + split[i] = func(item) | ||
| 23 | + return incoming.__class__().join(split) | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +class ColorBytes(bytes): | ||
| 27 | + """Str (bytes in Python3) subclass, .decode() overridden to return unicode (str in Python3) subclass instance.""" | ||
| 28 | + | ||
| 29 | + def __new__(cls, *args, **kwargs): | ||
| 30 | + """Save original class so decode() returns an instance of it.""" | ||
| 31 | + original_class = kwargs.pop('original_class') | ||
| 32 | + combined_args = [cls] + list(args) | ||
| 33 | + instance = bytes.__new__(*combined_args, **kwargs) | ||
| 34 | + instance.original_class = original_class | ||
| 35 | + return instance | ||
| 36 | + | ||
| 37 | + def decode(self, encoding='utf-8', errors='strict'): | ||
| 38 | + """Decode using the codec registered for encoding. Default encoding is 'utf-8'. | ||
| 39 | + | ||
| 40 | + errors may be given to set a different error handling scheme. Default is 'strict' meaning that encoding errors | ||
| 41 | + raise a UnicodeDecodeError. Other possible values are 'ignore' and 'replace' as well as any other name | ||
| 42 | + registered with codecs.register_error that is able to handle UnicodeDecodeErrors. | ||
| 43 | + | ||
| 44 | + :param str encoding: Codec. | ||
| 45 | + :param str errors: Error handling scheme. | ||
| 46 | + """ | ||
| 47 | + original_class = getattr(self, 'original_class') | ||
| 48 | + return original_class(super(ColorBytes, self).decode(encoding, errors)) | ||
| 49 | + | ||
| 50 | + | ||
| 51 | +class ColorStr(PARENT_CLASS): | ||
| 52 | + """Core color class.""" | ||
| 53 | + | ||
| 54 | + def __new__(cls, *args, **kwargs): | ||
| 55 | + """Parse color markup and instantiate.""" | ||
| 56 | + keep_tags = kwargs.pop('keep_tags', False) | ||
| 57 | + | ||
| 58 | + # Parse string. | ||
| 59 | + value_markup = args[0] if args else PARENT_CLASS() # e.g. '{red}test{/red}' | ||
| 60 | + value_colors, value_no_colors = parse_input(value_markup, ANSICodeMapping.DISABLE_COLORS, keep_tags) | ||
| 61 | + color_index = build_color_index(value_colors) | ||
| 62 | + | ||
| 63 | + # Instantiate. | ||
| 64 | + color_args = [cls, value_colors] + list(args[1:]) | ||
| 65 | + instance = PARENT_CLASS.__new__(*color_args, **kwargs) | ||
| 66 | + | ||
| 67 | + # Add additional attributes and return. | ||
| 68 | + instance.value_colors = value_colors | ||
| 69 | + instance.value_no_colors = value_no_colors | ||
| 70 | + instance.has_colors = value_colors != value_no_colors | ||
| 71 | + instance.color_index = color_index | ||
| 72 | + return instance | ||
| 73 | + | ||
| 74 | + def __add__(self, other): | ||
| 75 | + """Concatenate.""" | ||
| 76 | + return self.__class__(self.value_colors + other, keep_tags=True) | ||
| 77 | + | ||
| 78 | + def __getitem__(self, item): | ||
| 79 | + """Retrieve character.""" | ||
| 80 | + try: | ||
| 81 | + color_pos = self.color_index[int(item)] | ||
| 82 | + except TypeError: # slice | ||
| 83 | + return super(ColorStr, self).__getitem__(item) | ||
| 84 | + return self.__class__(find_char_color(self.value_colors, color_pos), keep_tags=True) | ||
| 85 | + | ||
| 86 | + def __iter__(self): | ||
| 87 | + """Yield one color-coded character at a time.""" | ||
| 88 | + for color_pos in self.color_index: | ||
| 89 | + yield self.__class__(find_char_color(self.value_colors, color_pos)) | ||
| 90 | + | ||
| 91 | + def __len__(self): | ||
| 92 | + """Length of string without color codes (what users expect).""" | ||
| 93 | + return self.value_no_colors.__len__() | ||
| 94 | + | ||
| 95 | + def __mod__(self, other): | ||
| 96 | + """String substitution (like printf).""" | ||
| 97 | + return self.__class__(self.value_colors % other, keep_tags=True) | ||
| 98 | + | ||
| 99 | + def __mul__(self, other): | ||
| 100 | + """Multiply string.""" | ||
| 101 | + return self.__class__(self.value_colors * other, keep_tags=True) | ||
| 102 | + | ||
| 103 | + def __repr__(self): | ||
| 104 | + """Representation of a class instance (like datetime.datetime.now()).""" | ||
| 105 | + return '{name}({value})'.format(name=self.__class__.__name__, value=repr(self.value_colors)) | ||
| 106 | + | ||
| 107 | + def capitalize(self): | ||
| 108 | + """Return a copy of the string with only its first character capitalized.""" | ||
| 109 | + return apply_text(self, lambda s: s.capitalize()) | ||
| 110 | + | ||
| 111 | + def center(self, width, fillchar=None): | ||
| 112 | + """Return centered in a string of length width. Padding is done using the specified fill character or space. | ||
| 113 | + | ||
| 114 | + :param int width: Length of output string. | ||
| 115 | + :param str fillchar: Use this character instead of spaces. | ||
| 116 | + """ | ||
| 117 | + if fillchar is not None: | ||
| 118 | + result = self.value_no_colors.center(width, fillchar) | ||
| 119 | + else: | ||
| 120 | + result = self.value_no_colors.center(width) | ||
| 121 | + return self.__class__(result.replace(self.value_no_colors, self.value_colors), keep_tags=True) | ||
| 122 | + | ||
| 123 | + def count(self, sub, start=0, end=-1): | ||
| 124 | + """Return the number of non-overlapping occurrences of substring sub in string[start:end]. | ||
| 125 | + | ||
| 126 | + Optional arguments start and end are interpreted as in slice notation. | ||
| 127 | + | ||
| 128 | + :param str sub: Substring to search. | ||
| 129 | + :param int start: Beginning position. | ||
| 130 | + :param int end: Stop comparison at this position. | ||
| 131 | + """ | ||
| 132 | + return self.value_no_colors.count(sub, start, end) | ||
| 133 | + | ||
| 134 | + def endswith(self, suffix, start=0, end=None): | ||
| 135 | + """Return True if ends with the specified suffix, False otherwise. | ||
| 136 | + | ||
| 137 | + With optional start, test beginning at that position. With optional end, stop comparing at that position. | ||
| 138 | + suffix can also be a tuple of strings to try. | ||
| 139 | + | ||
| 140 | + :param str suffix: Suffix to search. | ||
| 141 | + :param int start: Beginning position. | ||
| 142 | + :param int end: Stop comparison at this position. | ||
| 143 | + """ | ||
| 144 | + args = [suffix, start] + ([] if end is None else [end]) | ||
| 145 | + return self.value_no_colors.endswith(*args) | ||
| 146 | + | ||
| 147 | + def encode(self, encoding=None, errors='strict'): | ||
| 148 | + """Encode using the codec registered for encoding. encoding defaults to the default encoding. | ||
| 149 | + | ||
| 150 | + errors may be given to set a different error handling scheme. Default is 'strict' meaning that encoding errors | ||
| 151 | + raise a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and 'xmlcharrefreplace' as well as any | ||
| 152 | + other name registered with codecs.register_error that is able to handle UnicodeEncodeErrors. | ||
| 153 | + | ||
| 154 | + :param str encoding: Codec. | ||
| 155 | + :param str errors: Error handling scheme. | ||
| 156 | + """ | ||
| 157 | + return ColorBytes(super(ColorStr, self).encode(encoding, errors), original_class=self.__class__) | ||
| 158 | + | ||
| 159 | + def decode(self, encoding=None, errors='strict'): | ||
| 160 | + """Decode using the codec registered for encoding. encoding defaults to the default encoding. | ||
| 161 | + | ||
| 162 | + errors may be given to set a different error handling scheme. Default is 'strict' meaning that encoding errors | ||
| 163 | + raise a UnicodeDecodeError. Other possible values are 'ignore' and 'replace' as well as any other name | ||
| 164 | + registered with codecs.register_error that is able to handle UnicodeDecodeErrors. | ||
| 165 | + | ||
| 166 | + :param str encoding: Codec. | ||
| 167 | + :param str errors: Error handling scheme. | ||
| 168 | + """ | ||
| 169 | + return self.__class__(super(ColorStr, self).decode(encoding, errors), keep_tags=True) | ||
| 170 | + | ||
| 171 | + def find(self, sub, start=None, end=None): | ||
| 172 | + """Return the lowest index where substring sub is found, such that sub is contained within string[start:end]. | ||
| 173 | + | ||
| 174 | + Optional arguments start and end are interpreted as in slice notation. | ||
| 175 | + | ||
| 176 | + :param str sub: Substring to search. | ||
| 177 | + :param int start: Beginning position. | ||
| 178 | + :param int end: Stop comparison at this position. | ||
| 179 | + """ | ||
| 180 | + return self.value_no_colors.find(sub, start, end) | ||
| 181 | + | ||
| 182 | + def format(self, *args, **kwargs): | ||
| 183 | + """Return a formatted version, using substitutions from args and kwargs. | ||
| 184 | + | ||
| 185 | + The substitutions are identified by braces ('{' and '}'). | ||
| 186 | + """ | ||
| 187 | + return self.__class__(super(ColorStr, self).format(*args, **kwargs), keep_tags=True) | ||
| 188 | + | ||
| 189 | + def index(self, sub, start=None, end=None): | ||
| 190 | + """Like S.find() but raise ValueError when the substring is not found. | ||
| 191 | + | ||
| 192 | + :param str sub: Substring to search. | ||
| 193 | + :param int start: Beginning position. | ||
| 194 | + :param int end: Stop comparison at this position. | ||
| 195 | + """ | ||
| 196 | + return self.value_no_colors.index(sub, start, end) | ||
| 197 | + | ||
| 198 | + def isalnum(self): | ||
| 199 | + """Return True if all characters in string are alphanumeric and there is at least one character in it.""" | ||
| 200 | + return self.value_no_colors.isalnum() | ||
| 201 | + | ||
| 202 | + def isalpha(self): | ||
| 203 | + """Return True if all characters in string are alphabetic and there is at least one character in it.""" | ||
| 204 | + return self.value_no_colors.isalpha() | ||
| 205 | + | ||
| 206 | + def isdecimal(self): | ||
| 207 | + """Return True if there are only decimal characters in string, False otherwise.""" | ||
| 208 | + return self.value_no_colors.isdecimal() | ||
| 209 | + | ||
| 210 | + def isdigit(self): | ||
| 211 | + """Return True if all characters in string are digits and there is at least one character in it.""" | ||
| 212 | + return self.value_no_colors.isdigit() | ||
| 213 | + | ||
| 214 | + def isnumeric(self): | ||
| 215 | + """Return True if there are only numeric characters in string, False otherwise.""" | ||
| 216 | + return self.value_no_colors.isnumeric() | ||
| 217 | + | ||
| 218 | + def isspace(self): | ||
| 219 | + """Return True if all characters in string are whitespace and there is at least one character in it.""" | ||
| 220 | + return self.value_no_colors.isspace() | ||
| 221 | + | ||
| 222 | + def istitle(self): | ||
| 223 | + """Return True if string is a titlecased string and there is at least one character in it. | ||
| 224 | + | ||
| 225 | + That is uppercase characters may only follow uncased characters and lowercase characters only cased ones. Return | ||
| 226 | + False otherwise. | ||
| 227 | + """ | ||
| 228 | + return self.value_no_colors.istitle() | ||
| 229 | + | ||
| 230 | + def isupper(self): | ||
| 231 | + """Return True if all cased characters are uppercase and there is at least one cased character in it.""" | ||
| 232 | + return self.value_no_colors.isupper() | ||
| 233 | + | ||
| 234 | + def join(self, iterable): | ||
| 235 | + """Return a string which is the concatenation of the strings in the iterable. | ||
| 236 | + | ||
| 237 | + :param iterable: Join items in this iterable. | ||
| 238 | + """ | ||
| 239 | + return self.__class__(super(ColorStr, self).join(iterable), keep_tags=True) | ||
| 240 | + | ||
| 241 | + def ljust(self, width, fillchar=None): | ||
| 242 | + """Return left-justified string of length width. Padding is done using the specified fill character or space. | ||
| 243 | + | ||
| 244 | + :param int width: Length of output string. | ||
| 245 | + :param str fillchar: Use this character instead of spaces. | ||
| 246 | + """ | ||
| 247 | + if fillchar is not None: | ||
| 248 | + result = self.value_no_colors.ljust(width, fillchar) | ||
| 249 | + else: | ||
| 250 | + result = self.value_no_colors.ljust(width) | ||
| 251 | + return self.__class__(result.replace(self.value_no_colors, self.value_colors), keep_tags=True) | ||
| 252 | + | ||
| 253 | + def rfind(self, sub, start=None, end=None): | ||
| 254 | + """Return the highest index where substring sub is found, such that sub is contained within string[start:end]. | ||
| 255 | + | ||
| 256 | + Optional arguments start and end are interpreted as in slice notation. | ||
| 257 | + | ||
| 258 | + :param str sub: Substring to search. | ||
| 259 | + :param int start: Beginning position. | ||
| 260 | + :param int end: Stop comparison at this position. | ||
| 261 | + """ | ||
| 262 | + return self.value_no_colors.rfind(sub, start, end) | ||
| 263 | + | ||
| 264 | + def rindex(self, sub, start=None, end=None): | ||
| 265 | + """Like .rfind() but raise ValueError when the substring is not found. | ||
| 266 | + | ||
| 267 | + :param str sub: Substring to search. | ||
| 268 | + :param int start: Beginning position. | ||
| 269 | + :param int end: Stop comparison at this position. | ||
| 270 | + """ | ||
| 271 | + return self.value_no_colors.rindex(sub, start, end) | ||
| 272 | + | ||
| 273 | + def rjust(self, width, fillchar=None): | ||
| 274 | + """Return right-justified string of length width. Padding is done using the specified fill character or space. | ||
| 275 | + | ||
| 276 | + :param int width: Length of output string. | ||
| 277 | + :param str fillchar: Use this character instead of spaces. | ||
| 278 | + """ | ||
| 279 | + if fillchar is not None: | ||
| 280 | + result = self.value_no_colors.rjust(width, fillchar) | ||
| 281 | + else: | ||
| 282 | + result = self.value_no_colors.rjust(width) | ||
| 283 | + return self.__class__(result.replace(self.value_no_colors, self.value_colors), keep_tags=True) | ||
| 284 | + | ||
| 285 | + def splitlines(self, keepends=False): | ||
| 286 | + """Return a list of the lines in the string, breaking at line boundaries. | ||
| 287 | + | ||
| 288 | + Line breaks are not included in the resulting list unless keepends is given and True. | ||
| 289 | + | ||
| 290 | + :param bool keepends: Include linebreaks. | ||
| 291 | + """ | ||
| 292 | + return [self.__class__(l) for l in self.value_colors.splitlines(keepends)] | ||
| 293 | + | ||
| 294 | + def startswith(self, prefix, start=0, end=-1): | ||
| 295 | + """Return True if string starts with the specified prefix, False otherwise. | ||
| 296 | + | ||
| 297 | + With optional start, test beginning at that position. With optional end, stop comparing at that position. prefix | ||
| 298 | + can also be a tuple of strings to try. | ||
| 299 | + | ||
| 300 | + :param str prefix: Prefix to search. | ||
| 301 | + :param int start: Beginning position. | ||
| 302 | + :param int end: Stop comparison at this position. | ||
| 303 | + """ | ||
| 304 | + return self.value_no_colors.startswith(prefix, start, end) | ||
| 305 | + | ||
| 306 | + def swapcase(self): | ||
| 307 | + """Return a copy of the string with uppercase characters converted to lowercase and vice versa.""" | ||
| 308 | + return apply_text(self, lambda s: s.swapcase()) | ||
| 309 | + | ||
| 310 | + def title(self): | ||
| 311 | + """Return a titlecased version of the string. | ||
| 312 | + | ||
| 313 | + That is words start with uppercase characters, all remaining cased characters have lowercase. | ||
| 314 | + """ | ||
| 315 | + return apply_text(self, lambda s: s.title()) | ||
| 316 | + | ||
| 317 | + def translate(self, table): | ||
| 318 | + """Return a copy of the string, where all characters have been mapped through the given translation table. | ||
| 319 | + | ||
| 320 | + Table must be a mapping of Unicode ordinals to Unicode ordinals, strings, or None. Unmapped characters are left | ||
| 321 | + untouched. Characters mapped to None are deleted. | ||
| 322 | + | ||
| 323 | + :param table: Translation table. | ||
| 324 | + """ | ||
| 325 | + return apply_text(self, lambda s: s.translate(table)) | ||
| 326 | + | ||
| 327 | + def upper(self): | ||
| 328 | + """Return a copy of the string converted to uppercase.""" | ||
| 329 | + return apply_text(self, lambda s: s.upper()) | ||
| 330 | + | ||
| 331 | + def zfill(self, width): | ||
| 332 | + """Pad a numeric string with zeros on the left, to fill a field of the specified width. | ||
| 333 | + | ||
| 334 | + The string is never truncated. | ||
| 335 | + | ||
| 336 | + :param int width: Length of output string. | ||
| 337 | + """ | ||
| 338 | + if not self.value_no_colors: | ||
| 339 | + result = self.value_no_colors.zfill(width) | ||
| 340 | + else: | ||
| 341 | + result = self.value_colors.replace(self.value_no_colors, self.value_no_colors.zfill(width)) | ||
| 342 | + return self.__class__(result, keep_tags=True) |
oletools/thirdparty/colorclass/parse.py
0 → 100644
| 1 | +"""Parse color markup tags into ANSI escape sequences.""" | ||
| 2 | + | ||
| 3 | +import re | ||
| 4 | + | ||
| 5 | +from colorclass.codes import ANSICodeMapping, BASE_CODES | ||
| 6 | + | ||
| 7 | +CODE_GROUPS = ( | ||
| 8 | + tuple(set(str(i) for i in BASE_CODES.values() if i and (40 <= i <= 49 or 100 <= i <= 109))), # bg colors | ||
| 9 | + tuple(set(str(i) for i in BASE_CODES.values() if i and (30 <= i <= 39 or 90 <= i <= 99))), # fg colors | ||
| 10 | + ('1', '22'), ('2', '22'), ('3', '23'), ('4', '24'), ('5', '25'), ('6', '26'), ('7', '27'), ('8', '28'), ('9', '29'), | ||
| 11 | +) | ||
| 12 | +RE_ANSI = re.compile(r'(\033\[([\d;]+)m)') | ||
| 13 | +RE_COMBINE = re.compile(r'\033\[([\d;]+)m\033\[([\d;]+)m') | ||
| 14 | +RE_SPLIT = re.compile(r'(\033\[[\d;]+m)') | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +def prune_overridden(ansi_string): | ||
| 18 | + """Remove color codes that are rendered ineffective by subsequent codes in one escape sequence then sort codes. | ||
| 19 | + | ||
| 20 | + :param str ansi_string: Incoming ansi_string with ANSI color codes. | ||
| 21 | + | ||
| 22 | + :return: Color string with pruned color sequences. | ||
| 23 | + :rtype: str | ||
| 24 | + """ | ||
| 25 | + multi_seqs = set(p for p in RE_ANSI.findall(ansi_string) if ';' in p[1]) # Sequences with multiple color codes. | ||
| 26 | + | ||
| 27 | + for escape, codes in multi_seqs: | ||
| 28 | + r_codes = list(reversed(codes.split(';'))) | ||
| 29 | + | ||
| 30 | + # Nuke everything before {/all}. | ||
| 31 | + try: | ||
| 32 | + r_codes = r_codes[:r_codes.index('0') + 1] | ||
| 33 | + except ValueError: | ||
| 34 | + pass | ||
| 35 | + | ||
| 36 | + # Thin out groups. | ||
| 37 | + for group in CODE_GROUPS: | ||
| 38 | + for pos in reversed([i for i, n in enumerate(r_codes) if n in group][1:]): | ||
| 39 | + r_codes.pop(pos) | ||
| 40 | + | ||
| 41 | + # Done. | ||
| 42 | + reduced_codes = ';'.join(sorted(r_codes, key=int)) | ||
| 43 | + if codes != reduced_codes: | ||
| 44 | + ansi_string = ansi_string.replace(escape, '\033[' + reduced_codes + 'm') | ||
| 45 | + | ||
| 46 | + return ansi_string | ||
| 47 | + | ||
| 48 | + | ||
| 49 | +def parse_input(tagged_string, disable_colors, keep_tags): | ||
| 50 | + """Perform the actual conversion of tags to ANSI escaped codes. | ||
| 51 | + | ||
| 52 | + Provides a version of the input without any colors for len() and other methods. | ||
| 53 | + | ||
| 54 | + :param str tagged_string: The input unicode value. | ||
| 55 | + :param bool disable_colors: Strip all colors in both outputs. | ||
| 56 | + :param bool keep_tags: Skip parsing curly bracket tags into ANSI escape sequences. | ||
| 57 | + | ||
| 58 | + :return: 2-item tuple. First item is the parsed output. Second item is a version of the input without any colors. | ||
| 59 | + :rtype: tuple | ||
| 60 | + """ | ||
| 61 | + codes = ANSICodeMapping(tagged_string) | ||
| 62 | + output_colors = getattr(tagged_string, 'value_colors', tagged_string) | ||
| 63 | + | ||
| 64 | + # Convert: '{b}{red}' -> '\033[1m\033[31m' | ||
| 65 | + if not keep_tags: | ||
| 66 | + for tag, replacement in (('{' + k + '}', '' if v is None else '\033[%dm' % v) for k, v in codes.items()): | ||
| 67 | + output_colors = output_colors.replace(tag, replacement) | ||
| 68 | + | ||
| 69 | + # Strip colors. | ||
| 70 | + output_no_colors = RE_ANSI.sub('', output_colors) | ||
| 71 | + if disable_colors: | ||
| 72 | + return output_no_colors, output_no_colors | ||
| 73 | + | ||
| 74 | + # Combine: '\033[1m\033[31m' -> '\033[1;31m' | ||
| 75 | + while True: | ||
| 76 | + simplified = RE_COMBINE.sub(r'\033[\1;\2m', output_colors) | ||
| 77 | + if simplified == output_colors: | ||
| 78 | + break | ||
| 79 | + output_colors = simplified | ||
| 80 | + | ||
| 81 | + # Prune: '\033[31;32;33;34;35m' -> '\033[35m' | ||
| 82 | + output_colors = prune_overridden(output_colors) | ||
| 83 | + | ||
| 84 | + # Deduplicate: '\033[1;mT\033[1;mE\033[1;mS\033[1;mT' -> '\033[1;mTEST' | ||
| 85 | + previous_escape = None | ||
| 86 | + segments = list() | ||
| 87 | + for item in (i for i in RE_SPLIT.split(output_colors) if i): | ||
| 88 | + if RE_SPLIT.match(item): | ||
| 89 | + if item != previous_escape: | ||
| 90 | + segments.append(item) | ||
| 91 | + previous_escape = item | ||
| 92 | + else: | ||
| 93 | + segments.append(item) | ||
| 94 | + output_colors = ''.join(segments) | ||
| 95 | + | ||
| 96 | + return output_colors, output_no_colors |
oletools/thirdparty/colorclass/search.py
0 → 100644
| 1 | +"""Determine color of characters that may or may not be adjacent to ANSI escape sequences.""" | ||
| 2 | + | ||
| 3 | +from colorclass.parse import RE_SPLIT | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +def build_color_index(ansi_string): | ||
| 7 | + """Build an index between visible characters and a string with invisible color codes. | ||
| 8 | + | ||
| 9 | + :param str ansi_string: String with color codes (ANSI escape sequences). | ||
| 10 | + | ||
| 11 | + :return: Position of visible characters in color string (indexes match non-color string). | ||
| 12 | + :rtype: tuple | ||
| 13 | + """ | ||
| 14 | + mapping = list() | ||
| 15 | + color_offset = 0 | ||
| 16 | + for item in (i for i in RE_SPLIT.split(ansi_string) if i): | ||
| 17 | + if RE_SPLIT.match(item): | ||
| 18 | + color_offset += len(item) | ||
| 19 | + else: | ||
| 20 | + for _ in range(len(item)): | ||
| 21 | + mapping.append(color_offset) | ||
| 22 | + color_offset += 1 | ||
| 23 | + return tuple(mapping) | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +def find_char_color(ansi_string, pos): | ||
| 27 | + """Determine what color a character is in the string. | ||
| 28 | + | ||
| 29 | + :param str ansi_string: String with color codes (ANSI escape sequences). | ||
| 30 | + :param int pos: Position of the character in the ansi_string. | ||
| 31 | + | ||
| 32 | + :return: Character along with all surrounding color codes. | ||
| 33 | + :rtype: str | ||
| 34 | + """ | ||
| 35 | + result = list() | ||
| 36 | + position = 0 # Set to None when character is found. | ||
| 37 | + for item in (i for i in RE_SPLIT.split(ansi_string) if i): | ||
| 38 | + if RE_SPLIT.match(item): | ||
| 39 | + result.append(item) | ||
| 40 | + if position is not None: | ||
| 41 | + position += len(item) | ||
| 42 | + elif position is not None: | ||
| 43 | + for char in item: | ||
| 44 | + if position == pos: | ||
| 45 | + result.append(char) | ||
| 46 | + position = None | ||
| 47 | + break | ||
| 48 | + position += 1 | ||
| 49 | + return ''.join(result) |
oletools/thirdparty/colorclass/toggles.py
0 → 100644
| 1 | +"""Convenience functions to enable/disable features.""" | ||
| 2 | + | ||
| 3 | +from colorclass.codes import ANSICodeMapping | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +def disable_all_colors(): | ||
| 7 | + """Disable all colors. Strip any color tags or codes.""" | ||
| 8 | + ANSICodeMapping.disable_all_colors() | ||
| 9 | + | ||
| 10 | + | ||
| 11 | +def enable_all_colors(): | ||
| 12 | + """Enable colors.""" | ||
| 13 | + ANSICodeMapping.enable_all_colors() | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +def disable_if_no_tty(): | ||
| 17 | + """Disable all colors if there is no TTY available. | ||
| 18 | + | ||
| 19 | + :return: True if colors are disabled, False if stderr or stdout is a TTY. | ||
| 20 | + :rtype: bool | ||
| 21 | + """ | ||
| 22 | + return ANSICodeMapping.disable_if_no_tty() | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +def is_enabled(): | ||
| 26 | + """Are colors enabled.""" | ||
| 27 | + return not ANSICodeMapping.DISABLE_COLORS | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +def set_light_background(): | ||
| 31 | + """Choose dark colors for all 'auto'-prefixed codes for readability on light backgrounds.""" | ||
| 32 | + ANSICodeMapping.set_light_background() | ||
| 33 | + | ||
| 34 | + | ||
| 35 | +def set_dark_background(): | ||
| 36 | + """Choose dark colors for all 'auto'-prefixed codes for readability on light backgrounds.""" | ||
| 37 | + ANSICodeMapping.set_dark_background() | ||
| 38 | + | ||
| 39 | + | ||
| 40 | +def is_light(): | ||
| 41 | + """Are background colors for light backgrounds.""" | ||
| 42 | + return ANSICodeMapping.LIGHT_BACKGROUND |
oletools/thirdparty/colorclass/windows.py
0 → 100644
| 1 | +"""Windows console screen buffer handlers.""" | ||
| 2 | + | ||
| 3 | +from __future__ import print_function | ||
| 4 | + | ||
| 5 | +import atexit | ||
| 6 | +import ctypes | ||
| 7 | +import re | ||
| 8 | +import sys | ||
| 9 | + | ||
| 10 | +from colorclass.codes import ANSICodeMapping, BASE_CODES | ||
| 11 | +from colorclass.core import RE_SPLIT | ||
| 12 | + | ||
| 13 | +ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 | ||
| 14 | +INVALID_HANDLE_VALUE = -1 | ||
| 15 | +IS_WINDOWS = sys.platform == 'win32' | ||
| 16 | +RE_NUMBER_SEARCH = re.compile(r'\033\[([\d;]+)m') | ||
| 17 | +STD_ERROR_HANDLE = -12 | ||
| 18 | +STD_OUTPUT_HANDLE = -11 | ||
| 19 | +WINDOWS_CODES = { | ||
| 20 | + '/all': -33, '/fg': -39, '/bg': -49, | ||
| 21 | + | ||
| 22 | + 'black': 0, 'red': 4, 'green': 2, 'yellow': 6, 'blue': 1, 'magenta': 5, 'cyan': 3, 'white': 7, | ||
| 23 | + | ||
| 24 | + 'bgblack': -8, 'bgred': 64, 'bggreen': 32, 'bgyellow': 96, 'bgblue': 16, 'bgmagenta': 80, 'bgcyan': 48, | ||
| 25 | + 'bgwhite': 112, | ||
| 26 | + | ||
| 27 | + 'hiblack': 8, 'hired': 12, 'higreen': 10, 'hiyellow': 14, 'hiblue': 9, 'himagenta': 13, 'hicyan': 11, 'hiwhite': 15, | ||
| 28 | + | ||
| 29 | + 'hibgblack': 128, 'hibgred': 192, 'hibggreen': 160, 'hibgyellow': 224, 'hibgblue': 144, 'hibgmagenta': 208, | ||
| 30 | + 'hibgcyan': 176, 'hibgwhite': 240, | ||
| 31 | + | ||
| 32 | + '/black': -39, '/red': -39, '/green': -39, '/yellow': -39, '/blue': -39, '/magenta': -39, '/cyan': -39, | ||
| 33 | + '/white': -39, '/hiblack': -39, '/hired': -39, '/higreen': -39, '/hiyellow': -39, '/hiblue': -39, '/himagenta': -39, | ||
| 34 | + '/hicyan': -39, '/hiwhite': -39, | ||
| 35 | + | ||
| 36 | + '/bgblack': -49, '/bgred': -49, '/bggreen': -49, '/bgyellow': -49, '/bgblue': -49, '/bgmagenta': -49, | ||
| 37 | + '/bgcyan': -49, '/bgwhite': -49, '/hibgblack': -49, '/hibgred': -49, '/hibggreen': -49, '/hibgyellow': -49, | ||
| 38 | + '/hibgblue': -49, '/hibgmagenta': -49, '/hibgcyan': -49, '/hibgwhite': -49, | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | + | ||
| 42 | +class COORD(ctypes.Structure): | ||
| 43 | + """COORD structure. http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119.""" | ||
| 44 | + | ||
| 45 | + _fields_ = [ | ||
| 46 | + ('X', ctypes.c_short), | ||
| 47 | + ('Y', ctypes.c_short), | ||
| 48 | + ] | ||
| 49 | + | ||
| 50 | + | ||
| 51 | +class SmallRECT(ctypes.Structure): | ||
| 52 | + """SMALL_RECT structure. http://msdn.microsoft.com/en-us/library/windows/desktop/ms686311.""" | ||
| 53 | + | ||
| 54 | + _fields_ = [ | ||
| 55 | + ('Left', ctypes.c_short), | ||
| 56 | + ('Top', ctypes.c_short), | ||
| 57 | + ('Right', ctypes.c_short), | ||
| 58 | + ('Bottom', ctypes.c_short), | ||
| 59 | + ] | ||
| 60 | + | ||
| 61 | + | ||
| 62 | +class ConsoleScreenBufferInfo(ctypes.Structure): | ||
| 63 | + """CONSOLE_SCREEN_BUFFER_INFO structure. http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093.""" | ||
| 64 | + | ||
| 65 | + _fields_ = [ | ||
| 66 | + ('dwSize', COORD), | ||
| 67 | + ('dwCursorPosition', COORD), | ||
| 68 | + ('wAttributes', ctypes.c_ushort), | ||
| 69 | + ('srWindow', SmallRECT), | ||
| 70 | + ('dwMaximumWindowSize', COORD) | ||
| 71 | + ] | ||
| 72 | + | ||
| 73 | + | ||
| 74 | +def init_kernel32(kernel32=None): | ||
| 75 | + """Load a unique instance of WinDLL into memory, set arg/return types, and get stdout/err handles. | ||
| 76 | + | ||
| 77 | + 1. Since we are setting DLL function argument types and return types, we need to maintain our own instance of | ||
| 78 | + kernel32 to prevent overriding (or being overwritten by) user's own changes to ctypes.windll.kernel32. | ||
| 79 | + 2. While we're doing all this we might as well get the handles to STDOUT and STDERR streams. | ||
| 80 | + 3. If either stream has already been replaced set return value to INVALID_HANDLE_VALUE to indicate it shouldn't be | ||
| 81 | + replaced. | ||
| 82 | + | ||
| 83 | + :raise AttributeError: When called on a non-Windows platform. | ||
| 84 | + | ||
| 85 | + :param kernel32: Optional mock kernel32 object. For testing. | ||
| 86 | + | ||
| 87 | + :return: Loaded kernel32 instance, stderr handle (int), stdout handle (int). | ||
| 88 | + :rtype: tuple | ||
| 89 | + """ | ||
| 90 | + if not kernel32: | ||
| 91 | + kernel32 = ctypes.LibraryLoader(ctypes.WinDLL).kernel32 # Load our own instance. Unique memory address. | ||
| 92 | + kernel32.GetStdHandle.argtypes = [ctypes.c_ulong] | ||
| 93 | + kernel32.GetStdHandle.restype = ctypes.c_void_p | ||
| 94 | + kernel32.GetConsoleScreenBufferInfo.argtypes = [ | ||
| 95 | + ctypes.c_void_p, | ||
| 96 | + ctypes.POINTER(ConsoleScreenBufferInfo), | ||
| 97 | + ] | ||
| 98 | + kernel32.GetConsoleScreenBufferInfo.restype = ctypes.c_long | ||
| 99 | + | ||
| 100 | + # Get handles. | ||
| 101 | + if hasattr(sys.stderr, '_original_stream'): | ||
| 102 | + stderr = INVALID_HANDLE_VALUE | ||
| 103 | + else: | ||
| 104 | + stderr = kernel32.GetStdHandle(STD_ERROR_HANDLE) | ||
| 105 | + if hasattr(sys.stdout, '_original_stream'): | ||
| 106 | + stdout = INVALID_HANDLE_VALUE | ||
| 107 | + else: | ||
| 108 | + stdout = kernel32.GetStdHandle(STD_OUTPUT_HANDLE) | ||
| 109 | + | ||
| 110 | + return kernel32, stderr, stdout | ||
| 111 | + | ||
| 112 | + | ||
| 113 | +def get_console_info(kernel32, handle): | ||
| 114 | + """Get information about this current console window. | ||
| 115 | + | ||
| 116 | + http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231 | ||
| 117 | + https://code.google.com/p/colorama/issues/detail?id=47 | ||
| 118 | + https://bitbucket.org/pytest-dev/py/src/4617fe46/py/_io/terminalwriter.py | ||
| 119 | + | ||
| 120 | + Windows 10 Insider since around February 2016 finally introduced support for ANSI colors. No need to replace stdout | ||
| 121 | + and stderr streams to intercept colors and issue multiple SetConsoleTextAttribute() calls for these consoles. | ||
| 122 | + | ||
| 123 | + :raise OSError: When GetConsoleScreenBufferInfo or GetConsoleMode API calls fail. | ||
| 124 | + | ||
| 125 | + :param ctypes.windll.kernel32 kernel32: Loaded kernel32 instance. | ||
| 126 | + :param int handle: stderr or stdout handle. | ||
| 127 | + | ||
| 128 | + :return: Foreground and background colors (integers) as well as native ANSI support (bool). | ||
| 129 | + :rtype: tuple | ||
| 130 | + """ | ||
| 131 | + # Query Win32 API. | ||
| 132 | + csbi = ConsoleScreenBufferInfo() # Populated by GetConsoleScreenBufferInfo. | ||
| 133 | + lpcsbi = ctypes.byref(csbi) | ||
| 134 | + dword = ctypes.c_ulong() # Populated by GetConsoleMode. | ||
| 135 | + lpdword = ctypes.byref(dword) | ||
| 136 | + if not kernel32.GetConsoleScreenBufferInfo(handle, lpcsbi) or not kernel32.GetConsoleMode(handle, lpdword): | ||
| 137 | + raise ctypes.WinError() | ||
| 138 | + | ||
| 139 | + # Parse data. | ||
| 140 | + # buffer_width = int(csbi.dwSize.X - 1) | ||
| 141 | + # buffer_height = int(csbi.dwSize.Y) | ||
| 142 | + # terminal_width = int(csbi.srWindow.Right - csbi.srWindow.Left) | ||
| 143 | + # terminal_height = int(csbi.srWindow.Bottom - csbi.srWindow.Top) | ||
| 144 | + fg_color = csbi.wAttributes % 16 | ||
| 145 | + bg_color = csbi.wAttributes & 240 | ||
| 146 | + native_ansi = bool(dword.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) | ||
| 147 | + | ||
| 148 | + return fg_color, bg_color, native_ansi | ||
| 149 | + | ||
| 150 | + | ||
| 151 | +def bg_color_native_ansi(kernel32, stderr, stdout): | ||
| 152 | + """Get background color and if console supports ANSI colors natively for both streams. | ||
| 153 | + | ||
| 154 | + :param ctypes.windll.kernel32 kernel32: Loaded kernel32 instance. | ||
| 155 | + :param int stderr: stderr handle. | ||
| 156 | + :param int stdout: stdout handle. | ||
| 157 | + | ||
| 158 | + :return: Background color (int) and native ANSI support (bool). | ||
| 159 | + :rtype: tuple | ||
| 160 | + """ | ||
| 161 | + try: | ||
| 162 | + if stderr == INVALID_HANDLE_VALUE: | ||
| 163 | + raise OSError | ||
| 164 | + bg_color, native_ansi = get_console_info(kernel32, stderr)[1:] | ||
| 165 | + except OSError: | ||
| 166 | + try: | ||
| 167 | + if stdout == INVALID_HANDLE_VALUE: | ||
| 168 | + raise OSError | ||
| 169 | + bg_color, native_ansi = get_console_info(kernel32, stdout)[1:] | ||
| 170 | + except OSError: | ||
| 171 | + bg_color, native_ansi = WINDOWS_CODES['black'], False | ||
| 172 | + return bg_color, native_ansi | ||
| 173 | + | ||
| 174 | + | ||
| 175 | +class WindowsStream(object): | ||
| 176 | + """Replacement stream which overrides sys.stdout or sys.stderr. When writing or printing, ANSI codes are converted. | ||
| 177 | + | ||
| 178 | + ANSI (Linux/Unix) color codes are converted into win32 system calls, changing the next character's color before | ||
| 179 | + printing it. Resources referenced: | ||
| 180 | + https://github.com/tartley/colorama | ||
| 181 | + http://www.cplusplus.com/articles/2ywTURfi/ | ||
| 182 | + http://thomasfischer.biz/python-and-windows-terminal-colors/ | ||
| 183 | + http://stackoverflow.com/questions/17125440/c-win32-console-color | ||
| 184 | + http://www.tysos.org/svn/trunk/mono/corlib/System/WindowsConsoleDriver.cs | ||
| 185 | + http://stackoverflow.com/questions/287871/print-in-terminal-with-colors-using-python | ||
| 186 | + http://msdn.microsoft.com/en-us/library/windows/desktop/ms682088#_win32_character_attributes | ||
| 187 | + | ||
| 188 | + :cvar list ALL_BG_CODES: List of bg Windows codes. Used to determine if requested color is foreground or background. | ||
| 189 | + :cvar dict COMPILED_CODES: Translation dict. Keys are ANSI codes (values of BASE_CODES), values are Windows codes. | ||
| 190 | + :ivar int default_fg: Foreground Windows color code at the time of instantiation. | ||
| 191 | + :ivar int default_bg: Background Windows color code at the time of instantiation. | ||
| 192 | + """ | ||
| 193 | + | ||
| 194 | + ALL_BG_CODES = [v for k, v in WINDOWS_CODES.items() if k.startswith('bg') or k.startswith('hibg')] | ||
| 195 | + COMPILED_CODES = dict((v, WINDOWS_CODES[k]) for k, v in BASE_CODES.items() if k in WINDOWS_CODES) | ||
| 196 | + | ||
| 197 | + def __init__(self, kernel32, stream_handle, original_stream): | ||
| 198 | + """Constructor. | ||
| 199 | + | ||
| 200 | + :param ctypes.windll.kernel32 kernel32: Loaded kernel32 instance. | ||
| 201 | + :param int stream_handle: stderr or stdout handle. | ||
| 202 | + :param original_stream: sys.stderr or sys.stdout before being overridden by this class' instance. | ||
| 203 | + """ | ||
| 204 | + self._kernel32 = kernel32 | ||
| 205 | + self._stream_handle = stream_handle | ||
| 206 | + self._original_stream = original_stream | ||
| 207 | + self.default_fg, self.default_bg = self.colors | ||
| 208 | + | ||
| 209 | + def __getattr__(self, item): | ||
| 210 | + """If an attribute/function/etc is not defined in this function, retrieve the one from the original stream. | ||
| 211 | + | ||
| 212 | + Fixes ipython arrow key presses. | ||
| 213 | + """ | ||
| 214 | + return getattr(self._original_stream, item) | ||
| 215 | + | ||
| 216 | + @property | ||
| 217 | + def colors(self): | ||
| 218 | + """Return the current foreground and background colors.""" | ||
| 219 | + try: | ||
| 220 | + return get_console_info(self._kernel32, self._stream_handle)[:2] | ||
| 221 | + except OSError: | ||
| 222 | + return WINDOWS_CODES['white'], WINDOWS_CODES['black'] | ||
| 223 | + | ||
| 224 | + @colors.setter | ||
| 225 | + def colors(self, color_code): | ||
| 226 | + """Change the foreground and background colors for subsequently printed characters. | ||
| 227 | + | ||
| 228 | + None resets colors to their original values (when class was instantiated). | ||
| 229 | + | ||
| 230 | + Since setting a color requires including both foreground and background codes (merged), setting just the | ||
| 231 | + foreground color resets the background color to black, and vice versa. | ||
| 232 | + | ||
| 233 | + This function first gets the current background and foreground colors, merges in the requested color code, and | ||
| 234 | + sets the result. | ||
| 235 | + | ||
| 236 | + However if we need to remove just the foreground color but leave the background color the same (or vice versa) | ||
| 237 | + such as when {/red} is used, we must merge the default foreground color with the current background color. This | ||
| 238 | + is the reason for those negative values. | ||
| 239 | + | ||
| 240 | + :param int color_code: Color code from WINDOWS_CODES. | ||
| 241 | + """ | ||
| 242 | + if color_code is None: | ||
| 243 | + color_code = WINDOWS_CODES['/all'] | ||
| 244 | + | ||
| 245 | + # Get current color code. | ||
| 246 | + current_fg, current_bg = self.colors | ||
| 247 | + | ||
| 248 | + # Handle special negative codes. Also determine the final color code. | ||
| 249 | + if color_code == WINDOWS_CODES['/fg']: | ||
| 250 | + final_color_code = self.default_fg | current_bg # Reset the foreground only. | ||
| 251 | + elif color_code == WINDOWS_CODES['/bg']: | ||
| 252 | + final_color_code = current_fg | self.default_bg # Reset the background only. | ||
| 253 | + elif color_code == WINDOWS_CODES['/all']: | ||
| 254 | + final_color_code = self.default_fg | self.default_bg # Reset both. | ||
| 255 | + elif color_code == WINDOWS_CODES['bgblack']: | ||
| 256 | + final_color_code = current_fg # Black background. | ||
| 257 | + else: | ||
| 258 | + new_is_bg = color_code in self.ALL_BG_CODES | ||
| 259 | + final_color_code = color_code | (current_fg if new_is_bg else current_bg) | ||
| 260 | + | ||
| 261 | + # Set new code. | ||
| 262 | + self._kernel32.SetConsoleTextAttribute(self._stream_handle, final_color_code) | ||
| 263 | + | ||
| 264 | + def write(self, p_str): | ||
| 265 | + """Write to stream. | ||
| 266 | + | ||
| 267 | + :param str p_str: string to print. | ||
| 268 | + """ | ||
| 269 | + for segment in RE_SPLIT.split(p_str): | ||
| 270 | + if not segment: | ||
| 271 | + # Empty string. p_str probably starts with colors so the first item is always ''. | ||
| 272 | + continue | ||
| 273 | + if not RE_SPLIT.match(segment): | ||
| 274 | + # No color codes, print regular text. | ||
| 275 | + print(segment, file=self._original_stream, end='') | ||
| 276 | + self._original_stream.flush() | ||
| 277 | + continue | ||
| 278 | + for color_code in (int(c) for c in RE_NUMBER_SEARCH.findall(segment)[0].split(';')): | ||
| 279 | + if color_code in self.COMPILED_CODES: | ||
| 280 | + self.colors = self.COMPILED_CODES[color_code] | ||
| 281 | + | ||
| 282 | + | ||
| 283 | +class Windows(object): | ||
| 284 | + """Enable and disable Windows support for ANSI color character codes. | ||
| 285 | + | ||
| 286 | + Call static method Windows.enable() to enable color support for the remainder of the process' lifetime. | ||
| 287 | + | ||
| 288 | + This class is also a context manager. You can do this: | ||
| 289 | + with Windows(): | ||
| 290 | + print(Color('{autored}Test{/autored}')) | ||
| 291 | + | ||
| 292 | + Or this: | ||
| 293 | + with Windows(auto_colors=True): | ||
| 294 | + print(Color('{autored}Test{/autored}')) | ||
| 295 | + """ | ||
| 296 | + | ||
| 297 | + @classmethod | ||
| 298 | + def disable(cls): | ||
| 299 | + """Restore sys.stderr and sys.stdout to their original objects. Resets colors to their original values. | ||
| 300 | + | ||
| 301 | + :return: If streams restored successfully. | ||
| 302 | + :rtype: bool | ||
| 303 | + """ | ||
| 304 | + # Skip if not on Windows. | ||
| 305 | + if not IS_WINDOWS: | ||
| 306 | + return False | ||
| 307 | + | ||
| 308 | + # Restore default colors. | ||
| 309 | + if hasattr(sys.stderr, '_original_stream'): | ||
| 310 | + getattr(sys, 'stderr').color = None | ||
| 311 | + if hasattr(sys.stdout, '_original_stream'): | ||
| 312 | + getattr(sys, 'stdout').color = None | ||
| 313 | + | ||
| 314 | + # Restore original streams. | ||
| 315 | + changed = False | ||
| 316 | + if hasattr(sys.stderr, '_original_stream'): | ||
| 317 | + changed = True | ||
| 318 | + sys.stderr = getattr(sys.stderr, '_original_stream') | ||
| 319 | + if hasattr(sys.stdout, '_original_stream'): | ||
| 320 | + changed = True | ||
| 321 | + sys.stdout = getattr(sys.stdout, '_original_stream') | ||
| 322 | + | ||
| 323 | + return changed | ||
| 324 | + | ||
| 325 | + @staticmethod | ||
| 326 | + def is_enabled(): | ||
| 327 | + """Return True if either stderr or stdout has colors enabled.""" | ||
| 328 | + return hasattr(sys.stderr, '_original_stream') or hasattr(sys.stdout, '_original_stream') | ||
| 329 | + | ||
| 330 | + @classmethod | ||
| 331 | + def enable(cls, auto_colors=False, reset_atexit=False): | ||
| 332 | + """Enable color text with print() or sys.stdout.write() (stderr too). | ||
| 333 | + | ||
| 334 | + :param bool auto_colors: Automatically selects dark or light colors based on current terminal's background | ||
| 335 | + color. Only works with {autored} and related tags. | ||
| 336 | + :param bool reset_atexit: Resets original colors upon Python exit (in case you forget to reset it yourself with | ||
| 337 | + a closing tag). Does nothing on native ANSI consoles. | ||
| 338 | + | ||
| 339 | + :return: If streams replaced successfully. | ||
| 340 | + :rtype: bool | ||
| 341 | + """ | ||
| 342 | + if not IS_WINDOWS: | ||
| 343 | + return False # Windows only. | ||
| 344 | + | ||
| 345 | + # Get values from init_kernel32(). | ||
| 346 | + kernel32, stderr, stdout = init_kernel32() | ||
| 347 | + if stderr == INVALID_HANDLE_VALUE and stdout == INVALID_HANDLE_VALUE: | ||
| 348 | + return False # No valid handles, nothing to do. | ||
| 349 | + | ||
| 350 | + # Get console info. | ||
| 351 | + bg_color, native_ansi = bg_color_native_ansi(kernel32, stderr, stdout) | ||
| 352 | + | ||
| 353 | + # Set auto colors: | ||
| 354 | + if auto_colors: | ||
| 355 | + if bg_color in (112, 96, 240, 176, 224, 208, 160): | ||
| 356 | + ANSICodeMapping.set_light_background() | ||
| 357 | + else: | ||
| 358 | + ANSICodeMapping.set_dark_background() | ||
| 359 | + | ||
| 360 | + # Don't replace streams if ANSI codes are natively supported. | ||
| 361 | + if native_ansi: | ||
| 362 | + return False | ||
| 363 | + | ||
| 364 | + # Reset on exit if requested. | ||
| 365 | + if reset_atexit: | ||
| 366 | + atexit.register(cls.disable) | ||
| 367 | + | ||
| 368 | + # Overwrite stream references. | ||
| 369 | + if stderr != INVALID_HANDLE_VALUE: | ||
| 370 | + sys.stderr.flush() | ||
| 371 | + sys.stderr = WindowsStream(kernel32, stderr, sys.stderr) | ||
| 372 | + if stdout != INVALID_HANDLE_VALUE: | ||
| 373 | + sys.stdout.flush() | ||
| 374 | + sys.stdout = WindowsStream(kernel32, stdout, sys.stdout) | ||
| 375 | + | ||
| 376 | + return True | ||
| 377 | + | ||
| 378 | + def __init__(self, auto_colors=False): | ||
| 379 | + """Constructor.""" | ||
| 380 | + self.auto_colors = auto_colors | ||
| 381 | + | ||
| 382 | + def __enter__(self): | ||
| 383 | + """Context manager, enables colors on Windows.""" | ||
| 384 | + self.enable(auto_colors=self.auto_colors) | ||
| 385 | + | ||
| 386 | + def __exit__(self, *_): | ||
| 387 | + """Context manager, disabled colors on Windows.""" | ||
| 388 | + self.disable() |
oletools/thirdparty/tablestream/tablestream.py
| @@ -48,8 +48,9 @@ License: BSD, see source code or documentation | @@ -48,8 +48,9 @@ License: BSD, see source code or documentation | ||
| 48 | # 2015-11-01 v0.01 PL: - first version | 48 | # 2015-11-01 v0.01 PL: - first version |
| 49 | # 2016-01-01 v0.02 PL: - added styles, color support | 49 | # 2016-01-01 v0.02 PL: - added styles, color support |
| 50 | # 2016-04-19 v0.03 PL: - enable colorclass on Windows, fixed issue #39 | 50 | # 2016-04-19 v0.03 PL: - enable colorclass on Windows, fixed issue #39 |
| 51 | +# 2016-05-25 v0.04 PL: - updated for colorclass 2.2.0 (now a package) | ||
| 51 | 52 | ||
| 52 | -__version__ = '0.03' | 53 | +__version__ = '0.04' |
| 53 | 54 | ||
| 54 | #------------------------------------------------------------------------------ | 55 | #------------------------------------------------------------------------------ |
| 55 | # TODO: | 56 | # TODO: |
| @@ -64,7 +65,7 @@ __version__ = '0.03' | @@ -64,7 +65,7 @@ __version__ = '0.03' | ||
| 64 | import textwrap | 65 | import textwrap |
| 65 | import sys, os | 66 | import sys, os |
| 66 | 67 | ||
| 67 | -from thirdparty.colorclass import colorclass | 68 | +from thirdparty import colorclass |
| 68 | 69 | ||
| 69 | # On Windows, colorclass needs to be enabled: | 70 | # On Windows, colorclass needs to be enabled: |
| 70 | if os.name == 'nt': | 71 | if os.name == 'nt': |