views:

67

answers:

2

I've written a simple program that captures and executes command line Python scripts, but there is a problem. The text passed to a Python input function isn't written to my program despite my program capturing stdout.

For example: The Python script:

import sys

print("Hello, World!")
x = input("Please enter a number: ")
print(x)

print("This work?")

Would write "Hello, World!" then stop. When I pass it a number it would continue on writing "Please enter a number: 3". What is going on? Any solutions? My C# is as follows:

public partial class PyCon : Window
{
        public string strPythonPath;
        public string strFile;
        public string strArguments;
        private StreamWriter sw;

        public PyCon(string pythonpath, string file, string args)
        {
            strPythonPath = pythonpath;
            strFile = file;
            strArguments = args;

            InitializeComponent();

            Process p = new Process();

            p.StartInfo.FileName = strPythonPath;
            p.StartInfo.Arguments = "\"" + strFile + "\" " + strArguments;

            p.StartInfo.UseShellExecute = false;
            p.StartInfo.CreateNoWindow = true;

            p.StartInfo.RedirectStandardInput = true;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardError = true;

            p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
            p.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceived);

            p.Start();
            p.BeginOutputReadLine();
            p.BeginErrorReadLine();
            sw = p.StandardInput;
        }

        private void p_OutputDataReceived(object sendingProcess, DataReceivedEventArgs received) {
            if (!String.IsNullOrEmpty(received.Data)) {
                AppendConsole(received.Data);
            }
        }

        private void p_ErrorDataReceived(object sendingProcess, DataReceivedEventArgs received) {
            if (!String.IsNullOrEmpty(received.Data)) {
                AppendConsole(received.Data);
            }
        }

        private void AppendConsole(string message) {
            if (!txtConsole.Dispatcher.CheckAccess()) {
                txtConsole.Dispatcher.Invoke(DispatcherPriority.Normal, (System.Windows.Forms.MethodInvoker)delegate() { txtConsole.AppendText(message + "\n"); });
            } else {
                //Format text
                message = message.Replace("\n", Environment.NewLine);

                txtConsole.AppendText(message + "\n");   
            }
        }

        private void txtInput_KeyUp(object sender, KeyEventArgs e) {
            if (e.Key != Key.Enter) return;

            sw.WriteLine(txtInput.Text);

            txtInput.Text = "";


        }
    }

Edit: After a lot of research and help from this thread, I've come to the conclusion that the problem is with the Python input command not calling the C# DataReceivedEventHandler. There may not be a solution to this besides scripting changes. If that is the case, I'll make the answer containing those changes as accepted. Thanks for the help, guys!

+1  A: 

Smells like the Python i/o is line buffered, i.e. waits for a CRLF then sends a whole line at once. You could try turning that off (python -u myscript.py, or set the PYTHONUNBUFFERED environment variable) or work around it with something like this:

print("Hello, World!")
print("Please enter a number: ")
x = input()
print(x)
crazyscot
The script workaround is great. It corrects the problem, but isn't ideal.Setting PYTHONUNBUFFERED or running the script with -u don't appear to correct the problem.
OSMman
+1  A: 

It's difficult to tell because I'm using python 2.6 and you appear to be using 3.x, and I also have not programmed in c# for quite awhile but my guess is that this line:

sw.WriteLine(txtInput.Text);

Is sending "3" plus a windows new line character.

Try:

sw.Write(txtInput.Text + "\n")
sw.Flush()

This will just send a normal newline instead of a windows carriage return which may be the issue. Make sure you always Flush when dealing with complicated stream communication like this!

One more thing, make sure you can just redirect this at the command prompt. Too often programmers try to do everything at the same time and get stuck:

./stdintest.py < input.txt

If it doesn't work there, it's not going to work in C#. Good luck

manifest
As a note, if I went in input.txt and instead of putting a normal newline, put a ^M (windows character) after the 3, python 2.6 would throw an exception "unexpected EOF while parsing". This may or may not be the case in python 3.x
manifest
Flushing the StreamWriter is definitely a good call. The newline thing however deals with essentially the opposite problem that I have. The input posts fine, but the string passed into the Python input command doesn't show up in my captured data. So running the program goes something like this:Hello, World!3 [<-I type this and it is posted to the StreamWriter]Please enter a number: 3This work?I think crazyscot was barking up the right tree.
OSMman
I switched to version 2.6. I see the error you were referring to. It doesn't seem to be a problem in 3.1.
OSMman
Ahh, I understand now.This seems to have the best stack of answers for this issue:http://stackoverflow.com/questions/107705/python-output-buffering
manifest
Unfortunately, the buffering doesn't seem to be the problem. I'm beginning to think it has to do with that DataReceivedEventHandler. The event doesn't seem to be getting called after input("whatever").
OSMman