Ejemplo n.º 1
0
def parse_input(tagged_string, disable_colors, keep_tags):
    """Perform the actual conversion of tags to ANSI escaped codes.

    Provides a version of the input without any colors for len() and other methods.

    :param str tagged_string: The input unicode value.
    :param bool disable_colors: Strip all colors in both outputs.
    :param bool keep_tags: Skip parsing curly bracket tags into ANSI escape sequences.

    :return: 2-item tuple. First item is the parsed output. Second item is a version of the input without any colors.
    :rtype: tuple
    """
    codes = ANSICodeMapping(tagged_string)
    output_colors = getattr(tagged_string, 'value_colors', tagged_string)

    # Convert: '{b}{red}' -> '\033[1m\033[31m'
    if not keep_tags:
        for tag, replacement in (('{' + k + '}',
                                  '' if v is None else '\033[%dm' % v)
                                 for k, v in codes.items()):
            output_colors = output_colors.replace(tag, replacement)

    # Strip colors.
    output_no_colors = RE_ANSI.sub('', output_colors)
    if disable_colors:
        return output_no_colors, output_no_colors

    # Combine: '\033[1m\033[31m' -> '\033[1;31m'
    while True:
        simplified = RE_COMBINE.sub(r'\033[\1;\2m', output_colors)
        if simplified == output_colors:
            break
        output_colors = simplified

    # Prune: '\033[31;32;33;34;35m' -> '\033[35m'
    output_colors = prune_overridden(output_colors)

    # Deduplicate: '\033[1;mT\033[1;mE\033[1;mS\033[1;mT' -> '\033[1;mTEST'
    previous_escape = None
    segments = list()
    for item in (i for i in RE_SPLIT.split(output_colors) if i):
        if RE_SPLIT.match(item):
            if item != previous_escape:
                segments.append(item)
                previous_escape = item
        else:
            segments.append(item)
    output_colors = ''.join(segments)

    return output_colors, output_no_colors
Ejemplo n.º 2
0
def disable_if_no_tty():
    """Disable all colors if there is no TTY available.

    :return: True if colors are disabled, False if stderr or stdout is a TTY.
    :rtype: bool
    """
    return ANSICodeMapping.disable_if_no_tty()
Ejemplo n.º 3
0
    def enable(cls, auto_colors=False, reset_atexit=False):
        """Enable color text with print() or sys.stdout.write() (stderr too).

        :param bool auto_colors: Automatically selects dark or light colors based on current terminal's background
            color. Only works with {autored} and related tags.
        :param bool reset_atexit: Resets original colors upon Python exit (in case you forget to reset it yourself with
            a closing tag). Does nothing on native ANSI consoles.

        :return: If streams replaced successfully.
        :rtype: bool
        """
        if not IS_WINDOWS:
            return False  # Windows only.

        # Get values from init_kernel32().
        kernel32, stderr, stdout = init_kernel32()
        if stderr == INVALID_HANDLE_VALUE and stdout == INVALID_HANDLE_VALUE:
            return False  # No valid handles, nothing to do.

        # Get console info.
        bg_color, native_ansi = bg_color_native_ansi(kernel32, stderr, stdout)

        # Set auto colors:
        if auto_colors:
            if bg_color in (112, 96, 240, 176, 224, 208, 160):
                ANSICodeMapping.set_light_background()
            else:
                ANSICodeMapping.set_dark_background()

        # Don't replace streams if ANSI codes are natively supported.
        if native_ansi:
            return False

        # Reset on exit if requested.
        if reset_atexit:
            atexit.register(cls.disable)

        # Overwrite stream references.
        if stderr != INVALID_HANDLE_VALUE:
            sys.stderr.flush()
            sys.stderr = WindowsStream(kernel32, stderr, sys.stderr)
        if stdout != INVALID_HANDLE_VALUE:
            sys.stdout.flush()
            sys.stdout = WindowsStream(kernel32, stdout, sys.stdout)

        return True
