Vinay Sajip  committed f16d9f8 Draft

Routine update.

  • Participants
  • Parent commits ca9c99c
  • Branches default

Comments (0)

Files changed (4)

File docs/internals.rst

 The :func:`shell_quote` function works as follows. Firstly,
 an empty string is converted to ``''``. Next, a check is made to see if the
 string has already been quoted (i.e. it begins and ends with the ``'``
-character), and if so, it is returned unchanged. Otherwise,
-it's bracketed with the ``'`` character and every internal instance of ``'``
-is replaced with ``'"'"'``.
+character), and if so, it is returned enclosed in ``"`` and with any contained
+`"` characters escaped with a backslash. Otherwise, it's bracketed with the
+``'`` character and every internal instance of ``'`` is replaced with
 How shell command formatting works

File docs/overview.rst

 and there may be changes in this area. However, you aren't forced to use this
 feature, and ``sarge`` should be useful without it.
+Change log
+Released: Not yet.
+- ``expect`` method added to ``Capture`` class, to allow searching
+  for specific patterns in subprocess output streams.
+- added ``terminate``, ``kill`` and ``poll`` methods to ``Command``
+  class to operate on the wrapped subprocess.
+- ```` now propagates exceptions which occur while spawning
+  subprocesses.
+- Fixed issue #4: ``shell_shlex`` does not split on ``@``.
+- Fixed issue #3: ``run`` et al now accept commands as lists, just as
+  ``subprocess.Popen`` does.
+- Fixed issue #2: ``shell_quote`` implementation improved.
+- Improved ``shell_shlex`` resilience by handling Unicode on 2.x (where
+  ``shlex`` breaks if passed Unicode).
+- Added ``get_stdout``, ``get_stderr`` and ``get_both`` for when subprocess
+  output is not expected to be voluminous.
+- Added an internal lock to serialise access to shared data.
+- Tests added to cover added functionality and reported issues.
+- Numerous documentation updates.
+Released: 2012-02-10
+- Initial release.
 Next steps

File docs/reference.rst

      :param block: As for the :meth:`` method.
      :param timeout: As for the :meth:`` method.
+   .. method:: expect(string_or_pattern,  timeout=None)
+      This looks for a pattern in the captured output stream. If found, it
+      returns immediately; otherwise, it will block until the timeout expires,
+      waiting for a match as bytes from the captured stream continue to be read.
+      :param string_or_pattern: A string or pattern representing a regular
+                                expression to match. Note that this needs to
+                                be a bytestring pattern if you pass a pattern
+                                in; if you pass in text, it is converted to
+                                bytes using the ``utf-8`` codec and then to
+                                a pattern used for matching (using ``search``).
+                                If you pass in a pattern, you may want to
+                                ensure that its flags include ``re/MULTILINE``
+                                so that you can make use of ``^`` and ``$`` in
+                                matching line boundaries.
+      :param timeout: If not specified, the module's ``default_expect_timeout``
+                      is used.
+      :returns: A regular expression match instance, if a match was found
+                within the specified timeout, or ``None`` if no match was
+                found.
 .. class:: Popen
    This is a subclass of :class:`subprocess.Popen` which is provided mainly

File docs/tutorial.rst

     >>> shell_quote('"ab?"')
     >>> shell_quote("'ab?'")
-    "'ab?'"
+    '"\'ab?\'"'
 This function is used internally by :func:`shell_format`, so you shouldn't need
 to call it directly except in unusual cases.
 From the point of view of buffering, note that two elements are needed for
-this example to work:
+the above example to work:
 * We specify ``buffer_size=1`` in the Capture constructor. Without this,
   data would only be read into the Capture's queue after an I/O completes --
-  which would depend on how many bytes the Capture reads at a time.
+  which would depend on how many bytes the Capture reads at a time. You can
+  also pass a ``buffer_size=-1`` to indicate that you want to use line-
+  buffering, i.e. read a line at a time from the child process. (This may only
+  work if the child process flushes its outbut buffers after every line.)
 * We make a ``flush`` call in the ``receiver`` script, to ensure that the pipe
   is flushed to the capture queue. You could avoid the  ``flush`` call in the
   above example if you used ``python -u receiver`` as the command (which runs
 it). This invokes a program directing its output to a pseudo-tty device which
 gives line buffering behaviour. This doesn't always work, though :-(
+Looking for specific patterns in child process output
+You can look for specific patterns in the output of a child process, by using
+the :meth:`~Capture.expect` method of the :class:`Capture` class. This takes a
+string, bytestring or regular expression pattern object and a timeout, and
+either returns a regular expression match object (if a match was found in the
+specified timeout) or ``None`` (if no match was found in the specified
+timeout). If you pass in a bytestring, it will be converted to a regular
+expression pattern. If you pass in text, it will be encoded to bytes using the
+``utf-8`` codec and then to a regular expression pattern. This pattern will be
+used to look for a match (using ``search``). If you pass in a regular
+expression pattern, make sure it is meant for bytes rather than text (to avoid
+``TypeError`` on Python 3.x). You may also find it useful to specify
+``re.MULTILINE`` in the pattern flags, so that you can match using ``^`` and
+``$`` at line boundaries.
+.. versionadded:: 0.1.1
+   The ``expect`` method was added.
+To illustrate usage of :meth:`Capture.expect`, consider the program
+```` (which is provided as part of the source distribution). This
+prints ``line 1``, ``line 2`` etc. indefinitely with a configurable delay,
+flushing its output stream after each line. We can capture the output from a
+run of ````, ensuring that we use line-buffering::
+    >>> from sarge import Capture, run
+    >>> c = Capture(buffer_size=-1)
+    >>> p = run('python -d 0.01', async=True, stdout=c)
+    >>> m = c.expect('^line 1$')
+    >>> m.span()
+    (0, 6)
+    >>> m = c.expect('^line 5$')
+    >>> m.span()
+    (28, 34)
+    >>> m = c.expect('^line 1.*$')
+    >>> m.span()
+    (63, 70)
+    >>> c.close(True)           # close immediately, discard any unread input
+    >>> p.commands[0].kill()    # kill the subprocess
+    >>> c.bytes[63:70]
+    'line 10'
+    >>> m = c.expect(r'^line 1\d\d$')
+    >>> m.span()
+    (783, 791)
+    >>> c.bytes[783:791]
+    'line 100'
 Direct terminal usage