Simple process IO capture (Was: "shell-commands" and python!) - process.py (0/1)

Paul Moore gustav at morpheus.demon.co.uk
Tue Sep 25 17:25:33 EDT 2001


On Mon, 24 Sep 2001 13:42:22 -0700, Neil Schemenauer <nas at python.ca> wrote:

>Here's my humble attempt at a popen replacement.  Note that it doesn't
>use the shell (a feature IMHO) and it only works on Unix systems.
>Suggestions for the module name are welcome.  I would like to get
>something like this in the standard library.

I like the idea of having some form of "simplified piping" module in the
standard library. But I would *much* rather see something that is portable (at
least to Unix and Windows - I've no idea if the Mac has the concept of a command
line which would make sense here). The low-level building blocks are of
necessity non-portable, but the higher-level wrappers really ought to be (in
interface, if not implementation). Precisely because the lower levels are
non-portable - a portable higher level interface would make writing portable
programs that bit easier.

A portability requirement makes designing a suitable interface far more
difficult, but I think it's worth it.

(BTW, I could probably write a Win32 implementation of your interface (modulo
the availability of suitable low-level calls - I'd need to depend on either
win32all or dynwin). The only issue would be the argv tuples, which are *not*
the lowest level Win32 form - precisely the reverse of Unix (in Unix, execv()
takes argc/argv, and the shell converts command lines to argv arrays; on Win32,
CreateProcess() takes a command line, and the C runtime for an individual
program splits the command line into an argv array).

I'm relatively happy with the _ChildProcess class as a way of encapsulating the
various FDs relevant to the child. Although I miss the shortcuts my module has
for passing a string as stdin direct, or getting the contents of stdout direct.
Your class' equivalent is more verbose -

    p = execute(...)
    p.write(str)
    p.stdin.close()
    ret = p.read()
    p.stdout.close()

I might simply add extra "convenience" methods,

    def input(self, str):
        self.stdin.write(str)
        self.stdin.close()
        return self # to allow chaining, see below
    def output(self):
        str = self.stdout.read()
        self.stdout.close()
        return str

which allows

    ret = execute(...).input(str).output()

Hmm, no that still feels messy. I can't get away from the fact that my version

    ret = run(..., input=str)

feels cleaner.

And by the way, your version doesn't offer any way of "merging" stdout and
stderr. Not reading both, but merging as in 2>&1.

And I'd *still* like to add a way of specifying a pipeline, with all the
inter-process plumbing handled automatically. Something like

    child_obj = execute(("tr a-z A-Z", "sort", "uniq"))

(reverting to command-string form rather than argv-tuple, to avoid obscuring the
issue with nested tuples...)

OK, that's enough unstructured brain-dumping. I'll think about this some more,
and post a proposal.

[BTW, I assume that avoiding the shell is an important requirement on Unix? It's
less of an obvious thing to do on Win32, where a number of key commands like
DIR, COPY, DEL, RENAME and MOVE are shell built-ins, and not normally available
as separate EXEs]

Paul.



More information about the Python-list mailing list