Ejemplo n.º 4
0
    def enable(cls, auto_colors=False, reset_atexit=False):
        """Enable color text with print() or sys.stdout.write() (stderr too).

        :param bool auto_colors: Automatically selects dark or light colors based on current terminal's background
            color. Only works with {autored} and related tags.
        :param bool reset_atexit: Resets original colors upon Python exit (in case you forget to reset it yourself with
            a closing tag). Does nothing on native ANSI consoles.

        :return: If streams replaced successfully.
        :rtype: bool
        """
        if not IS_WINDOWS:
            return False  # Windows only.

        # Get values from init_kernel32().
        kernel32, stderr, stdout = init_kernel32()
        if stderr == INVALID_HANDLE_VALUE and stdout == INVALID_HANDLE_VALUE:
            return False  # No valid handles, nothing to do.

        # Get console info.
        bg_color, native_ansi = bg_color_native_ansi(kernel32, stderr, stdout)

        # Set auto colors:
        if auto_colors:
            if bg_color in (112, 96, 240, 176, 224, 208, 160):
                ANSICodeMapping.set_light_background()
            else:
                ANSICodeMapping.set_dark_background()

        # Don't replace streams if ANSI codes are natively supported.
        if native_ansi:
            return False

        # Reset on exit if requested.
        if reset_atexit:
            atexit.register(cls.disable)

        # Overwrite stream references.
        if stderr != INVALID_HANDLE_VALUE:
            sys.stderr.flush()
            sys.stderr = WindowsStream(kernel32, stderr, sys.stderr)
        if stdout != INVALID_HANDLE_VALUE:
            sys.stdout.flush()
            sys.stdout = WindowsStream(kernel32, stdout, sys.stdout)

        return True
Ejemplo n.º 5
0
def test_ansi_code_mapping_whitelist():
    """Test whitelist enforcement."""
    auto_codes = ANSICodeMapping('{green}{bgred}Test{/all}')

    # Test __getitem__.
    with pytest.raises(KeyError):
        assert not auto_codes['red']
    assert auto_codes['green'] == 32

    # Test iter and len.
    assert sorted(auto_codes) == ['/all', 'bgred', 'green']
    assert len(auto_codes) == 3
Ejemplo n.º 6
0
def parse_input(tagged_string, disable_colors, keep_tags):
    """Perform the actual conversion of tags to ANSI escaped codes.

    Provides a version of the input without any colors for len() and other methods.

    :param str tagged_string: The input unicode value.
    :param bool disable_colors: Strip all colors in both outputs.
    :param bool keep_tags: Skip parsing curly bracket tags into ANSI escape sequences.

    :return: 2-item tuple. First item is the parsed output. Second item is a version of the input without any colors.
    :rtype: tuple
    """
    codes = ANSICodeMapping(tagged_string)
    output_colors = getattr(tagged_string, 'value_colors', tagged_string)

    # Convert: '{b}{red}' -> '\033[1m\033[31m'
    if not keep_tags:
        for tag, replacement in (('{' + k + '}', '' if v is None else '\033[%dm' % v) for k, v in codes.items()):
            output_colors = output_colors.replace(tag, replacement)

    # Strip colors.
    output_no_colors = RE_ANSI.sub('', output_colors)
    if disable_colors:
        return output_no_colors, output_no_colors

    # Combine: '\033[1m\033[31m' -> '\033[1;31m'
    while True:
        simplified = RE_COMBINE.sub(r'\033[\1;\2m', output_colors)
        if simplified == output_colors:
            break
        output_colors = simplified

    # Prune: '\033[31;32;33;34;35m' -> '\033[35m'
    output_colors = prune_overridden(output_colors)

    # Deduplicate: '\033[1;mT\033[1;mE\033[1;mS\033[1;mT' -> '\033[1;mTEST'
    previous_escape = None
    segments = list()
    for item in (i for i in RE_SPLIT.split(output_colors) if i):
        if RE_SPLIT.match(item):
            if item != previous_escape:
                segments.append(item)
                previous_escape = item
        else:
            segments.append(item)
    output_colors = ''.join(segments)

    return output_colors, output_no_colors
Ejemplo n.º 7
0
def disable_all_colors():
    """Disable all colors. Strip any color tags or codes."""
    ANSICodeMapping.disable_all_colors()
Ejemplo n.º 8
0
def set_dark_background():
    """Choose dark colors for all 'auto'-prefixed codes for readability on light backgrounds."""
    ANSICodeMapping.set_dark_background()
Ejemplo n.º 9
0
def enable_all_colors():
    """Enable colors."""
    ANSICodeMapping.enable_all_colors()
