diff --git a/common/api/api_plugin_manager.cpp b/common/api/api_plugin_manager.cpp index dd4694ff75..f4c430f581 100644 --- a/common/api/api_plugin_manager.cpp +++ b/common/api/api_plugin_manager.cpp @@ -408,7 +408,7 @@ void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent ) [this]( int aRetVal, const wxString& aOutput, const wxString& aError ) { wxLogTrace( traceApi, - wxString::Format( "Manager: venv (%d): %s", aRetVal, aOutput ) ); + wxString::Format( "Manager: created venv (python returned %d)", aRetVal ) ); if( !aError.IsEmpty() ) wxLogTrace( traceApi, wxString::Format( "Manager: venv err: %s", aError ) ); @@ -455,8 +455,8 @@ void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent ) manager.Execute( cmd, [this]( int aRetVal, const wxString& aOutput, const wxString& aError ) { - wxLogTrace( traceApi, wxString::Format( "Manager: upgrade pip (%d): %s", - aRetVal, aOutput ) ); + wxLogTrace( traceApi, wxString::Format( "Manager: upgrade pip returned %d", + aRetVal ) ); if( !aError.IsEmpty() ) { @@ -501,15 +501,15 @@ void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent ) PYTHON_MANAGER manager( *python ); wxExecuteEnv env; - if( pythonHome ) - env.env[wxS( "VIRTUAL_ENV" )] = *pythonHome; - #ifdef _WIN32 wxString systemRoot; wxGetEnv( wxS( "SYSTEMROOT" ), &systemRoot ); env.env[wxS( "SYSTEMROOT" )] = systemRoot; #endif + if( pythonHome ) + env.env[wxS( "VIRTUAL_ENV" )] = *pythonHome; + wxString cmd = wxString::Format( wxS( "-m pip install --no-input --isolated --prefer-binary --require-virtualenv " "--exists-action i -r \"%s\"" ), @@ -520,9 +520,6 @@ void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent ) manager.Execute( cmd, [this, job]( int aRetVal, const wxString& aOutput, const wxString& aError ) { - if( !aOutput.IsEmpty() ) - wxLogTrace( traceApi, wxString::Format( "Manager: pip: %s", aOutput ) ); - if( !aError.IsEmpty() ) wxLogTrace( traceApi, wxString::Format( "Manager: pip stderr: %s", aError ) ); diff --git a/common/dialogs/panel_plugin_settings.cpp b/common/dialogs/panel_plugin_settings.cpp index f4b76b4508..372a227d33 100644 --- a/common/dialogs/panel_plugin_settings.cpp +++ b/common/dialogs/panel_plugin_settings.cpp @@ -153,5 +153,7 @@ void PANEL_PLUGIN_SETTINGS::validatePythonInterpreter() m_stPythonStatus->SetLabel( msg ); Layout(); - } ); + }, + /* aEnv = */ nullptr, + /* aSaveOutput = */ true ); } diff --git a/scripting/python_manager.cpp b/scripting/python_manager.cpp index 8732a763cd..37f4f79df0 100644 --- a/scripting/python_manager.cpp +++ b/scripting/python_manager.cpp @@ -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 ); + } } diff --git a/scripting/python_manager.h b/scripting/python_manager.h index c61aa57547..96f5c84d58 100644 --- a/scripting/python_manager.h +++ b/scripting/python_manager.h @@ -36,7 +36,8 @@ public: void Execute( const wxString& aArgs, const std::function<void(int, const wxString&, const wxString&)>& aCallback, - const wxExecuteEnv* aEnv = nullptr ); + const wxExecuteEnv* aEnv = nullptr, + bool aSaveOutput = false ); wxString GetInterpreterPath() const { return m_interpreterPath; } void SetInterpreterPath( const wxString& aPath ) { m_interpreterPath = aPath; }