コード例 #1
0
def format_usage(usage_text):
    """
    Highlight special items in a usage message.

    :param usage_text: The usage message to process (a string).
    :returns: The usage message with special items highlighted.

    This function highlights the following special items:

    - The initial line of the form "Usage: ..."
    - Short and long command line options
    - Environment variables
    - Meta variables (see :func:`find_meta_variables()`)

    All items are highlighted in the color defined by
    :data:`.HIGHLIGHT_COLOR`.
    """
    # Ugly workaround to avoid circular import errors due to interdependencies
    # between the humanfriendly.terminal and humanfriendly.usage modules.
    from humanfriendly.terminal import ansi_wrap, HIGHLIGHT_COLOR
    formatted_lines = []
    meta_variables = find_meta_variables(usage_text)
    for line in usage_text.strip().splitlines(True):
        if line.startswith(USAGE_MARKER):
            # Highlight the "Usage: ..." line in bold font and color.
            formatted_lines.append(ansi_wrap(line, color=HIGHLIGHT_COLOR))
        else:
            # Highlight options, meta variables and environment variables.
            formatted_lines.append(
                replace_special_tokens(
                    line,
                    meta_variables,
                    lambda token: ansi_wrap(token, color=HIGHLIGHT_COLOR),
                ))
    return ''.join(formatted_lines)
コード例 #2
0
ファイル: cli.py プロジェクト: hanish2760/ChatBot
def demonstrate_256_colors(i, j, group=None):
    """Demonstrate 256 color mode support."""
    # Generate the label.
    label = '256 color mode'
    if group:
        label += ' (%s)' % group
    output('\n' + ansi_wrap('%s:' % label, bold=True))
    # Generate a simple rendering of the colors in the requested range and
    # check if it will fit on a single line (given the terminal's width).
    single_line = ''.join(' ' + ansi_wrap(str(n), color=n)
                          for n in range(i, j + 1))
    lines, columns = find_terminal_size()
    if columns >= len(ansi_strip(single_line)):
        output(single_line)
    else:
        # Generate a more complex rendering of the colors that will nicely wrap
        # over multiple lines without using too many lines.
        width = len(str(j)) + 1
        colors_per_line = int(columns / width)
        colors = [
            ansi_wrap(str(n).rjust(width), color=n) for n in range(i, j + 1)
        ]
        blocks = [
            colors[n:n + colors_per_line]
            for n in range(0, len(colors), colors_per_line)
        ]
        output('\n'.join(''.join(b) for b in blocks))
コード例 #3
0
ファイル: usage.py プロジェクト: yangsongx/doc
def format_usage(usage_text):
    """
    Highlight special items in a usage message.

    :param usage_text: The usage message to process (a string).
    :returns: The usage message with special items highlighted.

    This function highlights the following special items:

    - The initial line of the form "Usage: ..."
    - Short and long command line options
    - Environment variables
    - Meta variables (see :func:`find_meta_variables()`)

    All items are highlighted in the color defined by
    :data:`.HIGHLIGHT_COLOR`.
    """
    # Ugly workaround to avoid circular import errors due to interdependencies
    # between the humanfriendly.terminal and humanfriendly.usage modules.
    from humanfriendly.terminal import ansi_wrap, HIGHLIGHT_COLOR
    formatted_lines = []
    meta_variables = find_meta_variables(usage_text)
    for line in usage_text.strip().splitlines(True):
        if line.startswith(USAGE_MARKER):
            # Highlight the "Usage: ..." line in bold font and color.
            formatted_lines.append(ansi_wrap(line, color=HIGHLIGHT_COLOR))
        else:
            # Highlight options, meta variables and environment variables.
            formatted_lines.append(replace_special_tokens(
                line, meta_variables,
                lambda token: ansi_wrap(token, color=HIGHLIGHT_COLOR),
            ))
    return ''.join(formatted_lines)
コード例 #4
0
ファイル: tests.py プロジェクト: xolox/python-coloredlogs
 def test_html_conversion(self):
     """Check the conversion from ANSI escape sequences to HTML."""
     # Check conversion of colored text.
     for color_name, ansi_code in ANSI_COLOR_CODES.items():
         ansi_encoded_text = 'plain text followed by %s text' % ansi_wrap(color_name, color=color_name)
         expected_html = format(
             '<code>plain text followed by <span style="color:{css}">{name}</span> text</code>',
             css=EIGHT_COLOR_PALETTE[ansi_code], name=color_name,
         )
         self.assertEquals(expected_html, convert(ansi_encoded_text))
     # Check conversion of bright colored text.
     expected_html = '<code><span style="color:#FF0">bright yellow</span></code>'
     self.assertEquals(expected_html, convert(ansi_wrap('bright yellow', color='yellow', bright=True)))
     # Check conversion of text with a background color.
     expected_html = '<code><span style="background-color:#DE382B">red background</span></code>'
     self.assertEquals(expected_html, convert(ansi_wrap('red background', background='red')))
     # Check conversion of text with a bright background color.
     expected_html = '<code><span style="background-color:#F00">bright red background</span></code>'
     self.assertEquals(expected_html, convert(ansi_wrap('bright red background', background='red', bright=True)))
     # Check conversion of text that uses the 256 color mode palette as a foreground color.
     expected_html = '<code><span style="color:#FFAF00">256 color mode foreground</span></code>'
     self.assertEquals(expected_html, convert(ansi_wrap('256 color mode foreground', color=214)))
     # Check conversion of text that uses the 256 color mode palette as a background color.
     expected_html = '<code><span style="background-color:#AF0000">256 color mode background</span></code>'
     self.assertEquals(expected_html, convert(ansi_wrap('256 color mode background', background=124)))
     # Check that invalid 256 color mode indexes don't raise exceptions.
     expected_html = '<code>plain text expected</code>'
     self.assertEquals(expected_html, convert('\x1b[38;5;256mplain text expected\x1b[0m'))
     # Check conversion of bold text.
     expected_html = '<code><span style="font-weight:bold">bold text</span></code>'
     self.assertEquals(expected_html, convert(ansi_wrap('bold text', bold=True)))
     # Check conversion of underlined text.
     expected_html = '<code><span style="text-decoration:underline">underlined text</span></code>'
     self.assertEquals(expected_html, convert(ansi_wrap('underlined text', underline=True)))
     # Check conversion of strike-through text.
     expected_html = '<code><span style="text-decoration:line-through">strike-through text</span></code>'
     self.assertEquals(expected_html, convert(ansi_wrap('strike-through text', strike_through=True)))
     # Check conversion of inverse text.
     expected_html = '<code><span style="background-color:#FFC706;color:#000">inverse</span></code>'
     self.assertEquals(expected_html, convert(ansi_wrap('inverse', color='yellow', inverse=True)))
     # Check conversion of URLs.
     for sample_text in 'www.python.org', 'http://coloredlogs.rtfd.org', 'https://coloredlogs.rtfd.org':
         sample_url = sample_text if '://' in sample_text else ('http://' + sample_text)
         expected_html = '<code><a href="%s" style="color:inherit">%s</a></code>' % (sample_url, sample_text)
         self.assertEquals(expected_html, convert(sample_text))
     # Check that the capture pattern for URLs doesn't match ANSI escape
     # sequences and also check that the short hand for the 0 reset code is
     # supported. These are tests for regressions of bugs found in
     # coloredlogs <= 8.0.
     reset_short_hand = '\x1b[0m'
     blue_underlined = ansi_style(color='blue', underline=True)
     ansi_encoded_text = '<%shttps://coloredlogs.readthedocs.io%s>' % (blue_underlined, reset_short_hand)
     expected_html = (
         '<code>&lt;<span style="color:#006FB8;text-decoration:underline">'
         '<a href="https://coloredlogs.readthedocs.io" style="color:inherit">'
         'https://coloredlogs.readthedocs.io'
         '</a></span>&gt;</code>'
     )
     self.assertEquals(expected_html, convert(ansi_encoded_text))
