Esempio n. 1
0
 def test_expect(self):
     cap = Capture(buffer_size=-1)  # line buffered
     p = run("%s lister.py -d 0.01" % sys.executable, async=True, stdout=cap)
     timeout = 1.0
     m1 = cap.expect("^line 1\r?$", timeout)
     self.assertTrue(m1)
     m2 = cap.expect("^line 5\r?$", timeout)
     self.assertTrue(m2)
     m3 = cap.expect("^line 1.*\r?$", timeout)
     self.assertTrue(m3)
     cap.close(True)
     p.commands[0].kill()
     data = cap.bytes
     self.assertEqual(data[m1.start() : m1.end()].rstrip(), b"line 1")
     self.assertEqual(data[m2.start() : m2.end()].rstrip(), b"line 5")
     self.assertEqual(data[m3.start() : m3.end()].rstrip(), b"line 10")
Esempio n. 2
0
 def test_expect(self):
     cap = Capture(buffer_size=-1)  # line buffered
     p = run('%s lister.py -d 0.01' % sys.executable,
             async=True,
             stdout=cap)
     timeout = 1.0
     m1 = cap.expect('^line 1\r?$', timeout)
     self.assertTrue(m1)
     m2 = cap.expect('^line 5\r?$', timeout)
     self.assertTrue(m2)
     m3 = cap.expect('^line 1.*\r?$', timeout)
     self.assertTrue(m3)
     cap.close(True)
     p.commands[0].kill()
     data = cap.bytes
     self.assertEqual(data[m1.start():m1.end()].rstrip(), b'line 1')
     self.assertEqual(data[m2.start():m2.end()].rstrip(), b'line 5')
     self.assertEqual(data[m3.start():m3.end()].rstrip(), b'line 10')
Esempio n. 3
0
    format=
    '%(asctime)s %(levelname)-8s %(name)s %(threadName)s %(lineno)4d %(message)s'
)
cap = Capture(buffer_size=-1)  # line buffered
p = run('python lister.py -d 0.01', async=True, stdout=cap)

WAIT_TIME = 1.0


def do_expect(pattern, timeout=None):
    stime = time.time()
    cap.expect(pattern, timeout or WAIT_TIME)
    elapsed = time.time() - stime
    if not cap.match:
        print('%r not found within time limit.' % pattern)
        result = False
    else:
        print('%r found at %s in %.1f seconds.' %
              (pattern, cap.match.span(), elapsed))
        result = True
    return result


if do_expect('line 1$'):
    if do_expect('line 5$'):
        if do_expect('line 1.*$'):
            cap.close(True)
            print(cap.bytes[cap.match.start():cap.match.end()])

p.commands[0].kill()
Esempio n. 4
0
logging.basicConfig(filename='test_expect.log', filemode='w',
                    level=logging.INFO,
                    format='%(asctime)s %(levelname)-8s %(name)s %(threadName)s %(lineno)4d %(message)s')
cap = Capture(buffer_size=-1)   # line buffered
p = run('python lister.py -d 0.01', async=True,
        stdout=cap)

WAIT_TIME = 1.0

def do_expect(pattern, timeout=None):
    stime = time.time()
    cap.expect(pattern, timeout or WAIT_TIME)
    elapsed = time.time() - stime
    if not cap.match:
        print('%r not found within time limit.' % pattern)
        result = False
    else:
        print('%r found at %s in %.1f seconds.' % (pattern, cap.match.span(),
                                                   elapsed))
        result = True
    return result

if do_expect('line 1$'):
    if do_expect('line 5$'):
        if do_expect('line 1.*$'):
            cap.close(True)
            print(cap.bytes[cap.match.start():cap.match.end()])

p.commands[0].kill()

Esempio n. 5
0
logging.basicConfig(filename='test_expect.log', filemode='w',
                    level=logging.INFO,
                    format='%(asctime)s %(levelname)-8s %(name)s %(threadName)s %(lineno)4d %(message)s')
cap = Capture(buffer_size=-1)   # line buffered
p = run('python lister.py -d 0.01 -i "<head|body>" docs/_build/html/tutorial.html', async=True,
        stdout=cap)
stime = time.time()
logger.info('Calling expect for head')
cap.expect('<head>', 60.0)
logger.info('Returned from expect for head')
elapsed = time.time() - stime
if not cap.match:
    print('<head> not found within time limit.')
else:
    print('<head> found at %s in %.1f seconds.' % (cap.match.span(), elapsed))
    stime = time.time()
    logger.info('Calling expect for body')
    cap.expect('<body>', 60.0)
    logger.info('Returned from expect for body')
    elapsed = time.time() - stime
    if not cap.match:
        print('<body> not found within time limit.')
    else:
        print('<body> found at %s in %.1f seconds.' % (cap.match.span(), elapsed))
logger.debug('Killing subprocess')
p.commands[0].kill()
logger.debug('Closing capture')
cap.close()

Esempio n. 6
0
    filename='test_expect.log',
    filemode='w',
    level=logging.INFO,
    format='%(asctime)s %(levelname)-8s %(name)s %(threadName)s %(lineno)4d %(message)s')
cap = Capture(buffer_size=-1)  # line buffered
p = run('python lister.py -d 0.01 -i "<head|body>" docs/_build/html/tutorial.html',
        async_=True,
        stdout=cap)
stime = time.time()
logger.info('Calling expect for head')
cap.expect('<head>', 60.0)
logger.info('Returned from expect for head')
elapsed = time.time() - stime
if not cap.match:
    print('<head> not found within time limit.')
