Example #1
0
 def test_carriage_return_interpretation(self):
     """Sanity check the results of clean_terminal_output()."""
     # Simple output should pass through unharmed.
     assert clean_terminal_output('foo') == ['foo']
     # Simple output should pass through unharmed.
     assert clean_terminal_output('foo\nbar') == ['foo', 'bar']
     # Carriage returns and preceding substrings should be stripped.
     assert clean_terminal_output('foo\rbar\nbaz') == ['bar', 'baz']
     # Trailing empty lines should be stripped.
     assert clean_terminal_output('foo\nbar\nbaz\n\n\n') == [
         'foo', 'bar', 'baz'
     ]
def capture(command, encoding='UTF-8'):
    """
    Capture the output of an external command as if it runs in an interactive terminal.

    :param command: The command name and its arguments (a list of strings).
    :param encoding: The encoding to use to decode the output (a string).
    :returns: The output of the command.

    This function runs an external command under ``script`` (emulating an
    interactive terminal) to capture the output of the command as if it was
    running in an interactive terminal (including ANSI escape sequences).
    """
    with open(os.devnull, 'wb') as dev_null:
        # We start by invoking the `script' program in a form that is supported
        # by the Linux implementation [1] but fails command line validation on
        # the MacOS (BSD) implementation [2]: The command is specified using
        # the -c option and the typescript file is /dev/null.
        #
        # [1] http://man7.org/linux/man-pages/man1/script.1.html
        # [2] https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/script.1.html
        command_line = ['script', '-qc', ' '.join(map(pipes.quote, command)), '/dev/null']
        script = subprocess.Popen(command_line, stdout=subprocess.PIPE, stderr=dev_null)
        stdout, stderr = script.communicate()
        if script.returncode == 0:
            # If `script' succeeded we assume that it understood our command line
            # invocation which means it's the Linux implementation (in this case
            # we can use standard output instead of a temporary file).
            output = stdout.decode(encoding)
        else:
            # If `script' failed we assume that it didn't understand our command
            # line invocation which means it's the MacOS (BSD) implementation
            # (in this case we need a temporary file because the command line
            # interface requires it).
            fd, temporary_file = tempfile.mkstemp(prefix='colouredlogs-', suffix='-capture.txt')
            try:
                command_line = ['script', '-q', temporary_file] + list(command)
                subprocess.Popen(command_line, stdout=dev_null, stderr=dev_null).wait()
                with codecs.open(temporary_file, 'r', encoding) as handle:
                    output = handle.read()
            finally:
                os.unlink(temporary_file)
            # On MacOS when standard input is /dev/null I've observed
            # the captured output starting with the characters '^D':
            #
            #   $ script -q capture.txt echo example </dev/null
            #   example
            #   $ xxd capture.txt
            #   00000000: 5e44 0808 6578 616d 706c 650d 0a         ^D..example..
            #
            # I'm not sure why this is here, although I suppose it has to do
            # with ^D in caret notation signifying end-of-file [1]. What I do
            # know is that this is an implementation detail that callers of the
            # capture() function shouldn't be bothered with, so we strip it.
            #
            # [1] https://en.wikipedia.org/wiki/End-of-file
            if output.startswith(b'^D'):
                output = output[2:]
    # Clean up backspace and carriage return characters and the 'erase line'
    # ANSI escape sequence and return the output as a Unicode string.
    return u'\n'.join(clean_terminal_output(output))
