Files
create/src/Base/Interpreter.h
wmayer f539138dd9 fix readability-*:
* readability-const-return-type
* readability-container-data-pointer
* readability-container-size-empty
* readability-delete-null-pointer
* readability-else-after-return
* readability-inconsistent-declaration-parameter-name
* readability-redundant-member-init
* readability-redundant-smartptr-get
* readability-redundant-string-cstr
* readability-use-anyofallof
* readability-static-definition-in-anonymous-namespace
* readability-static-accessed-through-instance
* readability-simplify-boolean-expr
2023-11-16 01:22:08 +01:00

368 lines
13 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 *
* *
***************************************************************************/
#ifndef BASE_INTERPRETER_H
#define BASE_INTERPRETER_H
#if defined(_POSIX_C_SOURCE)
#undef _POSIX_C_SOURCE
#endif // (re-)defined in pyconfig.h
#if defined(_XOPEN_SOURCE)
#undef _XOPEN_SOURCE
#endif // (re-)defined in pyconfig.h
#ifdef FC_OS_MACOSX
#undef toupper
#undef tolower
#undef isupper
#undef islower
#undef isspace
#undef isalpha
#undef isalnum
#endif
#include <CXX/Extensions.hxx>
#include <list>
#include <string>
#include "Exception.h"
/** Helper macro to obtain callable from an object
*
* @param _pyobj: PyObject pointer
* @param _name: the callable string name
* @param _var: the callable variable to be assigned
*
* See FeaturePythonImp::init() for example usage
*/
#define FC_PY_GetCallable(_pyobj, _name, _var) \
do { \
_var = Py::Object(); \
if (PyObject_HasAttrString(_pyobj, _name)) { \
Py::Object _obj(PyObject_GetAttrString(_pyobj, _name), true); \
if (_obj.isCallable()) \
_var = _obj; \
} \
} while (0)
/** Helper macro to obtain attribute from an object
*
* @param _pyobj: PyObject pointer
* @param _name: the attribute string name
* @param _var: the attribute variable to be assigned
*
* See FeaturePythonImp::init() for example usage
*/
#define FC_PY_GetObject(_pyobj, _name, _var) \
do { \
_var = Py::Object(); \
if (PyObject_HasAttrString(_pyobj, _name)) \
_var = Py::asObject(PyObject_GetAttrString(_pyobj, _name)); \
} while (0)
namespace Base
{
using std::string;
using std::vector;
class BaseExport PyException: public Exception
{
public:
/// constructor does the whole job
PyException();
PyException(const Py::Object& obj);
~PyException() noexcept override;
void raiseException();
/// this method determines if the original exception
/// can be reconstructed or not, if yes throws the reconstructed version
/// if not, throws a generic PyException.
static void ThrowException();
/// this function returns the stack trace
const std::string& getStackTrace() const
{
return _stackTrace;
}
const std::string& getErrorType() const
{
return _errorType;
}
PyObject* getPyExceptionType() const override
{
return _exceptionType;
}
void ReportException() const override;
/// Sets the Python error indicator and an error message
void setPyException() const override;
protected:
std::string _stackTrace;
std::string _errorType;
PyObject* _exceptionType;
};
inline Py::Object pyCall(PyObject* callable, PyObject* args = nullptr)
{
PyObject* result = PyObject_CallObject(callable, args);
if (!result) {
throw Py::Exception();
}
return Py::asObject(result);
}
inline Py::Object pyCallWithKeywords(PyObject* callable, PyObject* args, PyObject* kwds = nullptr)
{
PyObject* result = PyObject_Call(callable, args, kwds);
if (!result) {
throw Py::Exception();
}
return Py::asObject(result);
}
/**
* The SystemExitException is thrown if the Python-internal PyExc_SystemExit exception
* was thrown.
* @author Werner Mayer
*/
class BaseExport SystemExitException: public Exception
{
public:
SystemExitException();
~SystemExitException() noexcept override = default;
long getExitCode() const
{
return _exitCode;
}
protected:
long _exitCode;
};
/** If the application starts we release immediately the global interpreter lock
* (GIL) once the Python interpreter is initialized, i.e. no thread -- including
* the main thread doesn't hold the GIL. Thus, every thread must instantiate an
* object of PyGILStateLocker if it needs to access protected areas in Python or
* areas where the lock is needed. It's best to create the instance on the stack,
* not on the heap.
*/
class BaseExport PyGILStateLocker
{
public:
PyGILStateLocker()
{
gstate = PyGILState_Ensure();
}
~PyGILStateLocker()
{
PyGILState_Release(gstate);
}
private:
PyGILState_STATE gstate;
};
/**
* If a thread holds the global interpreter lock (GIL) but runs a long operation
* in C where it doesn't need to hold the GIL it can release it temporarily. Or
* if the thread has to run code in the main thread where Python code may be
* executed it must release the GIL to avoid a deadlock. In either case the thread
* must hold the GIL when instantiating an object of PyGILStateRelease.
* As PyGILStateLocker it's best to create an instance of PyGILStateRelease on the
* stack.
*/
class BaseExport PyGILStateRelease
{
public:
PyGILStateRelease()
{
// release the global interpreter lock
state = PyEval_SaveThread();
}
~PyGILStateRelease()
{
// grab the global interpreter lock again
PyEval_RestoreThread(state);
}
private:
PyThreadState* state;
};
/** The Interpreter class
* This class manage the python interpreter and hold a lot
* helper functions for handling python stuff
*/
class BaseExport InterpreterSingleton
{
public:
InterpreterSingleton();
~InterpreterSingleton();
/** @name execution methods
*/
//@{
/// Run a statement on the python interpreter and gives back a string with the representation of
/// the result.
std::string runString(const char* psCmd);
/// Run a statement on the python interpreter with a key for exchanging strings
std::string
runStringWithKey(const char* psCmd, const char* key, const char* key_initial_value = "");
/// Run a statement on the python interpreter and return back the result object.
Py::Object runStringObject(const char* sCmd);
/// Run a statement on the python interpreter and gives back a string with the representation of
/// the result.
void runInteractiveString(const char* psCmd);
/// Run file (script) on the python interpreter
void runFile(const char* pxFileName, bool local);
/// Run a statement with arguments on the python interpreter
void runStringArg(const char* psCom, ...);
/// runs a python object method with no return value and no arguments
void runMethodVoid(PyObject* pobject, const char* method);
/// runs a python object method which returns a arbitrary object
PyObject* runMethodObject(PyObject* pobject, const char* method);
/// runs a python method with arbitrary params
void runMethod(PyObject* pobject,
const char* method,
const char* resfmt = nullptr,
void* cresult = nullptr,
const char* argfmt = "()",
...);
//@}
/** @name Module handling
*/
//@{
/* Loads a module
*/
bool loadModule(const char* psModName);
/// Add an additional python path
void addPythonPath(const char* Path);
static void addType(PyTypeObject* Type, PyObject* Module, const char* Name);
/// Add a module and return a PyObject to it
PyObject* addModule(Py::ExtensionModuleBase*);
/// Clean-up registered modules
void cleanupModules();
//@}
/** @name Cleanup
*/
//@{
/** Register a cleanup function to be called by finalize(). The cleanup function will be called
* with no arguments and should return no value. At most 32 cleanup functions can be registered.
* When the registration is successful 0 is returned; on failure -1 is returned. The cleanup
* function registered last is called first. Each cleanup function will be called at most once.
* Since Python's internal finalization will have completed before the cleanup function, no
* Python APIs should be called by \a func.
*/
int cleanup(void (*func)());
/** This calls the registered cleanup functions. @see cleanup() for more details. */
void finalize();
/// This shuts down the application.
void systemExit();
//@}
/** @name startup and singletons
*/
//@{
/// init the interpreter and returns the module search path
const char* init(int argc, char* argv[]);
int runCommandLine(const char* prompt);
void replaceStdOutput();
static InterpreterSingleton& Instance();
static void Destruct();
//@}
/** @name external wrapper libs
* here we can access external dynamically loaded wrapper libs
* done e.g. by SWIG or SIP
*/
//@{
/// generate a SWIG object
PyObject*
createSWIGPointerObj(const char* Module, const char* TypeName, void* Pointer, int own);
bool convertSWIGPointerObj(const char* Module,
const char* TypeName,
PyObject* obj,
void** ptr,
int flags);
void cleanupSWIG(const char* TypeName);
PyTypeObject* getSWIGPointerTypeObj(const char* Module, const char* TypeName);
//@}
/** @name methods for debugging facility
*/
//@{
/// sets the file name which should be debugged
void dbgObserveFile(const char* sFileName = "");
/// sets a break point to a special line number in the current file
void dbgSetBreakPoint(unsigned int uiLineNumber);
/// unsets a break point to a special line number in the current file
void dbgUnsetBreakPoint(unsigned int uiLineNumber);
/// One step further
void dbgStep();
//@}
/** @name static helper functions
*/
//@{
/// replaces all char with escapes for usage in python console
static std::string strToPython(const char* Str);
static std::string strToPython(const std::string& Str)
{
return strToPython(Str.c_str());
}
//@}
PyObject* getValue(const char* key, const char* result_var);
protected:
// singleton
static InterpreterSingleton* _pcSingleton;
private:
std::string _cDebugFileName;
PyThreadState* _global;
std::list<Py::ExtensionModuleBase*> _modules;
};
/** Access to the InterpreterSingleton object
* This method is used to gain access to the one and only instance of
* the InterpreterSingleton class.
*/
inline InterpreterSingleton& Interpreter()
{
return InterpreterSingleton::Instance();
}
} // namespace Base
#endif // BASE_INTERPRETER_H