コード例 #5
0
 def test_html_conversion(self):
     """Check the conversion from ANSI escape sequences to HTML."""
     # Check conversion of colored text.
     for color_name, ansi_code in ANSI_COLOR_CODES.items():
         ansi_encoded_text = 'plain text followed by %s text' % ansi_wrap(color_name, color=color_name)
         expected_html = format(
             '<code>plain text followed by <span style="color:{css}">{name}</span> text</code>',
             css=EIGHT_COLOR_PALETTE[ansi_code], name=color_name,
         )
         self.assertEqual(expected_html, convert(ansi_encoded_text))
     # Check conversion of bright colored text.
     expected_html = '<code><span style="color:#FF0">bright yellow</span></code>'
     self.assertEqual(expected_html, convert(ansi_wrap('bright yellow', color='yellow', bright=True)))
     # Check conversion of text with a background color.
     expected_html = '<code><span style="background-color:#DE382B">red background</span></code>'
     self.assertEqual(expected_html, convert(ansi_wrap('red background', background='red')))
     # Check conversion of text with a bright background color.
     expected_html = '<code><span style="background-color:#F00">bright red background</span></code>'
     self.assertEqual(expected_html, convert(ansi_wrap('bright red background', background='red', bright=True)))
     # Check conversion of text that uses the 256 color mode palette as a foreground color.
     expected_html = '<code><span style="color:#FFAF00">256 color mode foreground</span></code>'
     self.assertEqual(expected_html, convert(ansi_wrap('256 color mode foreground', color=214)))
     # Check conversion of text that uses the 256 color mode palette as a background color.
     expected_html = '<code><span style="background-color:#AF0000">256 color mode background</span></code>'
     self.assertEqual(expected_html, convert(ansi_wrap('256 color mode background', background=124)))
     # Check that invalid 256 color mode indexes don't raise exceptions.
     expected_html = '<code>plain text expected</code>'
     self.assertEqual(expected_html, convert('\x1b[38;5;256mplain text expected\x1b[0m'))
     # Check conversion of bold text.
     expected_html = '<code><span style="font-weight:bold">bold text</span></code>'
     self.assertEqual(expected_html, convert(ansi_wrap('bold text', bold=True)))
     # Check conversion of underlined text.
     expected_html = '<code><span style="text-decoration:underline">underlined text</span></code>'
     self.assertEqual(expected_html, convert(ansi_wrap('underlined text', underline=True)))
     # Check conversion of strike-through text.
     expected_html = '<code><span style="text-decoration:line-through">strike-through text</span></code>'
     self.assertEqual(expected_html, convert(ansi_wrap('strike-through text', strike_through=True)))
     # Check conversion of inverse text.
     expected_html = '<code><span style="background-color:#FFC706;color:#000">inverse</span></code>'
     self.assertEqual(expected_html, convert(ansi_wrap('inverse', color='yellow', inverse=True)))
     # Check conversion of URLs.
     for sample_text in 'www.python.org', 'http://coloredlogs.rtfd.org', 'https://coloredlogs.rtfd.org':
         sample_url = sample_text if '://' in sample_text else ('http://' + sample_text)
         expected_html = '<code><a href="%s" style="color:inherit">%s</a></code>' % (sample_url, sample_text)
         self.assertEqual(expected_html, convert(sample_text))
     # Check that the capture pattern for URLs doesn't match ANSI escape
     # sequences and also check that the short hand for the 0 reset code is
     # supported. These are tests for regressions of bugs found in
     # coloredlogs <= 8.0.
     reset_short_hand = '\x1b[0m'
     blue_underlined = ansi_style(color='blue', underline=True)
     ansi_encoded_text = '<%shttps://coloredlogs.readthedocs.io%s>' % (blue_underlined, reset_short_hand)
     expected_html = (
         '<code>&lt;<span style="color:#006FB8;text-decoration:underline">'
         '<a href="https://coloredlogs.readthedocs.io" style="color:inherit">'
         'https://coloredlogs.readthedocs.io'
         '</a></span>&gt;</code>'
     )
     self.assertEqual(expected_html, convert(ansi_encoded_text))
コード例 #6
0
 def test_ansi_wrap(self):
     text = "Whatever"
     # Make sure ansi_wrap() does nothing when no keyword arguments are given.
     assert text == ansi_wrap(text)
     # Make sure ansi_wrap() starts the text with the CSI sequence.
     assert ansi_wrap(text, bold=True).startswith(ANSI_CSI)
     # Make sure ansi_wrap() ends the text by resetting the ANSI styles.
     assert ansi_wrap(text, bold=True).endswith(ANSI_RESET)
コード例 #7
0
ファイル: tests.py プロジェクト: wrishel/tevs_ancillary
 def test_ansi_wrap(self):
     """Test :func:`humanfriendly.terminal.ansi_wrap()`."""
     text = "Whatever"
     # Make sure ansi_wrap() does nothing when no keyword arguments are given.
     assert text == ansi_wrap(text)
     # Make sure ansi_wrap() starts the text with the CSI sequence.
     assert ansi_wrap(text, bold=True).startswith(ANSI_CSI)
     # Make sure ansi_wrap() ends the text by resetting the ANSI styles.
     assert ansi_wrap(text, bold=True).endswith(ANSI_RESET)
コード例 #8
0
ファイル: tests.py プロジェクト: wrishel/tevs_ancillary
 def test_get_pager_command(self):
     """Test :func:`humanfriendly.terminal.get_pager_command()`."""
     # Make sure --RAW-CONTROL-CHARS isn't used when it's not needed.
     assert '--RAW-CONTROL-CHARS' not in get_pager_command("Usage message")
     # Make sure --RAW-CONTROL-CHARS is used when it's needed.
     assert '--RAW-CONTROL-CHARS' in get_pager_command(ansi_wrap("Usage message", bold=True))
     # Make sure that less-specific options are only used when valid.
     options_specific_to_less = ['--no-init', '--quit-if-one-screen']
     for pager in 'cat', 'less':
         original_pager = os.environ.get('PAGER', None)
         try:
             # Set $PAGER to `cat' or `less'.
             os.environ['PAGER'] = pager
             # Get the pager command line.
             command_line = get_pager_command()
             # Check for less-specific options.
             if pager == 'less':
                 assert all(opt in command_line for opt in options_specific_to_less)
             else:
                 assert not any(opt in command_line for opt in options_specific_to_less)
         finally:
             if original_pager is not None:
                 # Restore the original $PAGER value.
                 os.environ['PAGER'] = original_pager
             else:
                 # Clear the custom $PAGER value.
                 os.environ.pop('PAGER')
コード例 #9
0
    def format(self, record):
        """
        Apply level-specific styling to log records.

        :param record: A :class:`~logging.LogRecord` object.
        :returns: The result of :func:`logging.Formatter.format()`.

        This method injects ANSI escape sequences that are specific to the
        level of each log record (because such logic cannot be expressed in the
        syntax of a log format string). It works by making a copy of the log
        record, changing the `msg` field inside the copy and passing the copy
        into the :func:`~logging.Formatter.format()` method of the base
        class.
        """
        style = self.nn.get(self.level_styles, record.levelname)
        if style:
            # Due to the way that Python's logging module is structured and
            # documented the only (IMHO) clean way to customize its behavior is
            # to change incoming LogRecord objects before they get to the base
            # formatter. However we don't want to break other formatters and
            # handlers, so we'll copy the log record.
            record = copy.copy(record)
            record.msg = ansi_wrap(coerce_string(record.msg), **style)
        # Delegate the remaining formatting to the base formatter.
        return logging.Formatter.format(self, record)
コード例 #10
0
 def callback(match):
     value = match.group(0)
     is_meta_variable = re.match('^[A-Z][A-Z0-9_]+$', value)
     if is_meta_variable and value not in meta_variables:
         return value
     else:
         return ansi_wrap(value, color=HIGHLIGHT_COLOR)
コード例 #11
0
    def format(self, record):
        """
        Apply level-specific styling to log records.

        :param record: A :class:`~logging.LogRecord` object.
        :returns: The result of :func:`logging.Formatter.format()`.

        This method injects ANSI escape sequences that are specific to the
        level of each log record (because such logic cannot be expressed in the
        syntax of a log format string). It works by making a copy of the log
        record, changing the `msg` field inside the copy and passing the copy
        into the :func:`~logging.Formatter.format()` method of the base
        class.
        """
        style = self.nn.get(self.level_styles, record.levelname)
        if style:
            # Due to the way that Python's logging module is structured and
            # documented the only (IMHO) clean way to customize its behavior is
            # to change incoming LogRecord objects before they get to the base
            # formatter. However we don't want to break other formatters and
            # handlers, so we'll copy the log record.
            record = copy.copy(record)
            record.msg = ansi_wrap(coerce_string(record.msg), **style)
        # Delegate the remaining formatting to the base formatter.
        return logging.Formatter.format(self, record)
