You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

143 lines
4.2 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
  5. * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software: you can redistribute it and/or modify it
  8. * under the terms of the GNU General Public License as published by the
  9. * Free Software Foundation, either version 3 of the License, or (at your
  10. * option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include <wx/process.h>
  21. #include <utility>
  22. #include <paths.h>
  23. #include <python_manager.h>
  24. class PYTHON_PROCESS : public wxProcess
  25. {
  26. public:
  27. PYTHON_PROCESS( std::function<void(int, const wxString&, const wxString&)> aCallback ) :
  28. wxProcess(),
  29. m_callback( std::move( aCallback ) )
  30. {}
  31. void OnTerminate( int aPid, int aStatus ) override
  32. {
  33. if( m_callback )
  34. {
  35. wxString output, error;
  36. wxInputStream* processOut = GetInputStream();
  37. size_t bytesRead = 0;
  38. while( processOut->CanRead() && bytesRead < MAX_OUTPUT_LEN )
  39. {
  40. char buffer[4096];
  41. buffer[ processOut->Read( buffer, sizeof( buffer ) - 1 ).LastRead() ] = '\0';
  42. output.append( buffer, processOut->LastRead() );
  43. bytesRead += processOut->LastRead();
  44. }
  45. processOut = GetErrorStream();
  46. bytesRead = 0;
  47. while( processOut->CanRead() && bytesRead < MAX_OUTPUT_LEN )
  48. {
  49. char buffer[4096];
  50. buffer[ processOut->Read( buffer, sizeof( buffer ) - 1 ).LastRead() ] = '\0';
  51. error.append( buffer, processOut->LastRead() );
  52. bytesRead += processOut->LastRead();
  53. }
  54. m_callback( aStatus, output, error );
  55. }
  56. }
  57. static constexpr size_t MAX_OUTPUT_LEN = 1024L * 1024L;
  58. private:
  59. std::function<void(int, const wxString&, const wxString&)> m_callback;
  60. };
  61. void PYTHON_MANAGER::Execute( const wxString& aArgs,
  62. const std::function<void( int, const wxString&,
  63. const wxString& )>& aCallback,
  64. const wxExecuteEnv* aEnv )
  65. {
  66. PYTHON_PROCESS* process = new PYTHON_PROCESS( aCallback );
  67. process->Redirect();
  68. wxString cmd = wxString::Format( wxS( "%s %s" ), m_interpreterPath, aArgs );
  69. long pid = wxExecute( cmd, wxEXEC_ASYNC, process, aEnv );
  70. if( pid == 0 )
  71. aCallback( -1, wxEmptyString, _( "Process could not be created" ) );
  72. }
  73. wxString PYTHON_MANAGER::FindPythonInterpreter()
  74. {
  75. #ifdef _WIN32
  76. // TODO(JE) where
  77. #else
  78. wxArrayString output;
  79. if( 0 == wxExecute( wxS( "which -a python3" ), output, wxEXEC_SYNC ) )
  80. {
  81. if( !output.IsEmpty() )
  82. return output[0];
  83. }
  84. if( 0 == wxExecute( wxS( "which -a python" ), output, wxEXEC_SYNC ) )
  85. {
  86. if( !output.IsEmpty() )
  87. return output[0];
  88. }
  89. #endif
  90. return wxEmptyString;
  91. }
  92. std::optional<wxString> PYTHON_MANAGER::GetPythonEnvironment( const wxString& aNamespace )
  93. {
  94. wxFileName path( PATHS::GetUserCachePath(), wxEmptyString );
  95. path.AppendDir( wxS( "python-environments" ) );
  96. path.AppendDir( aNamespace );
  97. if( !PATHS::EnsurePathExists( path.GetPath() ) )
  98. return std::nullopt;
  99. return path.GetPath();
  100. }
  101. std::optional<wxString> PYTHON_MANAGER::GetVirtualPython( const wxString& aNamespace )
  102. {
  103. std::optional<wxString> envPath = GetPythonEnvironment( aNamespace );
  104. if( !envPath )
  105. return std::nullopt;
  106. wxFileName python( *envPath, wxEmptyString );
  107. python.AppendDir( "bin" );
  108. python.SetFullName( "python" );
  109. if( !python.IsFileExecutable() )
  110. return std::nullopt;
  111. return python.GetFullPath();
  112. }