I am trying to build a wpf application that makes use of pythons dynamic interpreter and its eval function. EDIT: I already gave a more detailed description here In simple words, I want to be able to do something like the following:
string expression = Console.ReadLine("Please enter your expression");
if (EvaluateWithPythonProcess(expression) > 4)
{
// Do something
}
else
{
// Do something else
}
As my program uses this functionality during it's entire lifetime, I am not able to exit the python process each time I want to start an evaluation. As a consequence, the StdIn, StdOut and StdErr Streams remain open all the time.
I was able to start an interactive python.exe using the Process class and two corresponding OnOutputDataReceived and OnErrorDataReceived methods that transfer data from stdOut and stdErr into StringBuilders:
// create the python process StartupInfo object
ProcessStartInfo _processStartInfo = new ProcessStartInfo(PythonHelper.PathToPython + "python.exe");
// python uses "-i" to run in interactive mode
_processStartInfo.Arguments = "-i";
// Only start the python process, but don't show a (console) window
_processStartInfo.WindowStyle = ProcessWindowStyle.Minimized;
_processStartInfo.CreateNoWindow = true;
// Enable the redirection of python process std's
_processStartInfo.UseShellExecute = false;
_processStartInfo.RedirectStandardOutput = true;
_processStartInfo.RedirectStandardInput = true;
_processStartInfo.RedirectStandardError = true;
// Create the python process object and apply the startupInfos from above
_pythonProcess = new Process();
_pythonProcess.StartInfo = _processStartInfo;
// Start the process, _hasStarted indicates if the process was actually started (true) or if it was reused (false, was already running)
_pythonProcess.OutputDataReceived += new DataReceivedEventHandler(OnOutputDataReceived);
_pythonProcess.ErrorDataReceived += new DataReceivedEventHandler(OnErrorDataReceived);
bool _hasStarted = _pythonProcess.Start();
_pythonProcess.BeginOutputReadLine();
_pythonProcess.BeginErrorReadLine();
_input = _pythonProcess.StandardInput;
However, I cannot manage to synchronize my application with this asynchronous gathering of results. As the two On*DataReceived() Methods are called asynchronously, I do not know if python has finished the evaluation of my expression. A possible solution would be to create a wait handle before sending commands to pythons stdIn which I can wait for afterwards. Both the OnOutputDataReceived and the OnErrorDataReceived Methods could signal this handle. However, this is somehow obscured by the intended behaviour of python:
// example A: Import the sys modul in python
// this does cause neither an output, nor an error:
_input.WriteLine("import sys");
// example B: Writing to pythons stderr or stdout results in Error AND Output, how can I tell if an error occured?
_input.WriteLine("sys.stderr.write('Initialized stdErr')");
_input.WriteLine("sys.stdout.write('Initialized stdOut')");
// example C: This is the intended use, but how can I tell if evaluation has finished succesfully?
_input.WriteLine("print(4+7)");
// example D: A simple typo might lead to unforeseeable errors but how can I tell if evaluation has finished succesfully?
_input.WriteLine("pr int(4+7)");