コード例 #12
0
ファイル: ui.py プロジェクト: mmtk/ReBench
    def _output(self, text, color, *args, **kw):
        self._erase_spinner()

        if terminal_supports_colors(sys.stdout):
            text = ansi_wrap(text, color=color)
        auto_encode(sys.stdout, text, ind=_DETAIL_INDENT, *args, **kw)
        sys.stdout.flush()
コード例 #13
0
ファイル: ui.py プロジェクト: smarr/ReBench
    def _output(self, text, color, *args, **kw):
        self._erase_spinner()

        text = coerce_string(text)
        if terminal_supports_colors(sys.stdout):
            text = ansi_wrap(text, color=color)
        auto_encode(sys.stdout, text, ind=_DETAIL_INDENT, *args, **kw)
        sys.stdout.flush()
コード例 #14
0
 def test_html_conversion(self):
     """Check the conversion from ANSI escape sequences to HTML."""
     ansi_encoded_text = 'I like %s - www.eelstheband.com' % ansi_wrap('birds', bold=True, color='blue')
     assert ansi_encoded_text == 'I like \x1b[1;34mbirds\x1b[0m - www.eelstheband.com'
     html_encoded_text = convert(ansi_encoded_text)
     assert html_encoded_text == (
         'I&nbsp;like&nbsp;<span style="font-weight: bold; color: blue;">birds</span>&nbsp;-&nbsp;'
         '<a href="http://www.eelstheband.com" style="color: inherit;">www.eelstheband.com</a>'
     )
コード例 #15
0
 def test_html_conversion(self):
     """Check the conversion from ANSI escape sequences to HTML."""
     ansi_encoded_text = 'I like %s - www.eelstheband.com' % ansi_wrap('birds', bold=True, color='blue')
     assert ansi_encoded_text == 'I like \x1b[1;34mbirds\x1b[0m - www.eelstheband.com'
     html_encoded_text = convert(ansi_encoded_text)
     assert html_encoded_text == (
         'I&nbsp;like&nbsp;<span style="font-weight: bold; color: blue;">birds</span>&nbsp;-&nbsp;'
         '<a href="http://www.eelstheband.com" style="color: inherit;">www.eelstheband.com</a>'
     )
コード例 #16
0
    def _output(self, text, color, *args, **kw):
        if self._need_to_erase_spinner:
            if self._progress_spinner and self._progress_spinner.interactive:
                sys.stdout.write(erase_line_code)
            self._need_to_erase_spinner = False

        text = coerce_string(text)
        if terminal_supports_colors(sys.stdout):
            text = ansi_wrap(text, color=color)
        auto_encode(sys.stdout, text, ind=_DETAIL_INDENT, *args, **kw)
コード例 #17
0
 def format(self, record):
     style = self.nn.get(self.level_styles, record.levelname)
     if style and coloredlogs.Empty is not None:
         copy = coloredlogs.Empty()
         copy.__class__ = record.__class__
         copy.__dict__.update(record.__dict__)
         copy.levelname = ansi_wrap(coerce_string(record.levelname),
                                    **style)
         record = copy
     # Delegate the remaining formatting to the base formatter.
     return logging.Formatter.format(self, record)
コード例 #18
0
ファイル: cli.py プロジェクト: xolox/python-humanfriendly
def demonstrate_256_colors(i, j, group=None):
    """Demonstrate 256 color mode support."""
    # Generate the label.
    label = '256 color mode'
    if group:
        label += ' (%s)' % group
    output('\n' + ansi_wrap('%s:' % label, bold=True))
    # Generate a simple rendering of the colors in the requested range and
    # check if it will fit on a single line (given the terminal's width).
    single_line = ''.join(' ' + ansi_wrap(str(n), color=n) for n in range(i, j + 1))
    lines, columns = find_terminal_size()
    if columns >= len(ansi_strip(single_line)):
        output(single_line)
    else:
        # Generate a more complex rendering of the colors that will nicely wrap
        # over multiple lines without using too many lines.
        width = len(str(j)) + 1
        colors_per_line = int(columns / width)
        colors = [ansi_wrap(str(n).rjust(width), color=n) for n in range(i, j + 1)]
        blocks = [colors[n:n + colors_per_line] for n in range(0, len(colors), colors_per_line)]
        output('\n'.join(''.join(b) for b in blocks))
コード例 #19
0
def highlight(text):
    """
    Highlight a piece of text using ANSI escape sequences.

    :param text: The text to highlight (a string).
    :returns: The highlighted text (when standard output is connected to a
              terminal) or the original text (when standard output is not
              connected to a terminal).
    """
    if terminal_supports_colors(sys.stdout):
        text = ansi_wrap(text, color=HIGHLIGHT_COLOR)
    return text
コード例 #20
0
 def test_ansi_width(self):
     text = "Whatever"
     # Make sure ansi_width() works as expected on strings without ANSI escape sequences.
     assert len(text) == ansi_width(text)
     # Wrap a text in ANSI escape sequences and make sure ansi_width() treats it as expected.
     wrapped = ansi_wrap(text, bold=True)
     # Make sure ansi_wrap() changed the text.
     assert wrapped != text
     # Make sure ansi_wrap() added additional bytes.
     assert len(wrapped) > len(text)
     # Make sure the result of ansi_width() stays the same.
     assert len(text) == ansi_width(wrapped)
コード例 #21
0
ファイル: cli.py プロジェクト: ddboline/python-deb-pkg-tools
def highlight(text):
    """
    Highlight a piece of text using ANSI escape sequences.

    :param text: The text to highlight (a string).
    :returns: The highlighted text (when standard output is connected to a
              terminal) or the original text (when standard output is not
              connected to a terminal).
    """
    if terminal_supports_colors(sys.stdout):
        text = ansi_wrap(text, color=HIGHLIGHT_COLOR)
    return text
コード例 #22
0
ファイル: tests.py プロジェクト: wrishel/tevs_ancillary
 def test_ansi_width(self):
     """Test :func:`humanfriendly.terminal.ansi_width()`."""
     text = "Whatever"
     # Make sure ansi_width() works as expected on strings without ANSI escape sequences.
     assert len(text) == ansi_width(text)
     # Wrap a text in ANSI escape sequences and make sure ansi_width() treats it as expected.
     wrapped = ansi_wrap(text, bold=True)
     # Make sure ansi_wrap() changed the text.
     assert wrapped != text
     # Make sure ansi_wrap() added additional bytes.
     assert len(wrapped) > len(text)
     # Make sure the result of ansi_width() stays the same.
     assert len(text) == ansi_width(wrapped)
コード例 #23
0
ファイル: cli.py プロジェクト: xolox/python-humanfriendly
def demonstrate_ansi_formatting():
    """Demonstrate the use of ANSI escape sequences."""
    # First we demonstrate the supported text styles.
    output('%s', ansi_wrap('Text styles:', bold=True))
    styles = ['normal', 'bright']
    styles.extend(ANSI_TEXT_STYLES.keys())
    for style_name in sorted(styles):
        options = dict(color=HIGHLIGHT_COLOR)
        if style_name != 'normal':
            options[style_name] = True
        style_label = style_name.replace('_', ' ').capitalize()
        output(' - %s', ansi_wrap(style_label, **options))
    # Now we demonstrate named foreground and background colors.
    for color_type, color_label in (('color', 'Foreground colors'),
                                    ('background', 'Background colors')):
        intensities = [
            ('normal', dict()),
            ('bright', dict(bright=True)),
        ]
        if color_type != 'background':
            intensities.insert(0, ('faint', dict(faint=True)))
        output('\n%s' % ansi_wrap('%s:' % color_label, bold=True))
        output(format_smart_table([
            [color_name] + [
                ansi_wrap(
                    'XXXXXX' if color_type != 'background' else (' ' * 6),
                    **dict(list(kw.items()) + [(color_type, color_name)])
                ) for label, kw in intensities
            ] for color_name in sorted(ANSI_COLOR_CODES.keys())
        ], column_names=['Color'] + [
            label.capitalize() for label, kw in intensities
        ]))
    # Demonstrate support for 256 colors as well.
    demonstrate_256_colors(0, 7, 'standard colors')
    demonstrate_256_colors(8, 15, 'high-intensity colors')
    demonstrate_256_colors(16, 231, '216 colors')
    demonstrate_256_colors(232, 255, 'gray scale colors')
