/*========================================================================= Program: Visualization Toolkit Module: vtkPythonInterpreter.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkPython.h" // this must be the first include. #include "vtkPythonInterpreter.h" #include "vtkCommand.h" #include "vtkObjectFactory.h" #include "vtkPythonStdStreamCaptureHelper.h" #include "vtkWeakPointer.h" #include #include #include #include #include #if PY_VERSION_HEX >= 0x03000000 #if defined(__APPLE__) && PY_VERSION_HEX < 0x03050000 extern "C" { extern wchar_t* _Py_DecodeUTF8_surrogateescape(const char* s, Py_ssize_t size); } #endif #endif namespace { class StringPool { public: std::vector Strings; ~StringPool() { for (size_t cc = 0; cc < this->Strings.size(); cc++) { delete[] this->Strings[cc]; } } }; static std::vector > GlobalInterpreters; static std::vector PythonPaths; void NotifyInterpreters(unsigned long eventid, void* calldata = nullptr) { std::vector >::iterator iter; for (iter = GlobalInterpreters.begin(); iter != GlobalInterpreters.end(); ++iter) { if (iter->GetPointer()) { iter->GetPointer()->InvokeEvent(eventid, calldata); } } } inline void vtkPrependPythonPath(const char* pathtoadd) { vtkPythonScopeGilEnsurer gilEnsurer; PyObject* path = PySys_GetObject(const_cast("path")); #if PY_VERSION_HEX >= 0x03000000 PyObject* newpath = PyUnicode_FromString(pathtoadd); #else PyObject* newpath = PyString_FromString(pathtoadd); #endif PyList_Insert(path, 0, newpath); Py_DECREF(newpath); } } bool vtkPythonInterpreter::InitializedOnce = false; bool vtkPythonInterpreter::CaptureStdin = false; bool vtkPythonInterpreter::ConsoleBuffering = false; std::string vtkPythonInterpreter::StdErrBuffer; std::string vtkPythonInterpreter::StdOutBuffer; vtkStandardNewMacro(vtkPythonInterpreter); //---------------------------------------------------------------------------- vtkPythonInterpreter::vtkPythonInterpreter() { GlobalInterpreters.push_back(this); } //---------------------------------------------------------------------------- vtkPythonInterpreter::~vtkPythonInterpreter() { std::vector >::iterator iter; for (iter = GlobalInterpreters.begin(); iter != GlobalInterpreters.end(); ++iter) { if (*iter == this) { GlobalInterpreters.erase(iter); break; } } } //---------------------------------------------------------------------------- bool vtkPythonInterpreter::IsInitialized() { return (Py_IsInitialized() != 0); } //---------------------------------------------------------------------------- void vtkPythonInterpreter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); } //---------------------------------------------------------------------------- bool vtkPythonInterpreter::Initialize(int initsigs /*=0*/) { if (Py_IsInitialized() == 0) { #if (VTK_PYTHON_MAJOR_VERSION > 2) || \ (VTK_PYTHON_MAJOR_VERSION == 2 && VTK_PYTHON_MINOR_VERSION >= 4) Py_InitializeEx(initsigs); #else (void)initsigs; Py_Initialize(); #endif #ifdef SIGINT // Put default SIGINT handler back after Py_Initialize/Py_InitializeEx. signal(SIGINT, SIG_DFL); #endif } if (!vtkPythonInterpreter::InitializedOnce) { vtkPythonInterpreter::InitializedOnce = true; #ifdef VTK_PYTHON_FULL_THREADSAFE int threadInit = PyEval_ThreadsInitialized(); PyEval_InitThreads(); // safe to call this multiple time if (!threadInit) { PyEval_SaveThread(); // release GIL } #endif // HACK: Calling PyRun_SimpleString for the first time for some reason results in // a "\n" message being generated which is causing the error dialog to // popup. So we flush that message out of the system before setting up the // callbacks. vtkPythonInterpreter::RunSimpleString(""); // Setup handlers for stdout/stdin/stderr. vtkPythonStdStreamCaptureHelper* wrapperOut = NewPythonStdStreamCaptureHelper(false); vtkPythonStdStreamCaptureHelper* wrapperErr = NewPythonStdStreamCaptureHelper(true); // Redirect Python's stdout and stderr and stdin - GIL protected operation { vtkPythonScopeGilEnsurer gilEnsurer; PySys_SetObject(const_cast("stdout"), reinterpret_cast(wrapperOut)); PySys_SetObject(const_cast("stderr"), reinterpret_cast(wrapperErr)); PySys_SetObject(const_cast("stdin"), reinterpret_cast(wrapperOut)); Py_DECREF(wrapperOut); Py_DECREF(wrapperErr); } for (size_t cc = 0; cc < PythonPaths.size(); cc++) { vtkPrependPythonPath(PythonPaths[cc].c_str()); } NotifyInterpreters(vtkCommand::EnterEvent); return true; } return false; } //---------------------------------------------------------------------------- void vtkPythonInterpreter::Finalize() { if (Py_IsInitialized() != 0) { NotifyInterpreters(vtkCommand::ExitEvent); vtkPythonScopeGilEnsurer gilEnsurer(false, true); // Py_Finalize will take care of relasing gil Py_Finalize(); } } //---------------------------------------------------------------------------- void vtkPythonInterpreter::SetProgramName(const char* programname) { if (vtkPythonInterpreter::InitializedOnce || Py_IsInitialized() != 0) { return; } if (programname) { static StringPool pool; pool.Strings.push_back(vtksys::SystemTools::DuplicateString(programname)); // From Python Docs: The argument should point to a zero-terminated character // string in static storage whose contents will not change for the duration of // the program's execution. No code in the Python interpreter will change the // contents of this storage. #if PY_VERSION_HEX >= 0x03000000 wchar_t* argv0; const std::string& av0 = pool.Strings.back(); #if PY_VERSION_HEX >= 0x03050000 argv0 = Py_DecodeLocale(av0.c_str(), nullptr); #elif defined(__APPLE__) argv0 = _Py_DecodeUTF8_surrogateescape(av0.data(), av0.length()); #else argv0 = _Py_char2wchar(av0.c_str(), nullptr); #endif if (argv0 == 0) { fprintf(stderr, "Fatal vtkpython error: " "unable to decode the program name\n"); static wchar_t empty[1] = { 0 }; argv0 = empty; } Py_SetProgramName(argv0); #else Py_SetProgramName(pool.Strings.back()); #endif } } //---------------------------------------------------------------------------- void vtkPythonInterpreter::PrependPythonPath(const char* dir) { if (!dir) { return; } std::string out_dir = dir; #if defined(_WIN32) && !defined(__CYGWIN__) // Convert slashes for this platform. std::replace(out_dir.begin(), out_dir.end(), '/', '\\'); #endif // save path for future use. PythonPaths.push_back(out_dir); if (Py_IsInitialized() == 0) { return; } // Append the path to the python sys.path object. vtkPrependPythonPath(out_dir.c_str()); } //---------------------------------------------------------------------------- int vtkPythonInterpreter::PyMain(int argc, char** argv) { if (!vtkPythonInterpreter::InitializedOnce && Py_IsInitialized() == 0 && argc > 0) { vtkPythonInterpreter::SetProgramName(argv[0]); } vtkPythonInterpreter::Initialize(1); #if PY_VERSION_HEX >= 0x03000000 wchar_t* argv0; #if PY_VERSION_HEX >= 0x03050000 argv0 = Py_DecodeLocale(argv[0], nullptr); #elif defined(__APPLE__) argv0 = _Py_DecodeUTF8_surrogateescape(argv[0], strlen(argv[0])); #else argv0 = _Py_char2wchar(argv[0], nullptr); #endif if (argv0 == 0) { static wchar_t empty[1] = { 0 }; argv0 = empty; } // Need two copies of args, because programs might modify the first wchar_t** argvWide = new wchar_t*[argc]; wchar_t** argvWide2 = new wchar_t*[argc]; for (int i = 0; i < argc; i++) { #if PY_VERSION_HEX >= 0x03050000 argvWide[i] = Py_DecodeLocale(argv[i], nullptr); #elif defined(__APPLE__) argvWide[i] = _Py_DecodeUTF8_surrogateescape(argv[i], strlen(argv[i])); #else argvWide[i] = _Py_char2wchar(argv[i], nullptr); #endif argvWide2[i] = argvWide[i]; if (argvWide[i] == 0) { fprintf(stderr, "Fatal vtkpython error: " "unable to decode the command line argument #%i\n", i + 1); for (int k = 0; k < i; k++) { PyMem_Free(argvWide2[i]); } PyMem_Free(argv0); delete[] argvWide; delete[] argvWide2; return 1; } } vtkPythonScopeGilEnsurer gilEnsurer; int res = Py_Main(argc, argvWide); PyMem_Free(argv0); for (int i = 0; i < argc; i++) { PyMem_Free(argvWide2[i]); } delete[] argvWide; delete[] argvWide2; return res; #else vtkPythonScopeGilEnsurer gilEnsurer(false, true); return Py_Main(argc, argv); #endif } //---------------------------------------------------------------------------- int vtkPythonInterpreter::RunSimpleString(const char* script) { vtkPythonInterpreter::Initialize(1); vtkPythonInterpreter::ConsoleBuffering = true; // The embedded python interpreter cannot handle DOS line-endings, see // http://sourceforge.net/tracker/?group_id=5470&atid=105470&func=detail&aid=1167922 std::string buffer = script ? script : ""; buffer.erase(std::remove(buffer.begin(), buffer.end(), '\r'), buffer.end()); // The cast is necessary because PyRun_SimpleString() hasn't always been const-correct int pyReturn; { vtkPythonScopeGilEnsurer gilEnsurer; pyReturn = PyRun_SimpleString(const_cast(buffer.c_str())); } vtkPythonInterpreter::ConsoleBuffering = false; if (!vtkPythonInterpreter::StdErrBuffer.empty()) { vtkOutputWindowDisplayErrorText(vtkPythonInterpreter::StdErrBuffer.c_str()); NotifyInterpreters( vtkCommand::ErrorEvent, const_cast(vtkPythonInterpreter::StdErrBuffer.c_str())); vtkPythonInterpreter::StdErrBuffer.clear(); } if (!vtkPythonInterpreter::StdOutBuffer.empty()) { vtkOutputWindowDisplayText(vtkPythonInterpreter::StdOutBuffer.c_str()); NotifyInterpreters( vtkCommand::SetOutputEvent, const_cast(vtkPythonInterpreter::StdOutBuffer.c_str())); vtkPythonInterpreter::StdOutBuffer.clear(); } return pyReturn; } //---------------------------------------------------------------------------- void vtkPythonInterpreter::SetCaptureStdin(bool val) { vtkPythonInterpreter::CaptureStdin = val; } //---------------------------------------------------------------------------- bool vtkPythonInterpreter::GetCaptureStdin() { return vtkPythonInterpreter::CaptureStdin; } //---------------------------------------------------------------------------- void vtkPythonInterpreter::WriteStdOut(const char* txt) { if (vtkPythonInterpreter::ConsoleBuffering) { vtkPythonInterpreter::StdOutBuffer += std::string(txt); } else { vtkOutputWindowDisplayText(txt); NotifyInterpreters(vtkCommand::SetOutputEvent, const_cast(txt)); } } //---------------------------------------------------------------------------- void vtkPythonInterpreter::FlushStdOut() { } //---------------------------------------------------------------------------- void vtkPythonInterpreter::WriteStdErr(const char* txt) { if (vtkPythonInterpreter::ConsoleBuffering) { vtkPythonInterpreter::StdErrBuffer += std::string(txt); } else { vtkOutputWindowDisplayErrorText(txt); NotifyInterpreters(vtkCommand::ErrorEvent, const_cast(txt)); } } //---------------------------------------------------------------------------- void vtkPythonInterpreter::FlushStdErr() { } //---------------------------------------------------------------------------- vtkStdString vtkPythonInterpreter::ReadStdin() { if (!vtkPythonInterpreter::CaptureStdin) { vtkStdString string; cin >> string; return string; } vtkStdString string; NotifyInterpreters(vtkCommand::UpdateEvent, &string); return string; }