diff env/lib/python3.9/site-packages/humanfriendly/terminal/__init__.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/env/lib/python3.9/site-packages/humanfriendly/terminal/__init__.py	Mon Mar 22 18:12:50 2021 +0000
@@ -0,0 +1,776 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: March 1, 2020
+# URL: https://humanfriendly.readthedocs.io
+
+"""
+Interaction with interactive text terminals.
+
+The :mod:`~humanfriendly.terminal` module makes it easy to interact with
+interactive text terminals and format text for rendering on such terminals. If
+the terms used in the documentation of this module don't make sense to you then
+please refer to the `Wikipedia article on ANSI escape sequences`_ for details
+about how ANSI escape sequences work.
+
+This module was originally developed for use on UNIX systems, but since then
+Windows 10 gained native support for ANSI escape sequences and this module was
+enhanced to recognize and support this. For details please refer to the
+:func:`enable_ansi_support()` function.
+
+.. _Wikipedia article on ANSI escape sequences: http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
+"""
+
+# Standard library modules.
+import codecs
+import numbers
+import os
+import platform
+import re
+import subprocess
+import sys
+
+# The `fcntl' module is platform specific so importing it may give an error. We
+# hide this implementation detail from callers by handling the import error and
+# setting a flag instead.
+try:
+    import fcntl
+    import termios
+    import struct
+    HAVE_IOCTL = True
+except ImportError:
+    HAVE_IOCTL = False
+
+# Modules included in our package.
+from humanfriendly.compat import coerce_string, is_unicode, on_windows, which
+from humanfriendly.decorators import cached
+from humanfriendly.deprecation import define_aliases
+from humanfriendly.text import concatenate, format
+from humanfriendly.usage import format_usage
+
+# Public identifiers that require documentation.
+__all__ = (
+    'ANSI_COLOR_CODES',
+    'ANSI_CSI',
+    'ANSI_ERASE_LINE',
+    'ANSI_HIDE_CURSOR',
+    'ANSI_RESET',
+    'ANSI_SGR',
+    'ANSI_SHOW_CURSOR',
+    'ANSI_TEXT_STYLES',
+    'CLEAN_OUTPUT_PATTERN',
+    'DEFAULT_COLUMNS',
+    'DEFAULT_ENCODING',
+    'DEFAULT_LINES',
+    'HIGHLIGHT_COLOR',
+    'ansi_strip',
+    'ansi_style',
+    'ansi_width',
+    'ansi_wrap',
+    'auto_encode',
+    'clean_terminal_output',
+    'connected_to_terminal',
+    'enable_ansi_support',
+    'find_terminal_size',
+    'find_terminal_size_using_ioctl',
+    'find_terminal_size_using_stty',
+    'get_pager_command',
+    'have_windows_native_ansi_support',
+    'message',
+    'output',
+    'readline_strip',
+    'readline_wrap',
+    'show_pager',
+    'terminal_supports_colors',
+    'usage',
+    'warning',
+)
+
+ANSI_CSI = '\x1b['
+"""The ANSI "Control Sequence Introducer" (a string)."""
+
+ANSI_SGR = 'm'
+"""The ANSI "Select Graphic Rendition" sequence (a string)."""
+
+ANSI_ERASE_LINE = '%sK' % ANSI_CSI
+"""The ANSI escape sequence to erase the current line (a string)."""
+
+ANSI_RESET = '%s0%s' % (ANSI_CSI, ANSI_SGR)
+"""The ANSI escape sequence to reset styling (a string)."""
+
+ANSI_HIDE_CURSOR = '%s?25l' % ANSI_CSI
+"""The ANSI escape sequence to hide the text cursor (a string)."""
+
+ANSI_SHOW_CURSOR = '%s?25h' % ANSI_CSI
+"""The ANSI escape sequence to show the text cursor (a string)."""
+
+ANSI_COLOR_CODES = dict(black=0, red=1, green=2, yellow=3, blue=4, magenta=5, cyan=6, white=7)
+"""
+A dictionary with (name, number) pairs of `portable color codes`_. Used by
+:func:`ansi_style()` to generate ANSI escape sequences that change font color.
+
+.. _portable color codes: http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
+"""
+
+ANSI_TEXT_STYLES = dict(bold=1, faint=2, italic=3, underline=4, inverse=7, strike_through=9)
+"""
+A dictionary with (name, number) pairs of text styles (effects). Used by
+:func:`ansi_style()` to generate ANSI escape sequences that change text
+styles. Only widely supported text styles are included here.
+"""
+
+CLEAN_OUTPUT_PATTERN = re.compile(u'(\r|\n|\b|%s)' % re.escape(ANSI_ERASE_LINE))
+"""
+A compiled regular expression used to separate significant characters from other text.
+
+This pattern is used by :func:`clean_terminal_output()` to split terminal
+output into regular text versus backspace, carriage return and line feed
+characters and ANSI 'erase line' escape sequences.
+"""
+
+DEFAULT_LINES = 25
+"""The default number of lines in a terminal (an integer)."""
+
+DEFAULT_COLUMNS = 80
+"""The default number of columns in a terminal (an integer)."""
+
+DEFAULT_ENCODING = 'UTF-8'
+"""The output encoding for Unicode strings."""
+
+HIGHLIGHT_COLOR = os.environ.get('HUMANFRIENDLY_HIGHLIGHT_COLOR', 'green')
+"""
+The color used to highlight important tokens in formatted text (e.g. the usage
+message of the ``humanfriendly`` program). If the environment variable
+``$HUMANFRIENDLY_HIGHLIGHT_COLOR`` is set it determines the value of
+:data:`HIGHLIGHT_COLOR`.
+"""
+
+
+def ansi_strip(text, readline_hints=True):
+    """
+    Strip ANSI escape sequences from the given string.
+
+    :param text: The text from which ANSI escape sequences should be removed (a
+                 string).
+    :param readline_hints: If :data:`True` then :func:`readline_strip()` is
+                           used to remove `readline hints`_ from the string.
+    :returns: The text without ANSI escape sequences (a string).
+    """
+    pattern = '%s.*?%s' % (re.escape(ANSI_CSI), re.escape(ANSI_SGR))
+    text = re.sub(pattern, '', text)
+    if readline_hints:
+        text = readline_strip(text)
+    return text
+
+
+def ansi_style(**kw):
+    """
+    Generate ANSI escape sequences for the given color and/or style(s).
+
+    :param color: The foreground color. Three types of values are supported:
+
+                  - The name of a color (one of the strings 'black', 'red',
+                    'green', 'yellow', 'blue', 'magenta', 'cyan' or 'white').
+                  - An integer that refers to the 256 color mode palette.
+                  - A tuple or list with three integers representing an RGB
+                    (red, green, blue) value.
+
+                  The value :data:`None` (the default) means no escape
+                  sequence to switch color will be emitted.
+    :param background: The background color (see the description
+                       of the `color` argument).
+    :param bright: Use high intensity colors instead of default colors
+                   (a boolean, defaults to :data:`False`).
+    :param readline_hints: If :data:`True` then :func:`readline_wrap()` is
+                           applied to the generated ANSI escape sequences (the
+                           default is :data:`False`).
+    :param kw: Any additional keyword arguments are expected to match a key
+               in the :data:`ANSI_TEXT_STYLES` dictionary. If the argument's
+               value evaluates to :data:`True` the respective style will be
+               enabled.
+    :returns: The ANSI escape sequences to enable the requested text styles or
+              an empty string if no styles were requested.
+    :raises: :exc:`~exceptions.ValueError` when an invalid color name is given.
+
+    Even though only eight named colors are supported, the use of `bright=True`
+    and `faint=True` increases the number of available colors to around 24 (it
+    may be slightly lower, for example because faint black is just black).
+
+    **Support for 8-bit colors**
+
+    In `release 4.7`_ support for 256 color mode was added. While this
+    significantly increases the available colors it's not very human friendly
+    in usage because you need to look up color codes in the `256 color mode
+    palette <https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit>`_.
+
+    You can use the ``humanfriendly --demo`` command to get a demonstration of
+    the available colors, see also the screen shot below. Note that the small
+    font size in the screen shot was so that the demonstration of 256 color
+    mode support would fit into a single screen shot without scrolling :-)
+    (I wasn't feeling very creative).
+
+      .. image:: images/ansi-demo.png
+
+    **Support for 24-bit colors**
+
+    In `release 4.14`_ support for 24-bit colors was added by accepting a tuple
+    or list with three integers representing the RGB (red, green, blue) value
+    of a color. This is not included in the demo because rendering millions of
+    colors was deemed unpractical ;-).
+
+    .. _release 4.7: http://humanfriendly.readthedocs.io/en/latest/changelog.html#release-4-7-2018-01-14
+    .. _release 4.14: http://humanfriendly.readthedocs.io/en/latest/changelog.html#release-4-14-2018-07-13
+    """
+    # Start with sequences that change text styles.
+    sequences = [ANSI_TEXT_STYLES[k] for k, v in kw.items() if k in ANSI_TEXT_STYLES and v]
+    # Append the color code (if any).
+    for color_type in 'color', 'background':
+        color_value = kw.get(color_type)
+        if isinstance(color_value, (tuple, list)):
+            if len(color_value) != 3:
+                msg = "Invalid color value %r! (expected tuple or list with three numbers)"
+                raise ValueError(msg % color_value)
+            sequences.append(48 if color_type == 'background' else 38)
+            sequences.append(2)
+            sequences.extend(map(int, color_value))
+        elif isinstance(color_value, numbers.Number):
+            # Numeric values are assumed to be 256 color codes.
+            sequences.extend((
+                39 if color_type == 'background' else 38,
+                5, int(color_value)
+            ))
+        elif color_value:
+            # Other values are assumed to be strings containing one of the known color names.
+            if color_value not in ANSI_COLOR_CODES:
+                msg = "Invalid color value %r! (expected an integer or one of the strings %s)"
+                raise ValueError(msg % (color_value, concatenate(map(repr, sorted(ANSI_COLOR_CODES)))))
+            # Pick the right offset for foreground versus background
+            # colors and regular intensity versus bright colors.
+            offset = (
+                (100 if kw.get('bright') else 40)
+                if color_type == 'background'
+                else (90 if kw.get('bright') else 30)
+            )
+            # Combine the offset and color code into a single integer.
+            sequences.append(offset + ANSI_COLOR_CODES[color_value])
+    if sequences:
+        encoded = ANSI_CSI + ';'.join(map(str, sequences)) + ANSI_SGR
+        return readline_wrap(encoded) if kw.get('readline_hints') else encoded
+    else:
+        return ''
+
+
+def ansi_width(text):
+    """
+    Calculate the effective width of the given text (ignoring ANSI escape sequences).
+
+    :param text: The text whose width should be calculated (a string).
+    :returns: The width of the text without ANSI escape sequences (an
+              integer).
+
+    This function uses :func:`ansi_strip()` to strip ANSI escape sequences from
+    the given string and returns the length of the resulting string.
+    """
+    return len(ansi_strip(text))
+
+
+def ansi_wrap(text, **kw):
+    """
+    Wrap text in ANSI escape sequences for the given color and/or style(s).
+
+    :param text: The text to wrap (a string).
+    :param kw: Any keyword arguments are passed to :func:`ansi_style()`.
+    :returns: The result of this function depends on the keyword arguments:
+
+              - If :func:`ansi_style()` generates an ANSI escape sequence based
+                on the keyword arguments, the given text is prefixed with the
+                generated ANSI escape sequence and suffixed with
+                :data:`ANSI_RESET`.
+
+              - If :func:`ansi_style()` returns an empty string then the text
+                given by the caller is returned unchanged.
+    """
+    start_sequence = ansi_style(**kw)
+    if start_sequence:
+        end_sequence = ANSI_RESET
+        if kw.get('readline_hints'):
+            end_sequence = readline_wrap(end_sequence)
+        return start_sequence + text + end_sequence
+    else:
+        return text
+
+
+def auto_encode(stream, text, *args, **kw):
+    """
+    Reliably write Unicode strings to the terminal.
+
+    :param stream: The file-like object to write to (a value like
+                   :data:`sys.stdout` or :data:`sys.stderr`).
+    :param text: The text to write to the stream (a string).
+    :param args: Refer to :func:`~humanfriendly.text.format()`.
+    :param kw: Refer to :func:`~humanfriendly.text.format()`.
+
+    Renders the text using :func:`~humanfriendly.text.format()` and writes it
+    to the given stream. If an :exc:`~exceptions.UnicodeEncodeError` is
+    encountered in doing so, the text is encoded using :data:`DEFAULT_ENCODING`
+    and the write is retried. The reasoning behind this rather blunt approach
+    is that it's preferable to get output on the command line in the wrong
+    encoding then to have the Python program blow up with a
+    :exc:`~exceptions.UnicodeEncodeError` exception.
+    """
+    text = format(text, *args, **kw)
+    try:
+        stream.write(text)
+    except UnicodeEncodeError:
+        stream.write(codecs.encode(text, DEFAULT_ENCODING))
+
+
+def clean_terminal_output(text):
+    """
+    Clean up the terminal output of a command.
+
+    :param text: The raw text with special characters (a Unicode string).
+    :returns: A list of Unicode strings (one for each line).
+
+    This function emulates the effect of backspace (0x08), carriage return
+    (0x0D) and line feed (0x0A) characters and the ANSI 'erase line' escape
+    sequence on interactive terminals. It's intended to clean up command output
+    that was originally meant to be rendered on an interactive terminal and
+    that has been captured using e.g. the :man:`script` program [#]_ or the
+    :mod:`pty` module [#]_.
+
+    .. [#] My coloredlogs_ package supports the ``coloredlogs --to-html``
+           command which uses :man:`script` to fool a subprocess into thinking
+           that it's connected to an interactive terminal (in order to get it
+           to emit ANSI escape sequences).
+
+    .. [#] My capturer_ package uses the :mod:`pty` module to fool the current
+           process and subprocesses into thinking they are connected to an
+           interactive terminal (in order to get them to emit ANSI escape
+           sequences).
+
+    **Some caveats about the use of this function:**
+
+    - Strictly speaking the effect of carriage returns cannot be emulated
+      outside of an actual terminal due to the interaction between overlapping
+      output, terminal widths and line wrapping. The goal of this function is
+      to sanitize noise in terminal output while preserving useful output.
+      Think of it as a useful and pragmatic but possibly lossy conversion.
+
+    - The algorithm isn't smart enough to properly handle a pair of ANSI escape
+      sequences that open before a carriage return and close after the last
+      carriage return in a linefeed delimited string; the resulting string will
+      contain only the closing end of the ANSI escape sequence pair. Tracking
+      this kind of complexity requires a state machine and proper parsing.
+
+    .. _capturer: https://pypi.org/project/capturer
+    .. _coloredlogs: https://pypi.org/project/coloredlogs
+    """
+    cleaned_lines = []
+    current_line = ''
+    current_position = 0
+    for token in CLEAN_OUTPUT_PATTERN.split(text):
+        if token == '\r':
+            # Seek back to the start of the current line.
+            current_position = 0
+        elif token == '\b':
+            # Seek back one character in the current line.
+            current_position = max(0, current_position - 1)
+        else:
+            if token == '\n':
+                # Capture the current line.
+                cleaned_lines.append(current_line)
+            if token in ('\n', ANSI_ERASE_LINE):
+                # Clear the current line.
+                current_line = ''
+                current_position = 0
+            elif token:
+                # Merge regular output into the current line.
+                new_position = current_position + len(token)
+                prefix = current_line[:current_position]
+                suffix = current_line[new_position:]
+                current_line = prefix + token + suffix
+                current_position = new_position
+    # Capture the last line (if any).
+    cleaned_lines.append(current_line)
+    # Remove any empty trailing lines.
+    while cleaned_lines and not cleaned_lines[-1]:
+        cleaned_lines.pop(-1)
+    return cleaned_lines
+
+
+def connected_to_terminal(stream=None):
+    """
+    Check if a stream is connected to a terminal.
+
+    :param stream: The stream to check (a file-like object,
+                   defaults to :data:`sys.stdout`).
+    :returns: :data:`True` if the stream is connected to a terminal,
+              :data:`False` otherwise.
+
+    See also :func:`terminal_supports_colors()`.
+    """
+    stream = sys.stdout if stream is None else stream
+    try:
+        return stream.isatty()
+    except Exception:
+        return False
+
+
+@cached
+def enable_ansi_support():
+    """
+    Try to enable support for ANSI escape sequences (required on Windows).
+
+    :returns: :data:`True` if ANSI is supported, :data:`False` otherwise.
+
+    This functions checks for the following supported configurations, in the
+    given order:
+
+    1. On Windows, if :func:`have_windows_native_ansi_support()` confirms
+       native support for ANSI escape sequences :mod:`ctypes` will be used to
+       enable this support.
+
+    2. On Windows, if the environment variable ``$ANSICON`` is set nothing is
+       done because it is assumed that support for ANSI escape sequences has
+       already been enabled via `ansicon <https://github.com/adoxa/ansicon>`_.
+
+    3. On Windows, an attempt is made to import and initialize the Python
+       package :pypi:`colorama` instead (of course for this to work
+       :pypi:`colorama` has to be installed).
+
+    4. On other platforms this function calls :func:`connected_to_terminal()`
+       to determine whether ANSI escape sequences are supported (that is to
+       say all platforms that are not Windows are assumed to support ANSI
+       escape sequences natively, without weird contortions like above).
+
+       This makes it possible to call :func:`enable_ansi_support()`
+       unconditionally without checking the current platform.
+
+    The :func:`~humanfriendly.decorators.cached` decorator is used to ensure
+    that this function is only executed once, but its return value remains
+    available on later calls.
+    """
+    if have_windows_native_ansi_support():
+        import ctypes
+        ctypes.windll.kernel32.SetConsoleMode(ctypes.windll.kernel32.GetStdHandle(-11), 7)
+        ctypes.windll.kernel32.SetConsoleMode(ctypes.windll.kernel32.GetStdHandle(-12), 7)
+        return True
+    elif on_windows():
+        if 'ANSICON' in os.environ:
+            return True
+        try:
+            import colorama
+            colorama.init()
+            return True
+        except ImportError:
+            return False
+    else:
+        return connected_to_terminal()
+
+
+def find_terminal_size():
+    """
+    Determine the number of lines and columns visible in the terminal.
+
+    :returns: A tuple of two integers with the line and column count.
+
+    The result of this function is based on the first of the following three
+    methods that works:
+
+    1. First :func:`find_terminal_size_using_ioctl()` is tried,
+    2. then :func:`find_terminal_size_using_stty()` is tried,
+    3. finally :data:`DEFAULT_LINES` and :data:`DEFAULT_COLUMNS` are returned.
+
+    .. note:: The :func:`find_terminal_size()` function performs the steps
+              above every time it is called, the result is not cached. This is
+              because the size of a virtual terminal can change at any time and
+              the result of :func:`find_terminal_size()` should be correct.
+
+              `Pre-emptive snarky comment`_: It's possible to cache the result
+              of this function and use :mod:`signal.SIGWINCH <signal>` to
+              refresh the cached values!
+
+              Response: As a library I don't consider it the role of the
+              :mod:`humanfriendly.terminal` module to install a process wide
+              signal handler ...
+
+    .. _Pre-emptive snarky comment: http://blogs.msdn.com/b/oldnewthing/archive/2008/01/30/7315957.aspx
+    """
+    # The first method. Any of the standard streams may have been redirected
+    # somewhere and there's no telling which, so we'll just try them all.
+    for stream in sys.stdin, sys.stdout, sys.stderr:
+        try:
+            result = find_terminal_size_using_ioctl(stream)
+            if min(result) >= 1:
+                return result
+        except Exception:
+            pass
+    # The second method.
+    try:
+        result = find_terminal_size_using_stty()
+        if min(result) >= 1:
+            return result
+    except Exception:
+        pass
+    # Fall back to conservative defaults.
+    return DEFAULT_LINES, DEFAULT_COLUMNS
+
+
+def find_terminal_size_using_ioctl(stream):
+    """
+    Find the terminal size using :func:`fcntl.ioctl()`.
+
+    :param stream: A stream connected to the terminal (a file object with a
+                   ``fileno`` attribute).
+    :returns: A tuple of two integers with the line and column count.
+    :raises: This function can raise exceptions but I'm not going to document
+             them here, you should be using :func:`find_terminal_size()`.
+
+    Based on an `implementation found on StackOverflow <http://stackoverflow.com/a/3010495/788200>`_.
+    """
+    if not HAVE_IOCTL:
+        raise NotImplementedError("It looks like the `fcntl' module is not available!")
+    h, w, hp, wp = struct.unpack('HHHH', fcntl.ioctl(stream, termios.TIOCGWINSZ, struct.pack('HHHH', 0, 0, 0, 0)))
+    return h, w
+
+
+def find_terminal_size_using_stty():
+    """
+    Find the terminal size using the external command ``stty size``.
+
+    :param stream: A stream connected to the terminal (a file object).
+    :returns: A tuple of two integers with the line and column count.
+    :raises: This function can raise exceptions but I'm not going to document
+             them here, you should be using :func:`find_terminal_size()`.
+    """
+    stty = subprocess.Popen(['stty', 'size'],
+                            stdout=subprocess.PIPE,
+                            stderr=subprocess.PIPE)
+    stdout, stderr = stty.communicate()
+    tokens = stdout.split()
+    if len(tokens) != 2:
+        raise Exception("Invalid output from `stty size'!")
+    return tuple(map(int, tokens))
+
+
+def get_pager_command(text=None):
+    """
+    Get the command to show a text on the terminal using a pager.
+
+    :param text: The text to print to the terminal (a string).
+    :returns: A list of strings with the pager command and arguments.
+
+    The use of a pager helps to avoid the wall of text effect where the user
+    has to scroll up to see where the output began (not very user friendly).
+
+    If the given text contains ANSI escape sequences the command ``less
+    --RAW-CONTROL-CHARS`` is used, otherwise the environment variable
+    ``$PAGER`` is used (if ``$PAGER`` isn't set :man:`less` is used).
+
+    When the selected pager is :man:`less`, the following options are used to
+    make the experience more user friendly:
+
+    - ``--quit-if-one-screen`` causes :man:`less` to automatically exit if the
+      entire text can be displayed on the first screen. This makes the use of a
+      pager transparent for smaller texts (because the operator doesn't have to
+      quit the pager).
+
+    - ``--no-init`` prevents :man:`less` from clearing the screen when it
+      exits. This ensures that the operator gets a chance to review the text
+      (for example a usage message) after quitting the pager, while composing
+      the next command.
+    """
+    # Compose the pager command.
+    if text and ANSI_CSI in text:
+        command_line = ['less', '--RAW-CONTROL-CHARS']
+    else:
+        command_line = [os.environ.get('PAGER', 'less')]
+    # Pass some additional options to `less' (to make it more
+    # user friendly) without breaking support for other pagers.
+    if os.path.basename(command_line[0]) == 'less':
+        command_line.append('--no-init')
+        command_line.append('--quit-if-one-screen')
+    return command_line
+
+
+@cached
+def have_windows_native_ansi_support():
+    """
+    Check if we're running on a Windows 10 release with native support for ANSI escape sequences.
+
+    :returns: :data:`True` if so, :data:`False` otherwise.
+
+    The :func:`~humanfriendly.decorators.cached` decorator is used as a minor
+    performance optimization. Semantically this should have zero impact because
+    the answer doesn't change in the lifetime of a computer process.
+    """
+    if on_windows():
+        try:
+            # I can't be 100% sure this will never break and I'm not in a
+            # position to test it thoroughly either, so I decided that paying
+            # the price of one additional try / except statement is worth the
+            # additional peace of mind :-).
+            components = tuple(int(c) for c in platform.version().split('.'))
+            return components >= (10, 0, 14393)
+        except Exception:
+            pass
+    return False
+
+
+def message(text, *args, **kw):
+    """
+    Print a formatted message to the standard error stream.
+
+    For details about argument handling please refer to
+    :func:`~humanfriendly.text.format()`.
+
+    Renders the message using :func:`~humanfriendly.text.format()` and writes
+    the resulting string (followed by a newline) to :data:`sys.stderr` using
+    :func:`auto_encode()`.
+    """
+    auto_encode(sys.stderr, coerce_string(text) + '\n', *args, **kw)
+
+
+def output(text, *args, **kw):
+    """
+    Print a formatted message to the standard output stream.
+
+    For details about argument handling please refer to
+    :func:`~humanfriendly.text.format()`.
+
+    Renders the message using :func:`~humanfriendly.text.format()` and writes
+    the resulting string (followed by a newline) to :data:`sys.stdout` using
+    :func:`auto_encode()`.
+    """
+    auto_encode(sys.stdout, coerce_string(text) + '\n', *args, **kw)
+
+
+def readline_strip(expr):
+    """
+    Remove `readline hints`_ from a string.
+
+    :param text: The text to strip (a string).
+    :returns: The stripped text.
+    """
+    return expr.replace('\001', '').replace('\002', '')
+
+
+def readline_wrap(expr):
+    """
+    Wrap an ANSI escape sequence in `readline hints`_.
+
+    :param text: The text with the escape sequence to wrap (a string).
+    :returns: The wrapped text.
+
+    .. _readline hints: http://superuser.com/a/301355
+    """
+    return '\001' + expr + '\002'
+
+
+def show_pager(formatted_text, encoding=DEFAULT_ENCODING):
+    """
+    Print a large text to the terminal using a pager.
+
+    :param formatted_text: The text to print to the terminal (a string).
+    :param encoding: The name of the text encoding used to encode the formatted
+                     text if the formatted text is a Unicode string (a string,
+                     defaults to :data:`DEFAULT_ENCODING`).
+
+    When :func:`connected_to_terminal()` returns :data:`True` a pager is used
+    to show the text on the terminal, otherwise the text is printed directly
+    without invoking a pager.
+
+    The use of a pager helps to avoid the wall of text effect where the user
+    has to scroll up to see where the output began (not very user friendly).
+
+    Refer to :func:`get_pager_command()` for details about the command line
+    that's used to invoke the pager.
+    """
+    if connected_to_terminal():
+        # Make sure the selected pager command is available.
+        command_line = get_pager_command(formatted_text)
+        if which(command_line[0]):
+            pager = subprocess.Popen(command_line, stdin=subprocess.PIPE)
+            if is_unicode(formatted_text):
+                formatted_text = formatted_text.encode(encoding)
+            pager.communicate(input=formatted_text)
+            return
+    output(formatted_text)
+
+
+def terminal_supports_colors(stream=None):
+    """
+    Check if a stream is connected to a terminal that supports ANSI escape sequences.
+
+    :param stream: The stream to check (a file-like object,
+                   defaults to :data:`sys.stdout`).
+    :returns: :data:`True` if the terminal supports ANSI escape sequences,
+              :data:`False` otherwise.
+
+    This function was originally inspired by the implementation of
+    `django.core.management.color.supports_color()
+    <https://github.com/django/django/blob/master/django/core/management/color.py>`_
+    but has since evolved significantly.
+    """
+    if on_windows():
+        # On Windows support for ANSI escape sequences is not a given.
+        have_ansicon = 'ANSICON' in os.environ
+        have_colorama = 'colorama' in sys.modules
+        have_native_support = have_windows_native_ansi_support()
+        if not (have_ansicon or have_colorama or have_native_support):
+            return False
+    return connected_to_terminal(stream)
+
+
+def usage(usage_text):
+    """
+    Print a human friendly usage message to the terminal.
+
+    :param text: The usage message to print (a string).
+
+    This function does two things:
+
+    1. If :data:`sys.stdout` is connected to a terminal (see
+       :func:`connected_to_terminal()`) then the usage message is formatted
+       using :func:`.format_usage()`.
+    2. The usage message is shown using a pager (see :func:`show_pager()`).
+    """
+    if terminal_supports_colors(sys.stdout):
+        usage_text = format_usage(usage_text)
+    show_pager(usage_text)
+
+
+def warning(text, *args, **kw):
+    """
+    Show a warning message on the terminal.
+
+    For details about argument handling please refer to
+    :func:`~humanfriendly.text.format()`.
+
+    Renders the message using :func:`~humanfriendly.text.format()` and writes
+    the resulting string (followed by a newline) to :data:`sys.stderr` using
+    :func:`auto_encode()`.
+
+    If :data:`sys.stderr` is connected to a terminal that supports colors,
+    :func:`ansi_wrap()` is used to color the message in a red font (to make
+    the warning stand out from surrounding text).
+    """
+    text = coerce_string(text)
+    if terminal_supports_colors(sys.stderr):
+        text = ansi_wrap(text, color='red')
+    auto_encode(sys.stderr, text + '\n', *args, **kw)
+
+
+# Define aliases for backwards compatibility.
+define_aliases(
+    module_name=__name__,
+    # In humanfriendly 1.31 the find_meta_variables() and format_usage()
+    # functions were extracted to the new module humanfriendly.usage.
+    find_meta_variables='humanfriendly.usage.find_meta_variables',
+    format_usage='humanfriendly.usage.format_usage',
+    # In humanfriendly 8.0 the html_to_ansi() function and HTMLConverter
+    # class were extracted to the new module humanfriendly.terminal.html.
+    html_to_ansi='humanfriendly.terminal.html.html_to_ansi',
+    HTMLConverter='humanfriendly.terminal.html.HTMLConverter',
+)