コード例 #24
0
ファイル: cli.py プロジェクト: hanish2760/ChatBot
def demonstrate_ansi_formatting():
    """Demonstrate the use of ANSI escape sequences."""
    # First we demonstrate the supported text styles.
    output('%s', ansi_wrap('Text styles:', bold=True))
    styles = ['normal', 'bright']
    styles.extend(ANSI_TEXT_STYLES.keys())
    for style_name in sorted(styles):
        options = dict(color=HIGHLIGHT_COLOR)
        if style_name != 'normal':
            options[style_name] = True
        style_label = style_name.replace('_', ' ').capitalize()
        output(' - %s', ansi_wrap(style_label, **options))
    # Now we demonstrate named foreground and background colors.
    for color_type, color_label in (('color', 'Foreground colors'),
                                    ('background', 'Background colors')):
        intensities = [
            ('normal', dict()),
            ('bright', dict(bright=True)),
        ]
        if color_type != 'background':
            intensities.insert(0, ('faint', dict(faint=True)))
        output('\n%s' % ansi_wrap('%s:' % color_label, bold=True))
        output(
            format_smart_table(
                [[color_name] + [
                    ansi_wrap(
                        'XXXXXX' if color_type != 'background' else (' ' * 6),
                        **dict(list(kw.items()) + [(color_type, color_name)]))
                    for label, kw in intensities
                ] for color_name in sorted(ANSI_COLOR_CODES.keys())],
                column_names=['Color'] +
                [label.capitalize() for label, kw in intensities]))
    # Demonstrate support for 256 colors as well.
    demonstrate_256_colors(0, 7, 'standard colors')
    demonstrate_256_colors(8, 15, 'high-intensity colors')
    demonstrate_256_colors(16, 231, '216 colors')
    demonstrate_256_colors(232, 255, 'gray scale colors')
コード例 #25
0
ファイル: prompts.py プロジェクト: divyadevadu/CAMstream
def prepare_prompt_text(prompt_text, **options):
    """
    Wrap a text to be rendered as an interactive prompt in ANSI escape sequences.

    :param prompt_text: The text to render on the prompt (a string).
    :param options: Any keyword arguments are passed on to :func:`.ansi_wrap()`.
    :returns: The resulting prompt text (a string).

    ANSI escape sequences are only used when the standard output stream is
    connected to a terminal. When the standard input stream is connected to a
    terminal any escape sequences are wrapped in "readline hints".
    """
    return (ansi_wrap(prompt_text, readline_hints=connected_to_terminal(sys.stdin), **options)
            if terminal_supports_colors(sys.stdout)
            else prompt_text)
コード例 #26
0
    def format(self, record):
        """
        Apply level-specific styling to log records.

        :param record: A :class:`~logging.LogRecord` object.
        :returns: The result of :func:`logging.Formatter.format()`.

        This method injects ANSI escape sequences that are specific to the
        level of each log record (because such logic cannot be expressed in the
        syntax of a log format string). It works by making a copy of the log
        record, changing the `msg` field inside the copy and passing the copy
        into the :func:`~logging.Formatter.format()` method of the base
        class.
        """
        style = self.nn.get(self.level_styles, record.levelname)
        # After the introduction of the `Empty' class it was reported in issue
        # 33 that format() can be called when `Empty' has already been garbage
        # collected. This explains the (otherwise rather out of place) `Empty
        # is not None' check in the following `if' statement. The reasoning
        # here is that it's much better to log a message without formatting
        # then to raise an exception ;-).
        #
        # For more details refer to issue 33 on GitHub:
        # https://github.com/xolox/python-coloredlogs/issues/33
        if style and Empty is not None:
            # Due to the way that Python's logging module is structured and
            # documented the only (IMHO) clean way to customize its behavior is
            # to change incoming LogRecord objects before they get to the base
            # formatter. However we don't want to break other formatters and
            # handlers, so we copy the log record.
            #
            # In the past this used copy.copy() but as reported in issue 29
            # (which is reproducible) this can cause deadlocks. The following
            # Python voodoo is intended to accomplish the same thing as
            # copy.copy() without all of the generalization and overhead that
            # we don't need for our -very limited- use case.
            #
            # For more details refer to issue 29 on GitHub:
            # https://github.com/xolox/python-coloredlogs/issues/29
            copy = Empty()
            copy.__class__ = logging.LogRecord
            copy.__dict__.update(record.__dict__)
            copy.msg = ansi_wrap(coerce_string(record.msg), **style)
            record = copy
        # Delegate the remaining formatting to the base formatter.
        return logging.Formatter.format(self, record)
コード例 #27
0
 def synchronize_channels(self):
     """Download messages from named channels."""
     response = self.client.channels.list()
     num_channels = len(response.body["channels"])
     for i, channel in enumerate(response.body["channels"], start=1):
         logger.verbose("Synchronizing #%s channel (%s) ..",
                        channel["name"], channel["id"])
         self.spinner.label = "Synchronizing channel %s: %s" % (
             "%i/%i" % (i, num_channels),
             ansi_wrap("#%s" % channel["name"], color=HIGHLIGHT_COLOR),
         )
         self.import_messages(
             self.client.channels,
             self.get_or_create_conversation(external_id=channel["id"],
                                             is_group_conversation=True,
                                             name=("#" + channel["name"])),
         )
コード例 #28
0
def interpret_script(shell_script):
    """Make it appear as if commands are typed into the terminal."""
    with CaptureOutput() as capturer:
        shell = subprocess.Popen(['bash', '-'], stdin=subprocess.PIPE)
        with open(shell_script) as handle:
            for line in handle:
                sys.stdout.write(ansi_wrap('$', color='green') + ' ' + line)
                sys.stdout.flush()
                shell.stdin.write(line)
                shell.stdin.flush()
            shell.stdin.close()
        time.sleep(12)
        # Get the text that was shown in the terminal.
        captured_output = capturer.get_text()
    # Store the text that was shown in the terminal.
    filename, extension = os.path.splitext(shell_script)
    transcript_file = '%s.txt' % filename
    logger.info("Updating %s ..", format_path(transcript_file))
    with open(transcript_file, 'w') as handle:
        handle.write(ansi_strip(captured_output))
コード例 #29
0
    def colorize_format(self, fmt):
        """
        Rewrite a logging format string to inject ANSI escape sequences.

        :param fmt: The log format string.
        :returns: The logging format string with ANSI escape sequences.

        This method takes a logging format string like the ones you give to
        :class:`logging.Formatter`, splits it into whitespace separated tokens
        and then processes each token as follows:

        It looks for ``%(...)`` field names in the token (from left to right). For
        each field name it checks if the field name has a style defined in the
        `field_styles` dictionary. The first field name that has a style defined
        determines the style for the complete token.

        As an example consider the default log format (:data:`DEFAULT_LOG_FORMAT`)::

         %(asctime)s %(hostname)s %(name)s[%(process)d] %(levelname)s %(message)s

        The default field styles (:data:`DEFAULT_FIELD_STYLES`) define a style for the
        `name` field but not for the `process` field, however because both fields
        are part of the same whitespace separated token they'll be highlighted
        together in the style defined for the `name` field.
        """
        result = []
        for token in fmt.split():
            # Look for field names in the token.
            for match in re.finditer(r'%\((\w+)\)', token):
                # Check if a style is defined for the matched field name.
                style = self.nn.get(self.field_styles, match.group(1))
                if style:
                    # If a style is defined we apply it.
                    token = ansi_wrap(token, **style)
                    # The style of the first field name that has a style defined
                    # `wins' (within each whitespace separated token).
                    break
            result.append(token)
        return ' '.join(result)
コード例 #30
0
ファイル: cli.py プロジェクト: plavjanik/foreman-tools
def main(argv=None):
    """Command line interface ``foreman`` for Foreman.

    :param argv: Override system arguments (sys.argv[1:]).
    """
    argv = argv or sys.argv[1:]
    args = docopt(__doc__,
                  version="Foreman Tools %s" % foreman_tools.__version__,
                  argv=argv)

    logging.basicConfig(level=logging.INFO)
    urllib3.disable_warnings()

    foreman_url = args["--url"] or DEFAULT_URL
    fs = ForemanSession(foreman_url=foreman_url)

    if args["list"] and args["hosts"]:
        # https://www.theforeman.org/api/1.14/apidoc/v2/hosts.html#description-index
        count, hosts = fs.get_all("hosts")
        for host in hosts:
            last_report = datetime.strptime(host["last_report"],
                                            "%Y-%m-%dT%H:%M:%SZ")
            last_report_age = (datetime.utcnow() - last_report).total_seconds()
            if last_report_age <= 60 * 60:
                age_color = "green"
            elif last_report_age <= 2 * 60 * 60:
                age_color = "yellow"
            else:
                age_color = "red"
            print(
                "%-40s %-15s %-10s" %
                (host["name"], host["ip"],
                 ansi_wrap(format_timespan(last_report_age), color=age_color)))

    elif args["start"] and args["<host>"]:
        fs.power(args["<host>"], "start")

    elif args["stop"] and args["<host>"]:
        fs.power(args["<host>"], "stop")
