"Professional CMake" book suggest the following: "Targets should build successfully with or without compiler support for precompiled headers. It should be considered an optimization, not a requirement. In particular, do not explicitly include a precompile header (e.g. stdafx.h) in the source code, let CMake force-include an automatically generated precompile header on the compiler command line instead. This is more portable across the major compilers and is likely to be easier to maintain. It will also avoid warnings being generated from certain code checking tools like iwyu (include what you use)." Therefore, removed the "#include <PreCompiled.h>" from sources, also there is no need for the "#ifdef _PreComp_" anymore
1009 lines
31 KiB
C++
1009 lines
31 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
|
|
* *
|
|
* This file is part of the FreeCAD CAx development system. *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU Library General Public License (LGPL) *
|
|
* as published by the Free Software Foundation; either version 2 of *
|
|
* the License, or (at your option) any later version. *
|
|
* for detail see the LICENCE text file. *
|
|
* *
|
|
* FreeCAD is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU Library General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Library General Public *
|
|
* License along with FreeCAD; if not, write to the Free Software *
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
* USA *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include <sstream>
|
|
#include <boost/regex.hpp>
|
|
|
|
#include <FCConfig.h>
|
|
|
|
#include "Interpreter.h"
|
|
#include "Console.h"
|
|
#include "ExceptionFactory.h"
|
|
#include "FileInfo.h"
|
|
#include "PyObjectBase.h"
|
|
#include "PyTools.h"
|
|
#include "Stream.h"
|
|
|
|
|
|
char format2[1024]; // Warning! Can't go over 512 characters!!!
|
|
unsigned int format2_len = 1024;
|
|
|
|
using namespace Base;
|
|
|
|
PyException::PyException(const Py::Object& obj)
|
|
{
|
|
setMessage(obj.as_string());
|
|
// WARNING: we are assuming that python type object will never be
|
|
// destroyed, so we don't keep reference here to save book-keeping in
|
|
// our copy constructor and destructor
|
|
// NOLINTBEGIN
|
|
_exceptionType = reinterpret_cast<PyObject*>(obj.ptr()->ob_type);
|
|
_errorType = obj.ptr()->ob_type->tp_name;
|
|
// NOLINTEND
|
|
}
|
|
|
|
PyException::PyException()
|
|
{
|
|
PP_Fetch_Error_Text(); /* fetch (and clear) exception */
|
|
|
|
setPyObject(PP_PyDict_Object);
|
|
|
|
std::string prefix = PP_last_error_type; /* exception name text */
|
|
std::string error = PP_last_error_info; /* exception data text */
|
|
|
|
setMessage(error);
|
|
_errorType = prefix;
|
|
|
|
// NOLINTNEXTLINE
|
|
_exceptionType = PP_last_exception_type;
|
|
|
|
if (PP_last_exception_type) {
|
|
// WARNING: we are assuming that python type object will never be
|
|
// destroyed, so we don't keep reference here to save book-keeping in
|
|
// our copy constructor and destructor
|
|
Py_DECREF(PP_last_exception_type);
|
|
PP_last_exception_type = nullptr;
|
|
}
|
|
|
|
_stackTrace = PP_last_error_trace; /* exception traceback text */
|
|
|
|
// This should be done in the constructor because when doing
|
|
// in the destructor it's not always clear when it is called
|
|
// and thus may clear a Python exception when it should not.
|
|
PyGILStateLocker locker;
|
|
PyErr_Clear(); // must be called to keep Python interpreter in a valid state (Werner)
|
|
}
|
|
|
|
PyException::~PyException() noexcept = default;
|
|
|
|
void PyException::throwException()
|
|
{
|
|
PyException myexcp;
|
|
myexcp.reportException();
|
|
myexcp.raiseException();
|
|
}
|
|
|
|
void PyException::raiseException()
|
|
{
|
|
PyGILStateLocker locker;
|
|
if (PP_PyDict_Object) {
|
|
// delete the Python dict upon destruction of edict
|
|
Py::Dict edict(PP_PyDict_Object, true);
|
|
PP_PyDict_Object = nullptr;
|
|
|
|
std::string exceptionname;
|
|
if (_exceptionType == Base::PyExc_FC_FreeCADAbort) {
|
|
edict.setItem("sclassname", Py::String(typeid(AbortException).name()));
|
|
}
|
|
if (getReported()) {
|
|
edict.setItem("breported", Py::True());
|
|
}
|
|
Base::ExceptionFactory::Instance().raiseException(edict.ptr());
|
|
}
|
|
|
|
PyExceptionData data {_exceptionType, getMessage(), getReported()};
|
|
Base::ExceptionFactory::Instance().raiseExceptionByType(data);
|
|
|
|
// Fallback
|
|
throw *this;
|
|
}
|
|
|
|
void PyException::reportException() const
|
|
{
|
|
if (!getReported()) {
|
|
setReported(true);
|
|
// set sys.last_vars to make post-mortem debugging work
|
|
PyGILStateLocker locker;
|
|
PySys_SetObject("last_traceback", PP_last_traceback);
|
|
Console().developerError("pyException",
|
|
"%s%s: %s\n",
|
|
_stackTrace.c_str(),
|
|
_errorType.c_str(),
|
|
what());
|
|
}
|
|
}
|
|
|
|
void PyException::setPyException() const
|
|
{
|
|
std::stringstream str;
|
|
str << getStackTrace() << getErrorType() << ": " << what();
|
|
PyErr_SetString(getPyExceptionType(), str.str().c_str());
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
|
|
SystemExitException::SystemExitException()
|
|
{
|
|
// Set exception message and code based upon the python sys.exit() code and/or message
|
|
// based upon the following sys.exit() call semantics.
|
|
//
|
|
// Invocation | _exitCode | _sErrMsg
|
|
// ---------------- + --------- + --------
|
|
// sys.exit(int#) | int# | "System Exit"
|
|
// sys.exit(string) | 1 | string
|
|
// sys.exit() | 1 | "System Exit"
|
|
|
|
long int errCode = 1;
|
|
std::string errMsg = "System exit";
|
|
PyObject* type {};
|
|
PyObject* value {};
|
|
PyObject* traceback {};
|
|
PyObject* code {};
|
|
|
|
PyGILStateLocker locker;
|
|
PyErr_Fetch(&type, &value, &traceback);
|
|
PyErr_NormalizeException(&type, &value, &traceback);
|
|
|
|
if (value) {
|
|
code = PyObject_GetAttrString(value, "code");
|
|
if (code && value != Py_None) {
|
|
Py_DECREF(value);
|
|
value = code;
|
|
}
|
|
|
|
if (PyLong_Check(value)) {
|
|
errCode = PyLong_AsLong(value);
|
|
}
|
|
else {
|
|
const char* str = PyUnicode_AsUTF8(value);
|
|
if (str) {
|
|
errMsg = errMsg + ": " + str;
|
|
}
|
|
}
|
|
}
|
|
|
|
setMessage(errMsg);
|
|
_exitCode = errCode;
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
|
|
// Fixes #0000831: python print causes File descriptor error on windows
|
|
// NOLINTNEXTLINE
|
|
class PythonStdOutput: public Py::PythonExtension<PythonStdOutput>
|
|
{
|
|
public:
|
|
static void init_type()
|
|
{
|
|
behaviors().name("PythonStdOutput");
|
|
behaviors().doc("Python standard output");
|
|
add_varargs_method("write", &PythonStdOutput::write, "write()");
|
|
add_varargs_method("flush", &PythonStdOutput::flush, "flush()");
|
|
}
|
|
|
|
PythonStdOutput() = default;
|
|
~PythonStdOutput() override = default;
|
|
|
|
Py::Object write(const Py::Tuple&)
|
|
{
|
|
return Py::None();
|
|
}
|
|
Py::Object flush(const Py::Tuple&)
|
|
{
|
|
return Py::None();
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------
|
|
|
|
InterpreterSingleton::InterpreterSingleton()
|
|
{
|
|
this->_global = nullptr;
|
|
}
|
|
|
|
InterpreterSingleton::~InterpreterSingleton() = default;
|
|
|
|
|
|
std::string InterpreterSingleton::runString(const char* sCmd)
|
|
{
|
|
PyObject* module {};
|
|
PyObject* dict {};
|
|
PyObject* presult {};
|
|
|
|
PyGILStateLocker locker;
|
|
module = PP_Load_Module("__main__"); /* get module, init python */
|
|
if (!module) {
|
|
throw PyException(); /* not incref'd */
|
|
}
|
|
dict = PyModule_GetDict(module); /* get dict namespace */
|
|
if (!dict) {
|
|
throw PyException(); /* not incref'd */
|
|
}
|
|
|
|
|
|
presult = PyRun_String(sCmd, Py_file_input, dict, dict); /* eval direct */
|
|
if (!presult) {
|
|
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
|
|
throw SystemExitException();
|
|
}
|
|
|
|
PyException::throwException();
|
|
return {}; // just to quieten code analyzers
|
|
}
|
|
|
|
PyObject* repr = PyObject_Repr(presult);
|
|
Py_DECREF(presult);
|
|
if (repr) {
|
|
std::string ret(PyUnicode_AsUTF8(repr));
|
|
Py_DECREF(repr);
|
|
return ret;
|
|
}
|
|
|
|
PyErr_Clear();
|
|
return {};
|
|
}
|
|
|
|
/** runStringWithKey(psCmd, key, key_initial_value)
|
|
* psCmd is python script to run
|
|
* key is the name of a python string variable the script will have read/write
|
|
* access to during script execution. It will be our return value.
|
|
* key_initial_value is the initial value c++ will set before calling the script
|
|
* If the script runs successfully it will be able to change the value of key as
|
|
* the return value, but if there is a runtime error key will not be changed even
|
|
* if the error occurs after changing it inside the script.
|
|
*/
|
|
|
|
std::string InterpreterSingleton::runStringWithKey(const char* psCmd,
|
|
const char* key,
|
|
const char* key_initial_value)
|
|
{
|
|
PyGILStateLocker locker;
|
|
Py::Module module("__main__");
|
|
Py::Dict globalDictionary = module.getDict();
|
|
Py::Dict localDictionary;
|
|
Py::String initial_value(key_initial_value);
|
|
localDictionary.setItem(key, initial_value);
|
|
|
|
PyObject* presult =
|
|
PyRun_String(psCmd, Py_file_input, globalDictionary.ptr(), localDictionary.ptr());
|
|
if (!presult) {
|
|
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
|
|
throw SystemExitException();
|
|
}
|
|
|
|
PyException::throwException();
|
|
return {}; // just to quieten code analyzers
|
|
}
|
|
Py_DECREF(presult);
|
|
|
|
Py::Object key_return_value = localDictionary.getItem(key);
|
|
if (!key_return_value.isString()) {
|
|
key_return_value = key_return_value.str(); // NOLINT
|
|
}
|
|
|
|
Py::Bytes str = Py::String(key_return_value).encode("utf-8");
|
|
std::string result = static_cast<std::string>(str);
|
|
return result;
|
|
}
|
|
|
|
Py::Object InterpreterSingleton::runStringObject(const char* sCmd)
|
|
{
|
|
PyObject* module {};
|
|
PyObject* dict {};
|
|
PyObject* presult {};
|
|
|
|
PyGILStateLocker locker;
|
|
module = PP_Load_Module("__main__"); /* get module, init python */
|
|
if (!module) {
|
|
throw PyException(); /* not incref'd */
|
|
}
|
|
dict = PyModule_GetDict(module); /* get dict namespace */
|
|
if (!dict) {
|
|
throw PyException(); /* not incref'd */
|
|
}
|
|
|
|
|
|
presult = PyRun_String(sCmd, Py_eval_input, dict, dict); /* eval direct */
|
|
if (!presult) {
|
|
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
|
|
throw SystemExitException();
|
|
}
|
|
|
|
throw PyException();
|
|
}
|
|
|
|
return Py::asObject(presult);
|
|
}
|
|
|
|
void InterpreterSingleton::systemExit()
|
|
{
|
|
/* This code is taken from the original Python code */
|
|
PyObject* exception {};
|
|
PyObject* value {};
|
|
PyObject* tb {};
|
|
int exitcode = 0;
|
|
|
|
PyErr_Fetch(&exception, &value, &tb);
|
|
fflush(stdout);
|
|
if (!value || value == Py_None) {
|
|
goto done; // NOLINT
|
|
}
|
|
if (PyExceptionInstance_Check(value)) {
|
|
/* The error code should be in the `code' attribute. */
|
|
PyObject* code = PyObject_GetAttrString(value, "code");
|
|
if (code) {
|
|
Py_DECREF(value);
|
|
value = code;
|
|
if (value == Py_None) {
|
|
goto done; // NOLINT
|
|
}
|
|
}
|
|
/* If we failed to dig out the 'code' attribute,
|
|
just let the else clause below print the error. */
|
|
}
|
|
if (PyLong_Check(value)) {
|
|
exitcode = (int)PyLong_AsLong(value);
|
|
}
|
|
else {
|
|
PyObject_Print(value, stderr, Py_PRINT_RAW);
|
|
PySys_WriteStderr("\n");
|
|
exitcode = 1;
|
|
}
|
|
done:
|
|
/* Restore and clear the exception info, in order to properly decref
|
|
* the exception, value, and traceback. If we just exit instead,
|
|
* these leak, which confuses PYTHONDUMPREFS output, and may prevent
|
|
* some finalizers from running.
|
|
*/
|
|
PyErr_Restore(exception, value, tb);
|
|
PyErr_Clear();
|
|
Py_Exit(exitcode);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
void InterpreterSingleton::runInteractiveString(const char* sCmd)
|
|
{
|
|
PyObject* module {};
|
|
PyObject* dict {};
|
|
PyObject* presult {};
|
|
|
|
PyGILStateLocker locker;
|
|
module = PP_Load_Module("__main__"); /* get module, init python */
|
|
if (!module) {
|
|
throw PyException(); /* not incref'd */
|
|
}
|
|
dict = PyModule_GetDict(module); /* get dict namespace */
|
|
if (!dict) {
|
|
throw PyException(); /* not incref'd */
|
|
}
|
|
|
|
presult = PyRun_String(sCmd, Py_single_input, dict, dict); /* eval direct */
|
|
if (!presult) {
|
|
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
|
|
throw SystemExitException();
|
|
}
|
|
/* get latest python exception information */
|
|
/* and print the error to the error output */
|
|
PyObject* errobj {};
|
|
PyObject* errdata {};
|
|
PyObject* errtraceback {};
|
|
PyErr_Fetch(&errobj, &errdata, &errtraceback);
|
|
|
|
RuntimeError exc(""); // do not use PyException since this clears the error indicator
|
|
if (errdata) {
|
|
if (PyUnicode_Check(errdata)) {
|
|
exc.setMessage(PyUnicode_AsUTF8(errdata));
|
|
}
|
|
}
|
|
PyErr_Restore(errobj, errdata, errtraceback);
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Print();
|
|
}
|
|
throw exc;
|
|
}
|
|
|
|
Py_DECREF(presult);
|
|
}
|
|
|
|
void InterpreterSingleton::runFile(const char* pxFileName, bool local)
|
|
{
|
|
#ifdef FC_OS_WIN32
|
|
FileInfo fi(pxFileName);
|
|
FILE* fp = _wfopen(fi.toStdWString().c_str(), L"r");
|
|
#else
|
|
FILE* fp = fopen(pxFileName, "r");
|
|
#endif
|
|
if (!fp) {
|
|
throw FileException("Unknown file", pxFileName);
|
|
}
|
|
|
|
PyGILStateLocker locker;
|
|
PyObject* module {};
|
|
PyObject* dict {};
|
|
module = PyImport_AddModule("__main__");
|
|
dict = PyModule_GetDict(module);
|
|
if (local) {
|
|
dict = PyDict_Copy(dict);
|
|
}
|
|
else {
|
|
Py_INCREF(dict); // avoid to further distinguish between local and global dict
|
|
}
|
|
|
|
if (!PyDict_GetItemString(dict, "__file__")) {
|
|
PyObject* pyObj = PyUnicode_FromString(pxFileName);
|
|
if (!pyObj) {
|
|
fclose(fp);
|
|
Py_DECREF(dict);
|
|
return;
|
|
}
|
|
if (PyDict_SetItemString(dict, "__file__", pyObj) < 0) {
|
|
Py_DECREF(pyObj);
|
|
fclose(fp);
|
|
Py_DECREF(dict);
|
|
return;
|
|
}
|
|
Py_DECREF(pyObj);
|
|
}
|
|
|
|
PyObject* result = PyRun_File(fp, pxFileName, Py_file_input, dict, dict);
|
|
fclose(fp);
|
|
Py_DECREF(dict);
|
|
|
|
if (!result) {
|
|
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
|
|
throw SystemExitException();
|
|
}
|
|
throw PyException();
|
|
}
|
|
Py_DECREF(result);
|
|
}
|
|
|
|
bool InterpreterSingleton::loadModule(const char* psModName)
|
|
{
|
|
PyObject* module {};
|
|
|
|
PyGILStateLocker locker;
|
|
module = PP_Load_Module(psModName);
|
|
|
|
if (!module) {
|
|
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
|
|
throw SystemExitException();
|
|
}
|
|
|
|
throw PyException();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
PyObject* InterpreterSingleton::addModule(Py::ExtensionModuleBase* mod)
|
|
{
|
|
_modules.push_back(mod);
|
|
return mod->module().ptr();
|
|
}
|
|
|
|
void InterpreterSingleton::cleanupModules()
|
|
{
|
|
// This is only needed to make the address sanitizer happy
|
|
#if defined(__has_feature)
|
|
#if __has_feature(address_sanitizer)
|
|
for (auto it : _modules) {
|
|
delete it;
|
|
}
|
|
_modules.clear();
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void InterpreterSingleton::addType(PyTypeObject* Type, PyObject* Module, const char* Name)
|
|
{
|
|
// NOTE: To finish the initialization of our own type objects we must
|
|
// call PyType_Ready, otherwise we run into a segmentation fault, later on.
|
|
// This function is responsible for adding inherited slots from a type's base class.
|
|
if (PyType_Ready(Type) < 0) {
|
|
return;
|
|
}
|
|
PyModule_AddObject(Module, Name, Base::getTypeAsObject(Type));
|
|
}
|
|
|
|
void InterpreterSingleton::addPythonPath(const char* Path)
|
|
{
|
|
PyGILStateLocker locker;
|
|
Py::List list(PySys_GetObject("path"));
|
|
list.append(Py::String(Path));
|
|
}
|
|
|
|
std::string InterpreterSingleton::getPythonPath()
|
|
{
|
|
// Construct something that looks like the output of the now-deprecated Py_GetPath
|
|
PyGILStateLocker lock;
|
|
PyObject* path = PySys_GetObject("path");
|
|
std::string result;
|
|
const char* separator = ":"; // Use ":" on Unix-like systems, ";" on Windows
|
|
#ifdef FC_OS_WIN32
|
|
separator = ";";
|
|
#endif
|
|
Py_ssize_t length = PyList_Size(path);
|
|
for (Py_ssize_t i = 0; i < length; ++i) {
|
|
PyObject* item = PyList_GetItem(path, i); // Borrowed reference
|
|
if (!item) {
|
|
throw Base::RuntimeError("Failed to retrieve item from path");
|
|
}
|
|
const char* item_str = PyUnicode_AsUTF8(item);
|
|
if (!item_str) {
|
|
throw Base::RuntimeError("Failed to convert path item to UTF-8 string");
|
|
}
|
|
if (!result.empty()) {
|
|
result += separator;
|
|
}
|
|
result += item_str;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#if PY_VERSION_HEX < 0x030b0000
|
|
std::string InterpreterSingleton::init(int argc, char* argv[])
|
|
{
|
|
if (!Py_IsInitialized()) {
|
|
Py_SetProgramName(Py_DecodeLocale(argv[0], nullptr));
|
|
// There is a serious bug in VS from 2010 until 2013 where the file descriptor for stdin,
|
|
// stdout or stderr returns a valid value for GUI applications (i.e. subsystem = Windows)
|
|
// where it shouldn't. This causes Python to fail during initialization. A workaround is to
|
|
// use freopen on stdin, stdout and stderr. See the class Redirection inside main()
|
|
// https://bugs.python.org/issue17797#msg197474
|
|
//
|
|
Py_Initialize();
|
|
const char* virtualenv = getenv("VIRTUAL_ENV");
|
|
if (virtualenv) {
|
|
PyRun_SimpleString(
|
|
"# Check for virtualenv, and activate if present.\n"
|
|
"# See "
|
|
"https://virtualenv.pypa.io/en/latest/"
|
|
"#using-virtualenv-without-bin-python\n"
|
|
"import os\n"
|
|
"import sys\n"
|
|
"base_path = os.getenv(\"VIRTUAL_ENV\")\n"
|
|
"if not base_path is None:\n"
|
|
" activate_this = os.path.join(base_path, \"bin\", \"activate_this.py\")\n"
|
|
" exec(open(activate_this).read(), {'__file__':activate_this})\n");
|
|
}
|
|
|
|
size_t size = argc;
|
|
static std::vector<wchar_t*> _argv(size);
|
|
for (int i = 0; i < argc; i++) {
|
|
_argv[i] = Py_DecodeLocale(argv[i], nullptr);
|
|
}
|
|
PySys_SetArgv(argc, _argv.data());
|
|
PythonStdOutput::init_type();
|
|
this->_global = PyEval_SaveThread();
|
|
}
|
|
|
|
PyGILStateLocker lock;
|
|
return Py_EncodeLocale(Py_GetPath(), nullptr);
|
|
}
|
|
#else
|
|
namespace
|
|
{
|
|
void initInterpreter(int argc, char* argv[])
|
|
{
|
|
PyStatus status;
|
|
PyConfig config;
|
|
PyConfig_InitIsolatedConfig(&config);
|
|
config.isolated = 0;
|
|
config.user_site_directory = 1;
|
|
|
|
status = PyConfig_SetBytesArgv(&config, argc, argv);
|
|
if (PyStatus_Exception(status)) {
|
|
throw Base::RuntimeError("Failed to set config");
|
|
}
|
|
|
|
status = Py_InitializeFromConfig(&config);
|
|
if (PyStatus_Exception(status)) {
|
|
throw Base::RuntimeError("Failed to init from config");
|
|
}
|
|
|
|
// If FreeCAD was run from within a Python virtual environment, ensure that the site-packages
|
|
// directory from that environment is used.
|
|
const char* virtualenv = getenv("VIRTUAL_ENV");
|
|
if (virtualenv) {
|
|
std::wstringstream ss;
|
|
PyConfig_Read(&config);
|
|
ss << virtualenv << L"/lib/python" << PY_MAJOR_VERSION << "." << PY_MINOR_VERSION
|
|
<< "/site-packages";
|
|
PyObject* venvLocation = PyUnicode_FromWideChar(ss.str().c_str(), ss.str().size());
|
|
PyObject* path = PySys_GetObject("path");
|
|
PyList_Append(path, venvLocation);
|
|
}
|
|
|
|
PyConfig_Clear(&config);
|
|
|
|
Py_Initialize();
|
|
}
|
|
} // namespace
|
|
std::string InterpreterSingleton::init(int argc, char* argv[])
|
|
{
|
|
try {
|
|
if (!Py_IsInitialized()) {
|
|
initInterpreter(argc, argv);
|
|
|
|
PythonStdOutput::init_type();
|
|
this->_global = PyEval_SaveThread();
|
|
}
|
|
return getPythonPath();
|
|
}
|
|
catch (const Exception& e) {
|
|
e.reportException();
|
|
throw;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void InterpreterSingleton::replaceStdOutput()
|
|
{
|
|
PyGILStateLocker locker;
|
|
PythonStdOutput* out = new PythonStdOutput();
|
|
PySys_SetObject("stdout", out);
|
|
PySys_SetObject("stderr", out);
|
|
}
|
|
|
|
int InterpreterSingleton::cleanup(void (*func)())
|
|
{
|
|
return Py_AtExit(func);
|
|
}
|
|
|
|
void InterpreterSingleton::finalize()
|
|
{
|
|
try {
|
|
PyEval_RestoreThread(this->_global);
|
|
cleanupModules();
|
|
Py_Finalize();
|
|
}
|
|
catch (...) {
|
|
}
|
|
}
|
|
|
|
void InterpreterSingleton::runStringArg(const char* psCom, ...)
|
|
{
|
|
// va stuff
|
|
va_list namelessVars;
|
|
va_start(namelessVars, psCom); // Get the "..." vars
|
|
int len = vsnprintf(format2, format2_len, psCom, namelessVars);
|
|
va_end(namelessVars);
|
|
if (len == -1) {
|
|
// argument too long
|
|
assert(false);
|
|
}
|
|
|
|
runString(format2);
|
|
}
|
|
|
|
|
|
// Singleton:
|
|
|
|
InterpreterSingleton* InterpreterSingleton::_pcSingleton = nullptr;
|
|
|
|
InterpreterSingleton& InterpreterSingleton::Instance()
|
|
{
|
|
// not initialized!
|
|
if (!_pcSingleton) {
|
|
_pcSingleton = new InterpreterSingleton();
|
|
}
|
|
return *_pcSingleton;
|
|
}
|
|
|
|
void InterpreterSingleton::Destruct()
|
|
{
|
|
// not initialized or double destruct!
|
|
assert(_pcSingleton);
|
|
delete _pcSingleton;
|
|
_pcSingleton = nullptr;
|
|
}
|
|
|
|
int InterpreterSingleton::runCommandLine(const char* prompt)
|
|
{
|
|
PyGILStateLocker locker;
|
|
return PP_Run_Command_Line(prompt);
|
|
}
|
|
|
|
/**
|
|
* Runs a member method of an object with no parameter and no return value
|
|
* void (void). There are other methods to run with returns
|
|
*/
|
|
void InterpreterSingleton::runMethodVoid(PyObject* pobject, const char* method)
|
|
{
|
|
PyGILStateLocker locker;
|
|
if (PP_Run_Method(pobject, // object
|
|
method, // run method
|
|
nullptr, // no return type
|
|
nullptr, // so no return object
|
|
"()") // no arguments
|
|
!= 0) {
|
|
throw PyException(/*"Error running InterpreterSingleton::RunMethodVoid()"*/);
|
|
}
|
|
}
|
|
|
|
PyObject* InterpreterSingleton::runMethodObject(PyObject* pobject, const char* method)
|
|
{
|
|
PyObject* pcO {};
|
|
|
|
PyGILStateLocker locker;
|
|
if (PP_Run_Method(pobject, // object
|
|
method, // run method
|
|
"O", // return type
|
|
&pcO, // return object
|
|
"()") // no arguments
|
|
!= 0) {
|
|
throw PyException();
|
|
}
|
|
|
|
return pcO;
|
|
}
|
|
|
|
void InterpreterSingleton::runMethod(PyObject* pobject,
|
|
const char* method,
|
|
const char* resfmt,
|
|
void* cresult, /* convert to c/c++ */
|
|
const char* argfmt,
|
|
...) /* convert to python */
|
|
{
|
|
PyObject* pmeth {};
|
|
PyObject* pargs {};
|
|
PyObject* presult {};
|
|
va_list argslist; /* "pobject.method(args)" */
|
|
va_start(argslist, argfmt);
|
|
|
|
PyGILStateLocker locker;
|
|
pmeth = PyObject_GetAttrString(pobject, method);
|
|
if (!pmeth) { /* get callable object */
|
|
va_end(argslist);
|
|
throw AttributeError(
|
|
"Error running InterpreterSingleton::RunMethod() method not defined"); /* bound method?
|
|
has self */
|
|
}
|
|
|
|
pargs = Py_VaBuildValue(argfmt, argslist); /* args: c->python */
|
|
va_end(argslist);
|
|
|
|
if (!pargs) {
|
|
Py_DECREF(pmeth);
|
|
throw TypeError("InterpreterSingleton::RunMethod() wrong arguments");
|
|
}
|
|
|
|
presult = PyObject_CallObject(pmeth, pargs); /* run interpreter */
|
|
|
|
Py_DECREF(pmeth);
|
|
Py_DECREF(pargs);
|
|
if (PP_Convert_Result(presult, resfmt, cresult) != 0) {
|
|
if (PyErr_Occurred()) {
|
|
PyErr_Print();
|
|
}
|
|
throw RuntimeError(
|
|
"Error running InterpreterSingleton::RunMethod() exception in called method");
|
|
}
|
|
}
|
|
|
|
PyObject* InterpreterSingleton::getValue(const char* key, const char* result_var)
|
|
{
|
|
PyObject* module {};
|
|
PyObject* dict {};
|
|
PyObject* presult {};
|
|
|
|
PyGILStateLocker locker;
|
|
module = PP_Load_Module("__main__"); /* get module, init python */
|
|
if (!module) {
|
|
throw PyException(); /* not incref'd */
|
|
}
|
|
dict = PyModule_GetDict(module); /* get dict namespace */
|
|
if (!dict) {
|
|
throw PyException(); /* not incref'd */
|
|
}
|
|
|
|
|
|
presult = PyRun_String(key, Py_file_input, dict, dict); /* eval direct */
|
|
if (!presult) {
|
|
throw PyException();
|
|
}
|
|
Py_DECREF(presult);
|
|
|
|
return PyObject_GetAttrString(module, result_var);
|
|
}
|
|
|
|
void InterpreterSingleton::dbgObserveFile(const char* sFileName)
|
|
{
|
|
if (sFileName) {
|
|
_cDebugFileName = sFileName;
|
|
}
|
|
else {
|
|
_cDebugFileName = "";
|
|
}
|
|
}
|
|
|
|
|
|
std::string InterpreterSingleton::strToPython(const char* Str)
|
|
{
|
|
std::string result;
|
|
const char* It = Str;
|
|
|
|
while (*It != '\0') {
|
|
switch (*It) {
|
|
case '\\':
|
|
result += "\\\\";
|
|
break;
|
|
case '\"':
|
|
result += "\\\"";
|
|
break;
|
|
case '\'':
|
|
result += "\\\'";
|
|
break;
|
|
default:
|
|
result += *It;
|
|
}
|
|
It++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
int getSWIGVersionFromModule(const std::string& module)
|
|
{
|
|
static std::map<std::string, int> moduleMap;
|
|
std::map<std::string, int>::iterator it = moduleMap.find(module);
|
|
if (it != moduleMap.end()) {
|
|
return it->second;
|
|
}
|
|
try {
|
|
// Get the module and check its __file__ attribute
|
|
Py::Dict dict(PyImport_GetModuleDict());
|
|
if (!dict.hasKey(module)) {
|
|
return 0;
|
|
}
|
|
Py::Module mod(module);
|
|
Py::String file(mod.getAttr("__file__"));
|
|
std::string filename = (std::string)file;
|
|
// file can have the extension .py or .pyc
|
|
filename = filename.substr(0, filename.rfind('.'));
|
|
filename += ".py";
|
|
boost::regex rx("^# Version ([1-9])\\.([0-9])\\.([0-9]+)");
|
|
boost::cmatch what;
|
|
|
|
std::string line;
|
|
Base::FileInfo fi(filename);
|
|
|
|
Base::ifstream str(fi, std::ios::in);
|
|
while (str && std::getline(str, line)) {
|
|
if (boost::regex_match(line.c_str(), what, rx)) {
|
|
int major = std::atoi(what[1].first);
|
|
int minor = std::atoi(what[2].first);
|
|
int micro = std::atoi(what[3].first);
|
|
int version = (major << 16) + (minor << 8) + micro;
|
|
moduleMap[module] = version;
|
|
return version;
|
|
}
|
|
}
|
|
}
|
|
catch (Py::Exception& e) {
|
|
e.clear();
|
|
}
|
|
|
|
#if (defined(HAVE_SWIG) && (HAVE_SWIG == 1))
|
|
moduleMap[module] = 0;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#if (defined(HAVE_SWIG) && (HAVE_SWIG == 1))
|
|
namespace Swig_python
|
|
{
|
|
extern int createSWIGPointerObj_T(const char* TypeName, void* obj, PyObject** ptr, int own);
|
|
extern int convertSWIGPointerObj_T(const char* TypeName, PyObject* obj, void** ptr, int flags);
|
|
extern void cleanupSWIG_T(const char* TypeName);
|
|
extern int getSWIGPointerTypeObj_T(const char* TypeName, PyTypeObject** ptr);
|
|
} // namespace Swig_python
|
|
#endif
|
|
|
|
PyObject* InterpreterSingleton::createSWIGPointerObj(const char* Module,
|
|
const char* TypeName,
|
|
void* Pointer,
|
|
int own)
|
|
{
|
|
int result = 0;
|
|
PyObject* proxy = nullptr;
|
|
PyGILStateLocker locker;
|
|
(void)Module;
|
|
#if (defined(HAVE_SWIG) && (HAVE_SWIG == 1))
|
|
result = Swig_python::createSWIGPointerObj_T(TypeName, Pointer, &proxy, own);
|
|
#else
|
|
(void)TypeName;
|
|
(void)Pointer;
|
|
(void)own;
|
|
result = -1; // indicates error
|
|
#endif
|
|
|
|
if (result == 0) {
|
|
return proxy;
|
|
}
|
|
|
|
// none of the SWIG's succeeded
|
|
throw Base::RuntimeError("No SWIG wrapped library loaded");
|
|
}
|
|
|
|
bool InterpreterSingleton::convertSWIGPointerObj(const char* Module,
|
|
const char* TypeName,
|
|
PyObject* obj,
|
|
void** ptr,
|
|
int flags)
|
|
{
|
|
int result = 0;
|
|
PyGILStateLocker locker;
|
|
(void)Module;
|
|
#if (defined(HAVE_SWIG) && (HAVE_SWIG == 1))
|
|
result = Swig_python::convertSWIGPointerObj_T(TypeName, obj, ptr, flags);
|
|
#else
|
|
(void)TypeName;
|
|
(void)obj;
|
|
(void)ptr;
|
|
(void)flags;
|
|
result = -1; // indicates error
|
|
#endif
|
|
|
|
if (result == 0) {
|
|
return true;
|
|
}
|
|
|
|
// none of the SWIG's succeeded
|
|
throw Base::RuntimeError("No SWIG wrapped library loaded");
|
|
}
|
|
|
|
void InterpreterSingleton::cleanupSWIG(const char* TypeName)
|
|
{
|
|
PyGILStateLocker locker;
|
|
#if (defined(HAVE_SWIG) && (HAVE_SWIG == 1))
|
|
Swig_python::cleanupSWIG_T(TypeName);
|
|
#else
|
|
(void)TypeName;
|
|
#endif
|
|
}
|
|
|
|
PyTypeObject* InterpreterSingleton::getSWIGPointerTypeObj(const char* Module, const char* TypeName)
|
|
{
|
|
int result = 0;
|
|
PyTypeObject* proxy = nullptr;
|
|
PyGILStateLocker locker;
|
|
(void)Module;
|
|
#if (defined(HAVE_SWIG) && (HAVE_SWIG == 1))
|
|
result = Swig_python::getSWIGPointerTypeObj_T(TypeName, &proxy);
|
|
#else
|
|
(void)TypeName;
|
|
result = -1; // indicates error
|
|
#endif
|
|
|
|
if (result == 0) {
|
|
return proxy;
|
|
}
|
|
|
|
// none of the SWIG's succeeded
|
|
throw Base::RuntimeError("No SWIG wrapped library loaded");
|
|
}
|