I'm trying to make a program which takes an executable name as an argument, runs the executable and reports the inputs and outputs for that run. For example consider a child program named "circle". The following would be desired run for my program:
$ python3 capture_io.py ./circle Enter radius of circle: 10 Area: 314.158997 [('output', 'Enter radius of circle: '), ('input', '10\n'), ('output', 'Area: 314.158997\n')]
I decided to use pexpect
module for this job. It has a method called interact
which lets the user interact with the child program as seen above. It also takes 2 optional parameters: output_filter
and input_filter
. From the documentation:
The
output_filter
will be passed all the output from the child process. Theinput_filter
will be passed all the keyboard input from the user.
So this is the code I wrote:
capture_io.py
import sys
import pexpect
_stdios = []
def read(data):
_stdios.append(("output", data.decode("utf8")))
return data
def write(data):
_stdios.append(("input", data.decode("utf8")))
return data
def capture_io(argv):
_stdios.clear()
child = pexpect.spawn(argv)
child.interact(input_filter=write, output_filter=read)
child.wait()
return _stdios
if __name__ == '__main__':
stdios_of_child = capture_io(sys.argv[1:])
print(stdios_of_child)
circle.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
float radius, area;
printf("Enter radius of circle: ");
scanf("%f", &radius);
if (radius < 0) {
fprintf(stderr, "Negative radius values are not allowed.\n");
exit(1);
}
area = 3.14159 * radius * radius;
printf("Area: %f\n", area);
return 0;
}
Which produces the following output:
$ python3 capture_io.py ./circle Enter radius of circle: 10 Area: 314.158997 [('output', 'Enter radius of circle: '), ('input', '1'), ('output', '1'), ('input', '0'), ('output', '0'), ('input', '\r'), ('output', '\r\n'), ('output', 'Area: 314.158997\r\n')]
As you can observe from the output, input is processed character by character and also echoed back as output which creates such a mess. Is it possible to change this behaviour so that my input_filter
will run only when Enter
is pressed?
Or more generally, what would be the best way to achieve my goal (with or without pexpect
)?