コード例 #31
0
    def colorize_format(self, fmt):
        """
        Rewrite a logging format string to inject ANSI escape sequences.

        :param fmt: The log format string.
        :returns: The logging format string with ANSI escape sequences.

        This method takes a logging format string like the ones you give to
        :class:`logging.Formatter`, splits it into whitespace separated tokens
        and then processes each token as follows:

        It looks for ``%(...)`` field names in the token (from left to right). For
        each field name it checks if the field name has a style defined in the
        `field_styles` dictionary. The first field name that has a style defined
        determines the style for the complete token.

        As an example consider the default log format (:data:`DEFAULT_LOG_FORMAT`)::

         %(asctime)s %(hostname)s %(name)s[%(process)d] %(levelname)s %(message)s

        The default field styles (:data:`DEFAULT_FIELD_STYLES`) define a style for the
        `name` field but not for the `process` field, however because both fields
        are part of the same whitespace separated token they'll be highlighted
        together in the style defined for the `name` field.
        """
        result = []
        for token in fmt.split():
            # Look for field names in the token.
            for match in re.finditer(r'%\((\w+)\)', token):
                # Check if a style is defined for the matched field name.
                style = self.nn.get(self.field_styles, match.group(1))
                if style:
                    # If a style is defined we apply it.
                    token = ansi_wrap(token, **style)
                    # The style of the first field name that has a style defined
                    # `wins' (within each whitespace separated token).
                    break
            result.append(token)
        return ' '.join(result)
コード例 #32
0
def format_usage(usage_text):
    """
    Highlight special items in a usage message.

    :param usage_text: The usage message to process (a string).
    :returns: The usage message with special items highlighted.

    This function highlights the following special items:

    - The initial line of the form "Usage: ..."
    - Short and long command line options
    - Environment variables
    - Meta variables (see :func:`find_meta_variables()`)

    All items are highlighted in the color defined by
    :data:`.HIGHLIGHT_COLOR`.
    """
    # Ugly workaround to avoid circular import errors due to interdependencies
    # between the humanfriendly.terminal and humanfriendly.usage modules.
    from humanfriendly.terminal import ansi_wrap, HIGHLIGHT_COLOR
    formatted_lines = []
    meta_variables = find_meta_variables(usage_text)
    for line in usage_text.strip().splitlines(True):
        if line.startswith('Usage:'):
            # Highlight the "Usage: ..." line in bold font and color.
            formatted_lines.append(ansi_wrap(line, color=HIGHLIGHT_COLOR))
        else:
            # Highlight options, meta variables and environment variables.
            def callback(match):
                value = match.group(0)
                is_meta_variable = re.match('^[A-Z][A-Z0-9_]+$', value)
                if is_meta_variable and value not in meta_variables:
                    return value
                else:
                    return ansi_wrap(value, color=HIGHLIGHT_COLOR)
            formatted_lines.append(USAGE_PATTERN.sub(callback, line))
    return ''.join(formatted_lines)
コード例 #33
0
ファイル: table.py プロジェクト: oxc/spinpid
 def format(self, width: int) -> str:
     value = self._format_value(width)
     if have_ansi:
         return ansi_wrap(value, color='white', bold=not self.stale)
     else:
         return value if not self.stale else f"({value})"
コード例 #34
0
def prompt_for_choice(choices, default=None, padding=True):
    """
    Prompt the user to select a choice from a group of options.

    :param choices: A sequence of strings with available options.
    :param default: The default choice if the user simply presses Enter
                    (expected to be a string, defaults to :data:`None`).
    :param padding: Refer to the documentation of :func:`prompt_for_input()`.
    :returns: The string corresponding to the user's choice.
    :raises: - :exc:`~exceptions.ValueError` if `choices` is an empty sequence.
             - Any exceptions raised by :func:`retry_limit()`.
             - Any exceptions raised by :func:`prompt_for_input()`.

    When no options are given an exception is raised:

    >>> prompt_for_choice([])
    Traceback (most recent call last):
      File "humanfriendly/prompts.py", line 148, in prompt_for_choice
        raise ValueError("Can't prompt for choice without any options!")
    ValueError: Can't prompt for choice without any options!

    If a single option is given the user isn't prompted:

    >>> prompt_for_choice(['only one choice'])
    'only one choice'

    Here's what the actual prompt looks like by default:

    >>> prompt_for_choice(['first option', 'second option'])
     <BLANKLINE>
      1. first option
      2. second option
     <BLANKLINE>
     Enter your choice as a number or unique substring (Control-C aborts): second
     <BLANKLINE>
    'second option'

    If you don't like the whitespace (empty lines and indentation):

    >>> prompt_for_choice(['first option', 'second option'], padding=False)
     1. first option
     2. second option
    Enter your choice as a number or unique substring (Control-C aborts): first
    'first option'
    """
    indent = ' ' if padding else ''
    # Make sure we can use 'choices' more than once (i.e. not a generator).
    choices = list(choices)
    if len(choices) == 1:
        # If there's only one option there's no point in prompting the user.
        logger.debug(
            "Skipping interactive prompt because there's only option (%r).",
            choices[0])
        return choices[0]
    elif not choices:
        # We can't render a choice prompt without any options.
        raise ValueError("Can't prompt for choice without any options!")
    # Generate the prompt text.
    prompt_text = ('\n\n' if padding else '\n').join([
        # Present the available choices in a user friendly way.
        "\n".join([(u" %i. %s" % (i, choice)) +
                   (" (default choice)" if choice == default else "")
                   for i, choice in enumerate(choices, start=1)]),
        # Instructions for the user.
        "Enter your choice as a number or unique substring (Control-C aborts): ",
    ])
    if terminal_supports_colors():
        prompt_text = ansi_wrap(prompt_text, bold=True, readline_hints=True)
    # Loop until a valid choice is made.
    logger.debug(
        "Requesting interactive choice on terminal (options are %s) ..",
        concatenate(map(repr, choices)))
    for attempt in retry_limit():
        reply = prompt_for_input(prompt_text, '', padding=padding, strip=True)
        if not reply and default is not None:
            logger.debug("Default choice selected by empty reply (%r).",
                         default)
            return default
        elif reply.isdigit():
            index = int(reply) - 1
            if 0 <= index < len(choices):
                logger.debug("Option (%r) selected by numeric reply (%s).",
                             choices[index], reply)
                return choices[index]
        # Check for substring matches.
        matches = []
        for choice in choices:
            lower_reply = reply.lower()
            lower_choice = choice.lower()
            if lower_reply == lower_choice:
                # If we have an 'exact' match we return it immediately.
                logger.debug("Option (%r) selected by reply (exact match).",
                             choice)
                return choice
            elif lower_reply in lower_choice and len(lower_reply) > 0:
                # Otherwise we gather substring matches.
                matches.append(choice)
        if len(matches) == 1:
            # If a single choice was matched we return it.
            logger.debug(
                "Option (%r) selected by reply (substring match on %r).",
                matches[0], reply)
            return matches[0]
        else:
            # Give the user a hint about what went wrong.
            if matches:
                details = format("text '%s' matches more than one choice: %s",
                                 reply, concatenate(matches))
            elif reply.isdigit():
                details = format("number %i is not a valid choice", int(reply))
            elif reply and not reply.isspace():
                details = format("text '%s' doesn't match any choices", reply)
            else:
                details = "there's no default choice"
            logger.debug("Got %s reply (%s), retrying (%i/%i) ..",
                         "invalid" if reply else "empty", details, attempt,
                         MAX_ATTEMPTS)
            warning("%sError: Invalid input (%s).", indent, details)
コード例 #35
0
def highlight_column_name(name):
    return ansi_wrap(name, bold=True, color=HIGHLIGHT_COLOR)