Ejemplo n.º 10
0
def test_auto_toggles(toggle):
    """Test auto colors and ANSICodeMapping class toggles.

    :param str toggle: Toggle method to call.
    """
    # Toggle.
    if toggle == 'light':
        ANSICodeMapping.enable_all_colors()
        ANSICodeMapping.set_light_background()
        assert ANSICodeMapping.DISABLE_COLORS is False
        assert ANSICodeMapping.LIGHT_BACKGROUND is True
    elif toggle == 'dark':
        ANSICodeMapping.enable_all_colors()
        ANSICodeMapping.set_dark_background()
        assert ANSICodeMapping.DISABLE_COLORS is False
        assert ANSICodeMapping.LIGHT_BACKGROUND is False
    else:
        ANSICodeMapping.disable_all_colors()
        assert ANSICodeMapping.DISABLE_COLORS is True
        assert ANSICodeMapping.LIGHT_BACKGROUND is False

    # Test iter and len.
    auto_codes = ANSICodeMapping('}{'.join([''] + list(BASE_CODES) + ['']))
    count = 0
    for k, v in auto_codes.items():
        count += 1
        assert str(k) == k
        assert v is None or int(v) == v
    assert len(auto_codes) == count

    # Test foreground properties.
    key_fg = '{autoblack}{autored}{autogreen}{autoyellow}{autoblue}{automagenta}{autocyan}{autowhite}'
    actual = key_fg.format(**auto_codes)
    if toggle == 'light':
        assert actual == '3031323334353637'
    elif toggle == 'dark':
        assert actual == '9091929394959697'
    else:
        assert actual == 'NoneNoneNoneNoneNoneNoneNoneNone'

    # Test background properties.
    key_fg = '{autobgblack}{autobgred}{autobggreen}{autobgyellow}{autobgblue}{autobgmagenta}{autobgcyan}{autobgwhite}'
    actual = key_fg.format(**auto_codes)
    if toggle == 'light':
        assert actual == '4041424344454647'
    elif toggle == 'dark':
        assert actual == '100101102103104105106107'
    else:
        assert actual == 'NoneNoneNoneNoneNoneNoneNoneNone'
Ejemplo n.º 11
0
def test_auto_toggles(toggle):
    """Test auto colors and ANSICodeMapping class toggles.

    :param str toggle: Toggle method to call.
    """
    # Toggle.
    if toggle == 'light':
        ANSICodeMapping.enable_all_colors()
        ANSICodeMapping.set_light_background()
        assert ANSICodeMapping.DISABLE_COLORS is False
        assert ANSICodeMapping.LIGHT_BACKGROUND is True
    elif toggle == 'dark':
        ANSICodeMapping.enable_all_colors()
        ANSICodeMapping.set_dark_background()
        assert ANSICodeMapping.DISABLE_COLORS is False
        assert ANSICodeMapping.LIGHT_BACKGROUND is False
    else:
        ANSICodeMapping.disable_all_colors()
        assert ANSICodeMapping.DISABLE_COLORS is True
        assert ANSICodeMapping.LIGHT_BACKGROUND is False

    # Test iter and len.
    auto_codes = ANSICodeMapping('}{'.join([''] + list(BASE_CODES) + ['']))
    count = 0
    for k, v in auto_codes.items():
        count += 1
        assert str(k) == k
        assert v is None or int(v) == v
    assert len(auto_codes) == count

    # Test foreground properties.
    key_fg = '{autoblack}{autored}{autogreen}{autoyellow}{autoblue}{automagenta}{autocyan}{autowhite}'
    actual = key_fg.format(**auto_codes)
    if toggle == 'light':
        assert actual == '3031323334353637'
    elif toggle == 'dark':
        assert actual == '9091929394959697'
    else:
        assert actual == 'NoneNoneNoneNoneNoneNoneNoneNone'

    # Test background properties.
    key_fg = '{autobgblack}{autobgred}{autobggreen}{autobgyellow}{autobgblue}{autobgmagenta}{autobgcyan}{autobgwhite}'
    actual = key_fg.format(**auto_codes)
    if toggle == 'light':
        assert actual == '4041424344454647'
    elif toggle == 'dark':
        assert actual == '100101102103104105106107'
    else:
        assert actual == 'NoneNoneNoneNoneNoneNoneNoneNone'