// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause /** * @class vtkPythonInterpreter * @brief wrapper for an embedded Python interpreter. * * vtkPythonInterpreter only offers static methods. However, there may be need * to register callbacks to call after the Python interpreter is initialized * and before the interpreter is finalized. One can register observers for * vtkCommand:EnterEvent and vtkCommand::ExitEvent to a local instance of * vtkPythonInterpreter. vtkPythonInterpreter keeps tracks of all instances and * invokes those events on all instances at appropriate times. * * Same is true for obtaining outputs/errors generated by Python. * vtkCommand::ErrorEvent and vtkCommand::SetOutputEvent will be fired with * calldata being const char* to the messages. Errors and output messages will * also be forwarded to the vtkOutputWindow singleton (via * `vtkOutputWindowDisplayErrorText` and `vtkOutputWindowDisplayText` calls). * Changing the output window temporarily (e.g. using a * vtkStringOutputWindow) is another way of capturing messages generated through * Python scripts. * * To capture stdin, especially for non-terminal applications, set CaptureStdin * to true. In that case vtkCommand::UpdateEvent will be fired with the calldata * being a reference to a vtkStdString that should be filled in with the text to * be passed in as the input. * * A few of the methods on this class implicitly call * vtkPythonInterpreter::Initialize() to ensure Python is initialized viz. * vtkPythonInterpreter::PyMain() and vtkPythonInterpreter::RunSimpleString(). * These implicit initialization always calls * vtkPythonInterpreter::Initialize(1). If that's not what is expected, * ensure that you call vtkPythonInterpreter::Initialize(0) before calling such * methods. Refer to Py_InitializeEx() documentation for details on the * differences between the two. * * Notes on calling Initialize/Finalize multiple times: Although applications * are free to call Initialize/Finalize pairs multiple times, there are subtle * differences between the first Initialize and subsequence Initialize calls * after Finalize especially when concerning with imported modules. Refer to * Python docs for details. In short, modules like numpy don't continue to work * after a re-initialize. Hence use it with caution. */ #ifndef vtkPythonInterpreter_h #define vtkPythonInterpreter_h #include "vtkObject.h" #include "vtkPythonInterpreterModule.h" // For export macro #include "vtkStdString.h" // needed for vtkStdString. #include // For std::pair #include VTK_ABI_NAMESPACE_BEGIN class VTKPYTHONINTERPRETER_EXPORT vtkPythonInterpreter : public vtkObject { public: static vtkPythonInterpreter* New(); vtkTypeMacro(vtkPythonInterpreter, vtkObject); void PrintSelf(ostream& os, vtkIndent indent) override; /** * Call this method to initialize Python. This has no effect if Python is * already initialized. Returns true if Python was initialized by this call, * or false if Python was already initialized. * Although, one can call Initialize()/Finalize() pair multiple times, Python * documentation warns that "Some extensions may not work properly if their * initialization routine is called more than once; this can happen if an * application calls Py_InitializeEx() and Py_Finalize() more than once." */ static bool Initialize(int initsigs = 1); /** * Call this method to finalize Python. This has no effect if Python hasn't * been initialized already. */ static void Finalize(); /** * Returns true is Python is initialized. */ static bool IsInitialized(); /** * Set the program name. * * Python uses the program name to determine values for prefix and exec_prefix * paths that are used to locate Python standard libraries and hence call this * only if you know what you are doing. * * If not explicitly overridden, `Initialize` will try to guess a good default * for the program name to help find the Python standard libraries based on * Python libraries used to build VTK. */ static void SetProgramName(const char* programname); /** * Call this method to start the Python event loop (Py_Main()). * This will initialize Python if not already initialized. * * @note This function handles `--enable-bt` command line argument before * passing it on to the `Py_Main(..)` call. Thus, the `--enable-bt` flag is * also removed from the arguments passed to `Py_Main`. */ static int PyMain(int argc, char** argv); /** * Developers are free to call Python C API directly. This convenience method * is provided to overcome an issue with the Python interpreter with handling * of DOS line endings. * This will initialize Python if not already initialized. * Returns 0 on success or -1 if a python exception was raised. */ static int RunSimpleString(const char* script); /** * Prepends the path to the sys.path variable. If Python has been * initialized, this call will update the sys.path variable otherwise the same * will be done once Python has been initialized. The paths added are saved so * that if Python is initialized again (by calls to Initialize()), then these * paths will be re-added. */ static void PrependPythonPath(const char* dir); ///@{ /** * Prepend custom paths to `sys.path` after attempt to find the `landmark` using the * `anchor` prefix provided. If found, the path to the landmark gets added the python path * using `PrependPythonPath`. Applications can use this to add paths to custom modules * in the module search path. This is also needed for static builds to assist the * interpreter in locating the path to `vtk` package. * * When `add_landmark` is true, then instead of adding the path to the * landmark to the module search path, the successfully located landmark itself is * added to the module search path. This is helpful when using zip-modules, * for example, since in that case, the zip file itself should be added to the * module search path and not its location. */ static void PrependPythonPath( const char* anchor, const char* landmark, bool add_landmark = false); ///@} ///@{ /** * To capture stdin, especially for non-terminal applications, set CaptureStdin * to true. In that case vtkCommand::UpdateEvent will be fired with the calldata * being a reference to a vtkStdString that should be filled in with the text to * be passed in as the input. */ static void SetCaptureStdin(bool); static bool GetCaptureStdin(); ///@} ///@{ /** * Enable/disable VTK from redirecting Python output to vtkOutputWindow. On by default. */ static void SetRedirectOutput(bool redirect); static bool GetRedirectOutput(); ///@} ///@{ /** * Get/Set the verbosity level at which vtkPythonInterpreter should generate * log output. Default value is `vtkLogger::VERBOSITY_TRACE`. */ static void SetLogVerbosity(int); static int GetLogVerbosity(); ///@} /** * Initialize the Python interpreter, forwarding specified args to it. * If programName is set, use it as the interpreter's program name. Otherwise, it * will be set to "/vtkpython" */ static bool InitializeWithArgs( int initsigs, int argc, char* argv[], const char* programName = nullptr); /** * Programs using VTK (such as ParaView) can add one or more paths for additional python modules. * libraryPath and landmark are used for each location of additional python modules, * as in the case for ParaView built with external VTK. * landmark is the relative path to the libraryPath such as 'vtkmodules/__init__.py' * for VTK or 'paraview/__init__.py' for ParaView. */ static void AddUserPythonPath(const char* libraryPath, const char* landmark); /** * Register a callback to be called when Python exits. * * This may be used to register callbacks before Python is initialized. * * Returns `0` if registration is successful, `-1` on failure, and `1` if * registration is deferred (Python is not yet initialized). */ static int AddAtExitCallback(void (*func)()); protected: vtkPythonInterpreter(); ~vtkPythonInterpreter() override; friend struct vtkPythonStdStreamCaptureHelper; ///@{ /** * Internal methods used by Python. Don't call directly. */ static void WriteStdOut(const char* txt); static void FlushStdOut(); static void WriteStdErr(const char* txt); static void FlushStdErr(); static vtkStdString ReadStdin(); ///@} private: vtkPythonInterpreter(const vtkPythonInterpreter&) = delete; void operator=(const vtkPythonInterpreter&) = delete; static bool InitializedOnce; static bool CaptureStdin; static bool RedirectOutput; /** * If true, buffer output to console and sent it to other modules at * the end of the operation. If false, send the output as it becomes available. */ static bool ConsoleBuffering; ///@{ /** * Accumulate here output printed to console by the python interpreter. */ static std::string StdErrBuffer; static std::string StdOutBuffer; ///@} /** * Verbosity level to use when logging info. */ static int LogVerbosity; ///@{ /** * Container of pairs used for setting up user python paths for applications using VTK (such as * ParaView). The first element of pair is LibraryPath and second element of pair is Landmark. * Landmark is relative path to the LibraryPath such as 'vtkmodules/__init__.py' for VTK or * 'paraview/__init__.py' for ParaView. */ static std::vector> UserPythonPaths; /** * Container of callbacks to register. */ static std::vector AtExitCallbacks; }; // For tracking global interpreters class VTKPYTHONINTERPRETER_EXPORT vtkPythonGlobalInterpreters { public: vtkPythonGlobalInterpreters(); ~vtkPythonGlobalInterpreters(); private: vtkPythonGlobalInterpreters(const vtkPythonGlobalInterpreters&) = delete; vtkPythonGlobalInterpreters& operator=(const vtkPythonGlobalInterpreters&) = delete; }; // This is here to implement the Schwarz counter idiom. static vtkPythonGlobalInterpreters vtkPythonInterpreters; #if defined(_WIN32) class VTKPYTHONINTERPRETER_EXPORT vtkWideArgsConverter { public: vtkWideArgsConverter(int argc, wchar_t* wargv[]); ~vtkWideArgsConverter(); char** GetArgs() { return &this->Args[0]; } int GetArgCount() { return this->Argc; } private: int Argc; std::vector Args; std::vector MemCache; vtkWideArgsConverter(const vtkWideArgsConverter&) = delete; vtkWideArgsConverter& operator=(const vtkWideArgsConverter&) = delete; }; #endif VTK_ABI_NAMESPACE_END #endif