コード例 #36
0
ファイル: cli.py プロジェクト: xolox/python-apache-manager
def main():
    """Command line interface for the ``apache-manager`` program."""
    # Configure logging output.
    coloredlogs.install()
    # Command line option defaults.
    data_file = '/tmp/apache-manager.txt'
    dry_run = False
    max_memory_active = None
    max_memory_idle = None
    max_ss = None
    watch = False
    zabbix_discovery = False
    verbosity = 0
    # Parse the command line options.
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'wa:i:t:f:znvqh', [
            'watch', 'max-memory-active=', 'max-memory-idle=', 'max-ss=',
            'max-time=', 'data-file=', 'zabbix-discovery', 'dry-run',
            'simulate', 'verbose', 'quiet', 'help',
        ])
        for option, value in options:
            if option in ('-w', '--watch'):
                watch = True
            elif option in ('-a', '--max-memory-active'):
                max_memory_active = parse_size(value)
            elif option in ('-i', '--max-memory-idle'):
                max_memory_idle = parse_size(value)
            elif option in ('-t', '--max-ss', '--max-time'):
                max_ss = parse_timespan(value)
            elif option in ('-f', '--data-file'):
                data_file = value
            elif option in ('-z', '--zabbix-discovery'):
                zabbix_discovery = True
            elif option in ('-n', '--dry-run', '--simulate'):
                logger.info("Performing a dry run ..")
                dry_run = True
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
                verbosity += 1
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
                verbosity -= 1
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
    except Exception as e:
        sys.stderr.write("Error: %s!\n" % e)
        sys.exit(1)
    # Execute the requested action(s).
    manager = ApacheManager()
    try:
        if max_memory_active or max_memory_idle or max_ss:
            manager.kill_workers(
                max_memory_active=max_memory_active,
                max_memory_idle=max_memory_idle,
                timeout=max_ss,
                dry_run=dry_run,
            )
        if watch and connected_to_terminal(sys.stdout):
            watch_metrics(manager)
        elif zabbix_discovery:
            report_zabbix_discovery(manager)
        elif data_file != '-' and verbosity >= 0:
            for line in report_metrics(manager):
                if line_is_heading(line):
                    line = ansi_wrap(line, color=HIGHLIGHT_COLOR)
                print(line)
    finally:
        if (not watch) and (data_file == '-' or not dry_run):
            manager.save_metrics(data_file)
コード例 #37
0
ファイル: __init__.py プロジェクト: davliu/python-coloredlogs
 def wrap_style(self, text, **kw):
     """
     Wrapper for :py:func:`ansi_text()` that's disabled when ``isatty=False``.
     """
     return ansi_wrap(text, **kw) if self.isatty else text
コード例 #38
0
def main():
    """Command line interface for the ``apache-manager`` program."""
    # Configure logging output.
    coloredlogs.install(syslog=True)
    # Command line option defaults.
    data_file = '/tmp/apache-manager.txt'
    dry_run = False
    max_memory_active = None
    max_memory_idle = None
    max_ss = None
    watch = False
    zabbix_discovery = False
    verbosity = 0
    # Parse the command line options.
    try:
        options, arguments = getopt.getopt(sys.argv[1:], 'wa:i:t:f:znvqh', [
            'watch', 'max-memory-active=', 'max-memory-idle=', 'max-ss=',
            'max-time=', 'data-file=', 'zabbix-discovery', 'dry-run',
            'simulate', 'verbose', 'quiet', 'help',
        ])
        for option, value in options:
            if option in ('-w', '--watch'):
                watch = True
            elif option in ('-a', '--max-memory-active'):
                max_memory_active = parse_size(value)
            elif option in ('-i', '--max-memory-idle'):
                max_memory_idle = parse_size(value)
            elif option in ('-t', '--max-ss', '--max-time'):
                max_ss = parse_timespan(value)
            elif option in ('-f', '--data-file'):
                data_file = value
            elif option in ('-z', '--zabbix-discovery'):
                zabbix_discovery = True
            elif option in ('-n', '--dry-run', '--simulate'):
                logger.info("Performing a dry run ..")
                dry_run = True
            elif option in ('-v', '--verbose'):
                coloredlogs.increase_verbosity()
                verbosity += 1
            elif option in ('-q', '--quiet'):
                coloredlogs.decrease_verbosity()
                verbosity -= 1
            elif option in ('-h', '--help'):
                usage(__doc__)
                return
    except Exception as e:
        sys.stderr.write("Error: %s!\n" % e)
        sys.exit(1)
    # Execute the requested action(s).
    manager = ApacheManager()
    try:
        if max_memory_active or max_memory_idle or max_ss:
            manager.kill_workers(
                max_memory_active=max_memory_active,
                max_memory_idle=max_memory_idle,
                timeout=max_ss,
                dry_run=dry_run,
            )
        elif watch and connected_to_terminal(sys.stdout):
            watch_metrics(manager)
        elif zabbix_discovery:
            report_zabbix_discovery(manager)
        elif data_file != '-' and verbosity >= 0:
            for line in report_metrics(manager):
                if line_is_heading(line):
                    line = ansi_wrap(line, color=HIGHLIGHT_COLOR)
                print(line)
    finally:
        if (not watch) and (data_file == '-' or not dry_run):
            manager.save_metrics(data_file)
コード例 #39
0
ファイル: tests.py プロジェクト: davliu/python-coloredlogs
 def test_html_conversion(self):
     ansi_encoded_text = 'I like %s - www.eelstheband.com' % ansi_wrap('birds', bold=True, color='blue')
     assert ansi_encoded_text == 'I like \x1b[1;34mbirds\x1b[0m - www.eelstheband.com'
     html_encoded_text = coloredlogs.converter.convert(ansi_encoded_text)
     assert html_encoded_text == 'I&nbsp;like&nbsp;<span style="font-weight: bold; color: blue;">birds</span>&nbsp;-&nbsp;<a href="http://www.eelstheband.com" style="color: inherit;">www.eelstheband.com</a>'
コード例 #40
0
ファイル: base.py プロジェクト: sathish86/odin
 def title(self, value, underline='='):
     self.print(
         ansi_wrap(value, bold=True, color=HIGHLIGHT_COLOR),
         underline * len(value),
         sep='\n'
     )
コード例 #41
0
    def format_text(self, include_password=True, use_colors=None, padding=True, filters=()):
        """
        Format :attr:`text` for viewing on a terminal.

        :param include_password: :data:`True` to include the password in the
                                 formatted text, :data:`False` to exclude the
                                 password from the formatted text.
        :param use_colors: :data:`True` to use ANSI escape sequences,
                           :data:`False` otherwise. When this is :data:`None`
                           :func:`~humanfriendly.terminal.terminal_supports_colors()`
                           will be used to detect whether ANSI escape sequences
                           are supported.
        :param padding: :data:`True` to add empty lines before and after the
                        entry and indent the entry's text with two spaces,
                        :data:`False` to skip the padding.
        :param filters: An iterable of regular expression patterns (defaults to
                        an empty tuple). If a line in the entry's text matches
                        one of these patterns it won't be shown on the
                        terminal.
        :returns: The formatted entry (a string).
        """
        # Determine whether we can use ANSI escape sequences.
        if use_colors is None:
            use_colors = terminal_supports_colors()
        # Extract the password (first line) from the entry.
        lines = self.text.splitlines()
        password = lines.pop(0).strip()
        # Compile the given patterns to case insensitive regular expressions
        # and use them to ignore lines that match any of the given filters.
        patterns = [coerce_pattern(f, re.IGNORECASE) for f in filters]
        lines = [l for l in lines if not any(p.search(l) for p in patterns)]
        text = trim_empty_lines("\n".join(lines))
        # Include the password in the formatted text?
        if include_password:
            text = "Password: %s\n%s" % (password, text)
        # Add the name to the entry (only when there's something to show).
        if text and not text.isspace():
            title = " / ".join(split(self.name, "/"))
            if use_colors:
                title = ansi_wrap(title, bold=True)
            text = "%s\n\n%s" % (title, text)
        # Highlight the entry's text using ANSI escape sequences.
        lines = []
        for line in text.splitlines():
            # Check for a "Key: Value" line.
            match = KEY_VALUE_PATTERN.match(line)
            if match:
                key = "%s:" % match.group(1).strip()
                value = match.group(2).strip()
                if use_colors:
                    # Highlight the key.
                    key = ansi_wrap(key, color=HIGHLIGHT_COLOR)
                    # Underline hyperlinks in the value.
                    tokens = value.split()
                    for i in range(len(tokens)):
                        if "://" in tokens[i]:
                            tokens[i] = ansi_wrap(tokens[i], underline=True)
                    # Replace the line with a highlighted version.
                    line = key + " " + " ".join(tokens)
            if padding:
                line = "  " + line
            lines.append(line)
        text = "\n".join(lines)
        text = trim_empty_lines(text)
        if text and padding:
            text = "\n%s\n" % text
        return text
