T
Toby
I'm trying to write a simple commandline wrapper: a script that runs
another program as a child and relays unbuffered stdin and stdout
to/from the child process, possibly filtering it.
The usefulness of such a program lies in the filtering stage, in a
possible integration with readline, or in other interface enhancements.
Still, I'd like to discuss with you the unfiltered, unembellished
version, because I'm not satisfied with it and because I'm having some
minor problems.
Here's the script:
#!/usr/bin/python
import sys, os, thread
def stdin_handler(child_stdin):
while True:
d = sys.stdin.read(1)
if not d: break
child_stdin.write(d)
child_stdin.flush()
child_stdin.close()
def stdout_handler(child_stdout):
while True:
try:
d = child_stdout.read(1)
if not d: break
sys.stdout.write(d)
sys.stdout.flush()
except KeyboardInterrupt:
pass
def wrap(cmd):
child_stdin, child_stdout = os.popen2(cmd)
thread.start_new_thread(stdin_handler, (child_stdin,))
stdout_handler(child_stdout)
if __name__ == '__main__':
wrap(sys.argv[1])
You invoke it passing a program name as the first argument (such as
bash, python itself, sbcl, whatever) and then you interact with the
child process as if this wrapper wasn't there. Ctrl-C and Ctrl-D work
as expected, sending SIGINT (that the child program can catch) and
closing stdin respectively.
Problems:
1.
I don't like the read(1) loops. I'd like to be able to read bigger
chunks at a time, but I haven't found a way to do that while keeping the
functionality intact. I have tried fidgeting with select() but I didn't
get far. What I (think I) need is a non-blocking read(), but I'm not
sure how to achieve that, at least with Python 2.4 on Linux.
2.
Even this version sometimes has problems. For example, if you launch
the wrapper around sbcl (a free Lisp interpreter/compiler) it mostly
works (entering 1 gives 1, entering (+ 2 3) gives 5...) until you get to
the debugger, for example by entering an undefined name. Now, when the
debugger says "0: [ABORT] Exit debugger...", entering 0 should get you
back to the "*" prompt--and indeed does, unless you are using my
wrapper, in which case everything hangs, and the only solution is to
terminate everything with Ctrl-\ or such.
Any idea how to improve the script and solve this problem?
Toby
another program as a child and relays unbuffered stdin and stdout
to/from the child process, possibly filtering it.
The usefulness of such a program lies in the filtering stage, in a
possible integration with readline, or in other interface enhancements.
Still, I'd like to discuss with you the unfiltered, unembellished
version, because I'm not satisfied with it and because I'm having some
minor problems.
Here's the script:
#!/usr/bin/python
import sys, os, thread
def stdin_handler(child_stdin):
while True:
d = sys.stdin.read(1)
if not d: break
child_stdin.write(d)
child_stdin.flush()
child_stdin.close()
def stdout_handler(child_stdout):
while True:
try:
d = child_stdout.read(1)
if not d: break
sys.stdout.write(d)
sys.stdout.flush()
except KeyboardInterrupt:
pass
def wrap(cmd):
child_stdin, child_stdout = os.popen2(cmd)
thread.start_new_thread(stdin_handler, (child_stdin,))
stdout_handler(child_stdout)
if __name__ == '__main__':
wrap(sys.argv[1])
You invoke it passing a program name as the first argument (such as
bash, python itself, sbcl, whatever) and then you interact with the
child process as if this wrapper wasn't there. Ctrl-C and Ctrl-D work
as expected, sending SIGINT (that the child program can catch) and
closing stdin respectively.
Problems:
1.
I don't like the read(1) loops. I'd like to be able to read bigger
chunks at a time, but I haven't found a way to do that while keeping the
functionality intact. I have tried fidgeting with select() but I didn't
get far. What I (think I) need is a non-blocking read(), but I'm not
sure how to achieve that, at least with Python 2.4 on Linux.
2.
Even this version sometimes has problems. For example, if you launch
the wrapper around sbcl (a free Lisp interpreter/compiler) it mostly
works (entering 1 gives 1, entering (+ 2 3) gives 5...) until you get to
the debugger, for example by entering an undefined name. Now, when the
debugger says "0: [ABORT] Exit debugger...", entering 0 should get you
back to the "*" prompt--and indeed does, unless you are using my
wrapper, in which case everything hangs, and the only solution is to
terminate everything with Ctrl-\ or such.
Any idea how to improve the script and solve this problem?
Toby