Example #3
0
def capture(command, encoding='UTF-8'):
    """
    Capture the output of an external command as if it runs in an interactive terminal.

    :param command: The command name and its arguments (a list of strings).
    :param encoding: The encoding to use to decode the output (a string).
    :returns: The output of the command.

    This function runs an external command under ``script`` (emulating an
    interactive terminal) to capture the output of the command as if it was
    running in an interactive terminal (including ANSI escape sequences).
    """
    with open(os.devnull, 'wb') as dev_null:
        # We start by invoking the `script' program in a form that is supported
        # by the Linux implementation [1] but fails command line validation on
        # the MacOS (BSD) implementation [2]: The command is specified using
        # the -c option and the typescript file is /dev/null.
        #
        # [1] http://man7.org/linux/man-pages/man1/script.1.html
        # [2] https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/script.1.html
        command_line = ['script', '-qc', ' '.join(map(pipes.quote, command)), '/dev/null']
        script = subprocess.Popen(command_line, stdout=subprocess.PIPE, stderr=dev_null)
        stdout, stderr = script.communicate()
        if script.returncode == 0:
            # If `script' succeeded we assume that it understood our command line
            # invocation which means it's the Linux implementation (in this case
            # we can use standard output instead of a temporary file).
            output = stdout.decode(encoding)
        else:
            # If `script' failed we assume that it didn't understand our command
            # line invocation which means it's the MacOS (BSD) implementation
            # (in this case we need a temporary file because the command line
            # interface requires it).
            fd, temporary_file = tempfile.mkstemp(prefix='coloredlogs-', suffix='-capture.txt')
            try:
                command_line = ['script', '-q', temporary_file] + list(command)
                subprocess.Popen(command_line, stdout=dev_null, stderr=dev_null).wait()
                with codecs.open(temporary_file, 'r', encoding) as handle:
                    output = handle.read()
            finally:
                os.unlink(temporary_file)
            # On MacOS when standard input is /dev/null I've observed
            # the captured output starting with the characters '^D':
            #
            #   $ script -q capture.txt echo example </dev/null
            #   example
            #   $ xxd capture.txt
            #   00000000: 5e44 0808 6578 616d 706c 650d 0a         ^D..example..
            #
            # I'm not sure why this is here, although I suppose it has to do
            # with ^D in caret notation signifying end-of-file [1]. What I do
            # know is that this is an implementation detail that callers of the
            # capture() function shouldn't be bothered with, so we strip it.
            #
            # [1] https://en.wikipedia.org/wiki/End-of-file
            if output.startswith(b'^D'):
                output = output[2:]
    # Clean up backspace and carriage return characters and the 'erase line'
    # ANSI escape sequence and return the output as a Unicode string.
    return u'\n'.join(clean_terminal_output(output))
Example #4
0
def capture(command, encoding='UTF-8'):
    """
    Capture the output of an external command as if it runs in an interactive terminal.

    :param command: The command name and its arguments (a list of strings).
    :param encoding: The encoding to use to decode the output (a string).
    :returns: The output of the command.

    This function runs an external command under ``script`` (emulating an
    interactive terminal) to capture the output of the command as if it was
    running in an interactive terminal (including ANSI escape sequences).
    """
    with open(os.devnull, 'wb') as dev_null:
        # We start by invoking the `script' program in a form that is supported
        # by the Linux implementation [1] but fails command line validation on
        # the Mac OS X (BSD) implementation [2]: The command is specified
        # using the -c option and the typescript file is /dev/null.
        #
        # [1] http://man7.org/linux/man-pages/man1/script.1.html
        # [2] https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/script.1.html
        command_line = [
            'script', '-qc', ' '.join(map(pipes.quote, command)), '/dev/null'
        ]
        script = subprocess.Popen(command_line,
                                  stdout=subprocess.PIPE,
                                  stderr=dev_null)
        stdout, stderr = script.communicate()
        if script.returncode == 0:
            # If `script' succeeded we assume that it understood our command line
            # invocation which means it's the Linux implementation (in this case
            # we can use standard output instead of a temporary file).
            output = stdout.decode(encoding)
        else:
            # If `script' failed we assume that it didn't understand our command
            # line invocation which means it's the Mac OS X (BSD) implementation
            # (in this case we need a temporary file because the command line
            # interface requires it).
            fd, temporary_file = tempfile.mkstemp(prefix='coloredlogs-',
                                                  suffix='-capture.txt')
            try:
                command_line = ['script', '-q', temporary_file] + list(command)
                subprocess.Popen(command_line,
                                 stdout=dev_null,
                                 stderr=dev_null).wait()
                with codecs.open(temporary_file, 'r', encoding) as handle:
                    output = handle.read()
            finally:
                os.unlink(temporary_file)
    # Clean up backspace and carriage return characters and the 'erase line'
    # ANSI escape sequence and return the output as a Unicode string.
    return u'\n'.join(clean_terminal_output(output))
