Rework how Python processes are launched

Fixes https://gitlab.com/kicad/code/kicad/-/issues/19465
This commit is contained in:
Jon Evans
2025-01-03 17:21:03 -05:00
parent 2e0f688b97
commit 1a1120435f
4 changed files with 52 additions and 13 deletions
+41 -2
View File
@@ -22,8 +22,10 @@
#include <gestfich.h>
#include <wx/process.h>
#include <future>
#include <utility>
#include <api/api_utils.h>
#include <paths.h>
#include <pgm_base.h>
#include <python_manager.h>
@@ -40,6 +42,9 @@ public:
void OnTerminate( int aPid, int aStatus ) override
{
// Print stdout trace info from the monitor thread
wxLog::GetActiveTarget()->Flush();
if( m_callback )
{
wxString output, error;
@@ -86,17 +91,51 @@ PYTHON_MANAGER::PYTHON_MANAGER( const wxString& aInterpreterPath )
void PYTHON_MANAGER::Execute( const wxString& aArgs,
const std::function<void( int, const wxString&,
const wxString& )>& aCallback,
const wxExecuteEnv* aEnv )
const wxString& )>& aCallback,
const wxExecuteEnv* aEnv, bool aSaveOutput )
{
PYTHON_PROCESS* process = new PYTHON_PROCESS( aCallback );
process->Redirect();
auto monitor =
[]( PYTHON_PROCESS* aProcess )
{
wxInputStream* processOut = aProcess->GetInputStream();
while( aProcess->IsInputOpened() )
{
if( processOut->CanRead() )
{
char buffer[4096];
buffer[processOut->Read( buffer, sizeof( buffer ) - 1 ).LastRead()] = '\0';
wxString stdOut( buffer, processOut->LastRead() );
stdOut = stdOut.BeforeLast( '\n' );
wxLogTrace( traceApi, wxString::Format( "Python: %s", stdOut ) );
}
}
};
wxString cmd = wxString::Format( wxS( "%s %s" ), m_interpreterPath, aArgs );
long pid = wxExecute( cmd, wxEXEC_ASYNC, process, aEnv );
if( pid == 0 )
{
delete process;
aCallback( -1, wxEmptyString, _( "Process could not be created" ) );
}
else
{
// On Windows, if there is a lot of stdout written by the process, this can
// hang up the wxProcess such that it will never call OnTerminate. To work
// around this, we use this monitor thread to just dump the stdout to the
// trace log, which prevents the hangup. This flag is provided to keep the
// old behavior for commands where we need to read the output directly,
// which is currently only used for detecting the interpreter version.
// If we need to use the async monitor thread approach and preserve the stdout
// contents in the future, a more complicated hack might be necessary.
if( !aSaveOutput )
auto future = std::async( std::launch::async, monitor, process );
}
}