コード例 #42
0
ファイル: prompts.py プロジェクト: ftri/python-humanfriendly
def prompt_for_choice(choices, default=None, padding=True):
    """
    Prompt the user to select a choice from a group of options.

    :param choices: A sequence of strings with available options.
    :param default: The default choice if the user simply presses Enter
                    (expected to be a string, defaults to :data:`None`).
    :param padding: Refer to the documentation of :func:`prompt_for_input()`.
    :returns: The string corresponding to the user's choice.
    :raises: - :exc:`~exceptions.ValueError` if `choices` is an empty sequence.
             - Any exceptions raised by :func:`retry_limit()`.
             - Any exceptions raised by :func:`prompt_for_input()`.

    When no options are given an exception is raised:

    >>> prompt_for_choice([])
    Traceback (most recent call last):
      File "humanfriendly/prompts.py", line 148, in prompt_for_choice
        raise ValueError("Can't prompt for choice without any options!")
    ValueError: Can't prompt for choice without any options!

    If a single option is given the user isn't prompted:

    >>> prompt_for_choice(['only one choice'])
    'only one choice'

    Here's what the actual prompt looks like by default:

    >>> prompt_for_choice(['first option', 'second option'])
     <BLANKLINE>
      1. first option
      2. second option
     <BLANKLINE>
     Enter your choice as a number or unique substring (Control-C aborts): second
     <BLANKLINE>
    'second option'

    If you don't like the whitespace (empty lines and indentation):

    >>> prompt_for_choice(['first option', 'second option'], padding=False)
     1. first option
     2. second option
    Enter your choice as a number or unique substring (Control-C aborts): first
    'first option'
    """
    indent = ' ' if padding else ''
    # Make sure we can use 'choices' more than once (i.e. not a generator).
    choices = list(choices)
    if len(choices) == 1:
        # If there's only one option there's no point in prompting the user.
        logger.debug("Skipping interactive prompt because there's only option (%r).", choices[0])
        return choices[0]
    elif not choices:
        # We can't render a choice prompt without any options.
        raise ValueError("Can't prompt for choice without any options!")
    # Generate the prompt text.
    prompt_text = ('\n\n' if padding else '\n').join([
        # Present the available choices in a user friendly way.
        "\n".join([
            (u" %i. %s" % (i, choice)) + (" (default choice)" if choice == default else "")
            for i, choice in enumerate(choices, start=1)
        ]),
        # Instructions for the user.
        "Enter your choice as a number or unique substring (Control-C aborts): ",
    ])
    if terminal_supports_colors():
        prompt_text = ansi_wrap(prompt_text, bold=True, readline_hints=True)
    # Loop until a valid choice is made.
    logger.debug("Requesting interactive choice on terminal (options are %s) ..",
                 concatenate(map(repr, choices)))
    for attempt in retry_limit():
        reply = prompt_for_input(prompt_text, '', padding=padding, strip=True)
        if not reply and default is not None:
            logger.debug("Default choice selected by empty reply (%r).", default)
            return default
        elif reply.isdigit():
            index = int(reply) - 1
            if 0 <= index < len(choices):
                logger.debug("Option (%r) selected by numeric reply (%s).", choices[index], reply)
                return choices[index]
        # Check for substring matches.
        matches = []
        for choice in choices:
            lower_reply = reply.lower()
            lower_choice = choice.lower()
            if lower_reply == lower_choice:
                # If we have an 'exact' match we return it immediately.
                logger.debug("Option (%r) selected by reply (exact match).", choice, reply)
                return choice
            elif lower_reply in lower_choice and len(lower_reply) > 0:
                # Otherwise we gather substring matches.
                matches.append(choice)
        if len(matches) == 1:
            # If a single choice was matched we return it.
            logger.debug("Option (%r) selected by reply (substring match on %r).", matches[0], reply)
            return matches[0]
        else:
            # Give the user a hint about what went wrong.
            if matches:
                details = format("text '%s' matches more than one choice: %s", reply, concatenate(matches))
            elif reply.isdigit():
                details = format("number %i is not a valid choice", int(reply))
            elif reply and not reply.isspace():
                details = format("text '%s' doesn't match any choices", reply)
            else:
                details = "there's no default choice"
            logger.debug("Got %s reply (%s), retrying (%i/%i) ..",
                         "invalid" if reply else "empty", details,
                         attempt, MAX_ATTEMPTS)
            warning("%sError: Invalid input (%s).", indent, details)
コード例 #43
0
 def format_item(self, key: str, val: Any) -> str:
     return "{}={}".format(
         (ansi_wrap(key, **self.style_key) if self.color else key),
         (ansi_wrap(repr(val), **self.style_val)
          if self.color else repr(val)))
コード例 #44
0
 def render_summary(self):
     """Render a summary of installed and removable kernel packages on the terminal."""
     logger.verbose("Sanity checking meta packages on %s ..", self.context)
     with AutomaticSpinner(label="Gathering information about %s" %
                           self.context):
         # Report the installed Linux kernel image meta package(s).
         if self.installed_image_meta_packages:
             logger.info(
                 "Found %s installed:",
                 pluralize(len(self.installed_image_meta_packages),
                           "Linux kernel image meta package"),
             )
             for package in self.installed_image_meta_packages:
                 logger.info(" - %s (%s)", package.name, package.version)
             if len(self.installed_image_meta_packages) > 1:
                 names = concatenate(
                     pkg.name for pkg in self.installed_image_meta_packages)
                 logger.warning(
                     compact(
                         """
                         You have more than one Linux kernel image meta
                         package installed ({names}) which means automatic
                         package removal can be unreliable!
                         """,
                         names=names,
                     ))
                 logger.verbose(
                     compact("""
                         I would suggest to stick to one Linux kernel image
                         meta package, preferably the one that matches the
                         newest kernel :-)
                         """))
         else:
             logger.warning(
                 compact("""
                     It looks like there's no Linux kernel image meta
                     package installed! I hope you've thought about how to
                     handle security updates?
                     """))
         # Report the installed Linux kernel header/image package(s).
         logger.verbose("Checking for removable packages on %s ..",
                        self.context)
         package_types = (
             (self.installed_kernel_packages, "image", True),
             (self.installed_header_packages, "header", False),
             (self.installed_modules_packages, "modules", False),
         )
         for collection, label, expected in package_types:
             if collection:
                 logger.info(
                     "Found %s:",
                     pluralize(len(collection),
                               "installed Linux kernel %s package" % label))
                 for group in self.installed_package_groups:
                     matching_packages = sorted(package.name
                                                for package in group
                                                if package in collection)
                     active_group = any(
                         package.name == self.active_kernel_package
                         for package in group)
                     removable_group = group in self.removable_package_groups
                     if matching_packages:
                         logger.info(
                             " - %s (%s)",
                             concatenate(matching_packages),
                             ansi_wrap("removable", color="green")
                             if removable_group else ansi_wrap(
                                 "the active kernel" if active_group else
                                 ("one of %i newest kernels" %
                                  self.preserve_count),
                                 color="blue",
                             ),
                         )
             elif expected:
                 logger.warning(
                     "No installed %s packages found, this can't be right?!",
                     label)
         # Report the removable packages.
         if self.removable_packages:
             logger.info("Found %s that can be removed.",
                         pluralize(len(self.removable_packages), "package"))
             # Report the shell command to remove the packages.
             logger.verbose("Command to remove packages: %s",
                            " ".join(self.cleanup_command))
         else:
             logger.info("No packages need to be removed! :-)")
コード例 #45
0
 def title(self, value, underline='='):
     self.print([ansi_wrap(value, bold=True, color=HIGHLIGHT_COLOR), underline * len(value)], sep='\n')