Example #5
0
    def get_text(self, interpreted=True, partial=PARTIAL_DEFAULT):
        """get_text(interpreted=True, partial=False)
        Get the captured output as a single string.

        :param interpreted: If :data:`True` (the default) captured output is
                            processed using :func:`clean_terminal_output()`.
        :param partial: Refer to :func:`get_handle()` for details.
        :returns: The captured output as a Unicode string.

        .. warning:: If partial is :data:`True` (not the default) the output
                     can end in a partial line, possibly in the middle of a
                     multi byte character (this may cause decoding errors).
        """
        output = self.get_bytes(partial)
        output = output.decode(self.encoding)
        if interpreted:
            output = u'\n'.join(clean_terminal_output(output))
        return output
Example #6
0
def capture(command, encoding='UTF-8'):
    """
    Capture the output of an external command as if it runs in an interactive terminal.

    :param command: The command name and its arguments (a list of strings).
    :param encoding: The encoding to use to decode the output (a string).
    :returns: The output of the command.

    This function runs an external command under ``script`` (emulating an
    interactive terminal) to capture the output of the command as if it was
    running in an interactive terminal (including ANSI escape sequences).
    """
    with open(os.devnull, 'wb') as dev_null:
        # We start by invoking the `script' program in a form that is supported
        # by the Linux implementation [1] but fails command line validation on
        # the Mac OS X (BSD) implementation [2]: The command is specified
        # using the -c option and the typescript file is /dev/null.
        #
        # [1] http://man7.org/linux/man-pages/man1/script.1.html
        # [2] https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/script.1.html
        command_line = ['script', '-qc', ' '.join(map(pipes.quote, command)), '/dev/null']
        script = subprocess.Popen(command_line, stdout=subprocess.PIPE, stderr=dev_null)
        stdout, stderr = script.communicate()
        if script.returncode == 0:
            # If `script' succeeded we assume that it understood our command line
            # invocation which means it's the Linux implementation (in this case
            # we can use standard output instead of a temporary file).
            output = stdout.decode(encoding)
        else:
            # If `script' failed we assume that it didn't understand our command
            # line invocation which means it's the Mac OS X (BSD) implementation
            # (in this case we need a temporary file because the command line
            # interface requires it).
            fd, temporary_file = tempfile.mkstemp(prefix='coloredlogs-', suffix='-capture.txt')
            try:
                command_line = ['script', '-q', temporary_file] + list(command)
                subprocess.Popen(command_line, stdout=dev_null, stderr=dev_null).wait()
                with codecs.open(temporary_file, 'r', encoding) as handle:
                    output = handle.read()
            finally:
                os.unlink(temporary_file)
    # Clean up backspace and carriage return characters and the 'erase line'
    # ANSI escape sequence and return the output as a Unicode string.
    return u'\n'.join(clean_terminal_output(output))
Example #7
0
 def test_clean_output(self):
     """Test :func:`humanfriendly.terminal.clean_terminal_output()`."""
     # Simple output should pass through unharmed (single line).
     assert clean_terminal_output('foo') == ['foo']
     # Simple output should pass through unharmed (multiple lines).
     assert clean_terminal_output('foo\nbar') == ['foo', 'bar']
     # Carriage returns and preceding substrings are removed.
     assert clean_terminal_output('foo\rbar\nbaz') == ['bar', 'baz']
     # Carriage returns move the cursor to the start of the line without erasing text.
     assert clean_terminal_output('aaa\rab') == ['aba']
     # Backspace moves the cursor one position back without erasing text.
     assert clean_terminal_output('aaa\b\bb') == ['aba']
     # Trailing empty lines should be stripped.
     assert clean_terminal_output('foo\nbar\nbaz\n\n\n') == ['foo', 'bar', 'baz']
Example #8
0
 def test_clean_output(self):
     """Test :func:`humanfriendly.terminal.clean_terminal_output()`."""
     # Simple output should pass through unharmed (single line).
     assert clean_terminal_output('foo') == ['foo']
     # Simple output should pass through unharmed (multiple lines).
     assert clean_terminal_output('foo\nbar') == ['foo', 'bar']
     # Carriage returns and preceding substrings are removed.
     assert clean_terminal_output('foo\rbar\nbaz') == ['bar', 'baz']
     # Carriage returns move the cursor to the start of the line without erasing text.
     assert clean_terminal_output('aaa\rab') == ['aba']
     # Backspace moves the cursor one position back without erasing text.
     assert clean_terminal_output('aaa\b\bb') == ['aba']
     # Trailing empty lines should be stripped.
     assert clean_terminal_output('foo\nbar\nbaz\n\n\n') == ['foo', 'bar', 'baz']