else:
    print('<head> found at %s in %.1f seconds.' % (cap.match.span(), elapsed))
    stime = time.time()
    logger.info('Calling expect for body')
    cap.expect('<body>', 60.0)
    logger.info('Returned from expect for body')
    elapsed = time.time() - stime
    if not cap.match:
        print('<body> not found within time limit.')
    else:
        print('<body> found at %s in %.1f seconds.' % (cap.match.span(), elapsed))
logger.debug('Killing subprocess')
p.commands[0].kill()
logger.debug('Closing capture')
cap.close()
Esempio n. 7
0
class Shell(AbstractContextManager):
    """A real shell running on the host machine, to be used in a context."""
    def __init__(self, shell_binary, shell_argstr, return_code_echo_command,
                 command_separator, is_interactive):
        """Sets up the context for a system shell process.

        Parameters
        ----------
        shell_binary : str
            The shell binary, e.g. ``/bin/bash``, to start.
        shell_argstr : str
            Additional arguments to be passed to the shell at start.
        return_code_echo_command : str
            A command string in the shell's own language that prints to
            standard output the return code of the previously executed command.
        command_separator : str
            The character sequence to separate commands with.
        is_interactive : bool
            Whether the started shell is an interactive one.
            This does only change the behaviour of the context manager, to make
            the shell itself interactive, additional arguments in
            `shell_argstr` might need to be passed.
        """
        self._args = shell_argstr
        self._binary = shell_binary
        # Note: The execution of the command and the reading of the output
        # has to happen BEFORE this timeout is hit, but a large timeout would
        # also mean waiting a lot for small commands, so this has to be
        # balanced carefully.
        self._capture = Capture(timeout=0.5, buffer_size=-1)
        self._command = Command(shell_binary + ' ' + shell_argstr,
                                stdout=self._capture)
        self._echo = return_code_echo_command + command_separator
        self._interactive = is_interactive
        self._separator = command_separator
        self._started = False

    def __enter__(self):
        """Starts the shell in a context manager setting."""
        return self.start()

    def __exit__(self, exc_type, exc_value, trace):
        """Destroys the shell and leaves the context."""
        if self._interactive:
            self.kill()
        else:
            self.terminate()
        return False

    def start(self):
        """Starts the underlying shell process as configured in
        :py:func:`__init__`.
        """
        if self._started:
            raise OSError(EEXIST, "The shell is already running!")
        print("[Shell] Starting '{0} {1}'...".format(self._binary, self._args),
              file=sys.stderr)
        try:
            self._command.run(input=PIPE, async_=True)
        except ValueError:
            raise FileNotFoundError("The shell binary '{0}' cannot be found "
                                    "on the system.".format(self._binary))
        self._started = True
        return self

    @property
    def pid(self):
        """The PID of the started process."""
        if not self._started:
            raise OSError(ESRCH, "The shell is not running!")
        return self._command.process.pid

    def kill(self):
        """Kills (by sending ``SIGKILL`` (``9``)) to the shell process."""
        if not self._started:
            raise OSError(ESRCH, "The shell is not running!")
        self._command.kill()
        self._command.wait()
        self._capture.close(True)
        self._started = False

    def terminate(self):
        """Terminates (by sending ``SIGTERM`` (``15``)) to the shell process.

        Note
        ----
        Interactive shells (:py:attr:`is_interactive`) usually catch and ignore
        this signal, and as such, :py:func:`kill` should be used to shut them
        down properly.
        """
        if not self._started:
            raise OSError(ESRCH, "The shell is not running!")
        self._command.terminate()
        self._command.wait()
        self._capture.close(True)
        self._started = False

    def execute_command(self, cmd, timeout=None):
        """Execute `cmd` in the shell, wait `timeout` seconds, and read back
        the result.

        Parameters
        ----------
        cmd : str
            The command to execute.
            This string will be written into the shell's standard input
            verbatim.
        timeout : int
            The time (in seconds) to wait before the output of the command is
            read.

        Returns
        -------
        return_code : int
            The return code of the executed command.
        result : str
            The *standard output* of the executed command.

        Note
        ----
        The command executed in the shell is extended with
        :py:attr:`command_separator` and :py:attr:`return_code_echo_command`,
        and written to the shell.
        In case of a conventional ``/bin/bash`` shell, for example, executing
        `cmd` ``echo "Foo"`` will actually execute:

        .. code-block:: bash

           echo "Foo";
           echo $;

        Which will result in the output:

        .. code-block:: bash

           Foo
           0

        to be read as a result.

        Warning
        -------
        The underlying library and the execution of piped shells does not allow
        a good method of "sensing" when the output became available while
        keeping interactivity.
        A too small `timeout` on a loaded system might result in output being
        lost, while a too big one will result in every command waiting for
        a considerable time.
        """
        cmd = cmd + self._separator + self._echo
        print("[Shell '{0}'] Running command:\n{1}\n".format(
            self._binary,
            '\n'.join(list(map(lambda l: "    > " + l, cmd.split('\n'))))),
              file=sys.stderr)

        self._command.stdin.write(cmd.encode('utf-8'))
        self._command.stdin.flush()

        stdout = self._capture.read(timeout=timeout)
        parts = stdout.decode().rstrip().split('\n')
        result, returncode = '\n'.join(parts[:-1]).rstrip(), parts[-1].rstrip()

        print("[Shell '{0}'] Command result #{1}:\n{2}".format(
            self._binary, returncode, result),
              file=sys.stderr)
        return int(returncode), result