コード例 #46
0
ファイル: prompts.py プロジェクト: ftri/python-humanfriendly
def prompt_for_confirmation(question, default=None, padding=True):
    """
    Prompt the user for confirmation.

    :param question: The text that explains what the user is confirming (a string).
    :param default: The default value (a boolean) or :data:`None`.
    :param padding: Refer to the documentation of :func:`prompt_for_input()`.
    :returns: - If the user enters 'yes' or 'y' then :data:`True` is returned.
              - If the user enters 'no' or 'n' then :data:`False`  is returned.
              - If the user doesn't enter any text or standard input is not
                connected to a terminal (which makes it impossible to prompt
                the user) the value of the keyword argument ``default`` is
                returned (if that value is not :data:`None`).
    :raises: - Any exceptions raised by :func:`retry_limit()`.
             - Any exceptions raised by :func:`prompt_for_input()`.

    When `default` is :data:`False` and the user doesn't enter any text an
    error message is printed and the prompt is repeated:

    >>> prompt_for_confirmation("Are you sure?")
     <BLANKLINE>
     Are you sure? [y/n]
     <BLANKLINE>
     Error: Please enter 'yes' or 'no' (there's no default choice).
     <BLANKLINE>
     Are you sure? [y/n]

    The same thing happens when the user enters text that isn't recognized:

    >>> prompt_for_confirmation("Are you sure?")
     <BLANKLINE>
     Are you sure? [y/n] about what?
     <BLANKLINE>
     Error: Please enter 'yes' or 'no' (the text 'about what?' is not recognized).
     <BLANKLINE>
     Are you sure? [y/n]
    """
    # Generate the text for the prompt.
    prompt_text = question
    if terminal_supports_colors():
        prompt_text = ansi_wrap(prompt_text, bold=True, readline_hints=True)
    # Append the valid replies (and default reply) to the prompt text.
    hint = "[Y/n]" if default else "[y/N]" if default is not None else "[y/n]"
    if terminal_supports_colors():
        hint = ansi_wrap(hint, color=HIGHLIGHT_COLOR, readline_hints=True)
    prompt_text += " %s " % hint
    # Loop until a valid response is given.
    logger.debug("Requesting interactive confirmation from terminal: %r", ansi_strip(prompt_text).rstrip())
    for attempt in retry_limit():
        reply = prompt_for_input(prompt_text, '', padding=padding, strip=True)
        if reply.lower() in ('y', 'yes'):
            logger.debug("Confirmation granted by reply (%r).", reply)
            return True
        elif reply.lower() in ('n', 'no'):
            logger.debug("Confirmation denied by reply (%r).", reply)
            return False
        elif (not reply) and default is not None:
            logger.debug("Default choice selected by empty reply (%r).",
                         "granted" if default else "denied")
            return default
        else:
            details = ("the text '%s' is not recognized" % reply
                       if reply else "there's no default choice")
            logger.debug("Got %s reply (%s), retrying (%i/%i) ..",
                         "invalid" if reply else "empty", details,
                         attempt, MAX_ATTEMPTS)
            warning("{indent}Error: Please enter 'yes' or 'no' ({details}).",
                    indent=' ' if padding else '', details=details)
コード例 #47
0
def prompt_for_confirmation(question, default=None, padding=True):
    """
    Prompt the user for confirmation.

    :param question: The text that explains what the user is confirming (a string).
    :param default: The default value (a boolean) or :data:`None`.
    :param padding: Refer to the documentation of :func:`prompt_for_input()`.
    :returns: - If the user enters 'yes' or 'y' then :data:`True` is returned.
              - If the user enters 'no' or 'n' then :data:`False`  is returned.
              - If the user doesn't enter any text or standard input is not
                connected to a terminal (which makes it impossible to prompt
                the user) the value of the keyword argument ``default`` is
                returned (if that value is not :data:`None`).
    :raises: - Any exceptions raised by :func:`retry_limit()`.
             - Any exceptions raised by :func:`prompt_for_input()`.

    When `default` is :data:`False` and the user doesn't enter any text an
    error message is printed and the prompt is repeated:

    >>> prompt_for_confirmation("Are you sure?")
     <BLANKLINE>
     Are you sure? [y/n]
     <BLANKLINE>
     Error: Please enter 'yes' or 'no' (there's no default choice).
     <BLANKLINE>
     Are you sure? [y/n]

    The same thing happens when the user enters text that isn't recognized:

    >>> prompt_for_confirmation("Are you sure?")
     <BLANKLINE>
     Are you sure? [y/n] about what?
     <BLANKLINE>
     Error: Please enter 'yes' or 'no' (the text 'about what?' is not recognized).
     <BLANKLINE>
     Are you sure? [y/n]
    """
    # Generate the text for the prompt.
    prompt_text = question
    if terminal_supports_colors():
        prompt_text = ansi_wrap(prompt_text, bold=True, readline_hints=True)
    # Append the valid replies (and default reply) to the prompt text.
    hint = "[Y/n]" if default else "[y/N]" if default is not None else "[y/n]"
    if terminal_supports_colors():
        hint = ansi_wrap(hint, color=HIGHLIGHT_COLOR, readline_hints=True)
    prompt_text += " %s " % hint
    # Loop until a valid response is given.
    logger.debug("Requesting interactive confirmation from terminal: %r",
                 ansi_strip(prompt_text).rstrip())
    for attempt in retry_limit():
        reply = prompt_for_input(prompt_text, '', padding=padding, strip=True)
        if reply.lower() in ('y', 'yes'):
            logger.debug("Confirmation granted by reply (%r).", reply)
            return True
        elif reply.lower() in ('n', 'no'):
            logger.debug("Confirmation denied by reply (%r).", reply)
            return False
        elif (not reply) and default is not None:
            logger.debug("Default choice selected by empty reply (%r).",
                         "granted" if default else "denied")
            return default
        else:
            details = ("the text '%s' is not recognized" %
                       reply if reply else "there's no default choice")
            logger.debug("Got %s reply (%s), retrying (%i/%i) ..",
                         "invalid" if reply else "empty", details, attempt,
                         MAX_ATTEMPTS)
            warning("{indent}Error: Please enter 'yes' or 'no' ({details}).",
                    indent=' ' if padding else '',
                    details=details)
コード例 #48
0
ファイル: tables.py プロジェクト: Flimm/python-humanfriendly
def highlight_column_name(name):
    return ansi_wrap(name, bold=True, color=HIGHLIGHT_COLOR)
コード例 #49
0
    def format_text(self,
                    include_password=True,
                    use_colors=None,
                    padding=True):
        """
        Format :attr:`text` for viewing on a terminal.

        :param include_password: :data:`True` to include the password in the
                                 formatted text, :data:`False` to exclude the
                                 password from the formatted text.
        :param use_colors: :data:`True` to use ANSI escape sequences,
                           :data:`False` otherwise. When this is :data:`None`
                           :func:`~humanfriendly.terminal.terminal_supports_colors()`
                           will be used to detect whether ANSI escape sequences
                           are supported.
        :param padding: :data:`True` to add empty lines before and after the
                        entry and indent the entry's text with two spaces,
                        :data:`False` to skip the padding.
        :returns: The formatted entry (a string).
        """
        # Determine whether we can use ANSI escape sequences.
        if use_colors is None:
            use_colors = terminal_supports_colors()
        # Extract the password (first line) from the entry.
        lines = self.text.splitlines()
        password = lines.pop(0).strip()
        text = trim_empty_lines('\n'.join(lines))
        # Include the password in the formatted text?
        if include_password:
            text = "Password: %s\n%s" % (password, text)
        # Add the name to the entry (only when there's something to show).
        if text and not text.isspace():
            title = ' / '.join(split(self.name, '/'))
            if use_colors:
                title = ansi_wrap(title, bold=True)
            text = "%s\n\n%s" % (title, text)
        # Highlight the entry's text using ANSI escape sequences.
        lines = []
        for line in text.splitlines():
            # Check for a "Key: Value" line.
            match = KEY_VALUE_PATTERN.match(line)
            if match:
                key = "%s:" % match.group(1).strip()
                value = match.group(2).strip()
                if use_colors:
                    # Highlight the key.
                    key = ansi_wrap(key, color=HIGHLIGHT_COLOR)
                    # Underline hyperlinks in the value.
                    tokens = value.split()
                    for i in range(len(tokens)):
                        if '://' in tokens[i]:
                            tokens[i] = ansi_wrap(tokens[i], underline=True)
                    # Replace the line with a highlighted version.
                    line = key + ' ' + ' '.join(tokens)
            if padding:
                line = '  ' + line
            lines.append(line)
        text = '\n'.join(lines)
        if text and padding:
            text = '\n%s\n' % text
        return text