【发布时间】:2016-11-22 10:41:40
【问题描述】:
我正在尝试构建一个使用 python 动态解释器及其 eval 函数的 wpf 应用程序。 编辑:我已经给出了更详细的描述here 简单来说,我希望能够执行以下操作:
string expression = Console.ReadLine("Please enter your expression");
if (EvaluateWithPythonProcess(expression) > 4)
{
// Do something
}
else
{
// Do something else
}
由于我的程序在其整个生命周期中都使用此功能,因此每次我想开始评估时都无法退出 python 进程。因此,StdIn、StdOut 和 StdErr 流始终保持打开状态。
我能够使用 Process 类和两个相应的 OnOutputDataReceived 和 OnErrorDataReceived 方法启动交互式 python.exe,它们将数据从 stdOut 和 stdErr 传输到 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;
但是,我无法将我的应用程序与这种异步结果收集同步。由于两个 On*DataReceived() 方法是异步调用的,我不知道 python 是否完成了对我的表达式的评估。一个可能的解决方案是在向 pythons stdIn 发送命令之前创建一个等待句柄,之后我可以等待。 OnOutputDataReceived 和 OnErrorDataReceived 方法都可以用信号通知这个句柄。然而,这在某种程度上被 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)");
【问题讨论】:
-
这个问题太笼统了。你有很多不同的场景你要问,但你没有提供确切的说明你想如何解决它们,也没有说明到目前为止你已经尝试过什么。请提供一个好的minimal reproducible example,清楚地显示您尝试过的内容,以及对代码的作用以及您希望它做什么的精确描述。同时,请记住 stdin 也被缓冲,所以如果您不期望特定的输出,您可以发送下一个输入。
-
至于跟踪什么是错误,什么不是,您必须做与人类相同的事情:根据您在 stdout 和/或 stderr 上收到的消息进行推断。实际上对于每个流的内容没有任何强制规则,一些进程使用 stdout 处理错误,而其他进程使用 stderr 输出或信息文本(例如警告)。由您决定如何处理任何一个流的输出。
-
嗯,我明白了基于上下文评估结果的重点。但是我的问题在我能做到之前就出现了:我怎么知道子进程已经收到我的命令,并完成了相应的操作(参见上面的示例 A:我无法判断 python 是否理解这个导入,或者卡在一个例如死锁...)
-
为了评估我已经做过的事情,请参阅我昨天尝试同步方法的帖子:stackoverflow.com/questions/38430330/…
-
“我怎么知道子进程已经收到了我的命令,并且已经完成了相应的动作”——和人类一样:你读输出。如果没有输出,您将遇到与人类相同的问题:您不知道进程是否被卡住,或者它是否已准备好执行新命令。您所能做的就是尝试输入一个新命令,看看是否有任何输出。
标签: c# multithreading asynchronous stdout stdin