mirror of https://github.com/Icinga/icinga2.git
Implement calling Python functions.
This commit is contained in:
parent
172938b19d
commit
79bf945868
|
@ -72,8 +72,6 @@ void ScriptInterpreter::ScriptFunctionThunk(const ScriptTask::Ptr& task,
|
||||||
|
|
||||||
void ScriptInterpreter::SubscribeFunction(const String& name)
|
void ScriptInterpreter::SubscribeFunction(const String& name)
|
||||||
{
|
{
|
||||||
assert(Application::IsMainThread());
|
|
||||||
|
|
||||||
m_SubscribedFunctions.insert(name);
|
m_SubscribedFunctions.insert(name);
|
||||||
|
|
||||||
ScriptFunction::Ptr sf = boost::make_shared<ScriptFunction>(boost::bind(&ScriptInterpreter::ScriptFunctionThunk, this, _1, name, _2));
|
ScriptFunction::Ptr sf = boost::make_shared<ScriptFunction>(boost::bind(&ScriptInterpreter::ScriptFunctionThunk, this, _1, name, _2));
|
||||||
|
@ -82,8 +80,6 @@ void ScriptInterpreter::SubscribeFunction(const String& name)
|
||||||
|
|
||||||
void ScriptInterpreter::UnsubscribeFunction(const String& name)
|
void ScriptInterpreter::UnsubscribeFunction(const String& name)
|
||||||
{
|
{
|
||||||
assert(Application::IsMainThread());
|
|
||||||
|
|
||||||
m_SubscribedFunctions.erase(name);
|
m_SubscribedFunctions.erase(name);
|
||||||
ScriptFunction::Unregister(name);
|
ScriptFunction::Unregister(name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -565,4 +565,3 @@ void Utility::SetNonBlockingSocket(SOCKET s)
|
||||||
ioctlsocket(s, FIONBIO, &lTrue);
|
ioctlsocket(s, FIONBIO, &lTrue);
|
||||||
#endif /* _WIN32 */
|
#endif /* _WIN32 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,17 +26,18 @@ PythonInterpreter::PythonInterpreter(const PythonLanguage::Ptr& language,
|
||||||
: ScriptInterpreter(script), m_Language(language), m_ThreadState(NULL)
|
: ScriptInterpreter(script), m_Language(language), m_ThreadState(NULL)
|
||||||
{
|
{
|
||||||
PyEval_AcquireLock();
|
PyEval_AcquireLock();
|
||||||
|
PythonInterpreter *interp = m_Language->GetCurrentInterpreter();
|
||||||
|
m_Language->SetCurrentInterpreter(this);
|
||||||
|
|
||||||
PyInterpreterState *interp = m_Language->GetMainThreadState()->interp;
|
PyInterpreterState *pinterp = m_Language->GetMainThreadState()->interp;
|
||||||
m_ThreadState = PyThreadState_New(interp);
|
m_ThreadState = PyThreadState_New(pinterp);
|
||||||
|
|
||||||
(void) PyThreadState_Swap(m_ThreadState);
|
(void) PyThreadState_Swap(m_ThreadState);
|
||||||
PyRun_SimpleString(script->GetCode().CStr());
|
PyRun_SimpleString(script->GetCode().CStr());
|
||||||
(void) PyThreadState_Swap(NULL);
|
(void) PyThreadState_Swap(NULL);
|
||||||
|
|
||||||
|
m_Language->SetCurrentInterpreter(interp);
|
||||||
PyEval_ReleaseLock();
|
PyEval_ReleaseLock();
|
||||||
|
|
||||||
SubscribeFunction("python::Test");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PythonInterpreter::~PythonInterpreter(void)
|
PythonInterpreter::~PythonInterpreter(void)
|
||||||
|
@ -70,8 +71,41 @@ void PythonInterpreter::ProcessCall(const ScriptTask::Ptr& task, const String& f
|
||||||
const vector<Value>& arguments)
|
const vector<Value>& arguments)
|
||||||
{
|
{
|
||||||
PyEval_AcquireThread(m_ThreadState);
|
PyEval_AcquireThread(m_ThreadState);
|
||||||
std::cout << "Received call for method '" << function << "'" << std::endl;
|
PythonInterpreter *interp = m_Language->GetCurrentInterpreter();
|
||||||
PyEval_ReleaseThread(m_ThreadState);
|
m_Language->SetCurrentInterpreter(this);
|
||||||
|
|
||||||
task->FinishResult(0);
|
try {
|
||||||
|
map<String, PyObject *>::iterator it = m_Functions.find(function);
|
||||||
|
|
||||||
|
if (it == m_Functions.end())
|
||||||
|
BOOST_THROW_EXCEPTION(invalid_argument("Function '" + function + "' does not exist."));
|
||||||
|
|
||||||
|
PyObject *func = it->second;
|
||||||
|
|
||||||
|
PyObject *args = PyTuple_New(arguments.size());
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
BOOST_FOREACH(const Value& argument, arguments) {
|
||||||
|
PyObject *arg = PythonLanguage::MarshalToPython(argument);
|
||||||
|
PyTuple_SetItem(args, i, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *result = PyObject_CallObject(func, args);
|
||||||
|
|
||||||
|
Py_DECREF(args);
|
||||||
|
|
||||||
|
if (result == NULL) {
|
||||||
|
// re-throw python exception
|
||||||
|
}
|
||||||
|
|
||||||
|
Value vresult = PythonLanguage::MarshalFromPython(result);
|
||||||
|
Py_DECREF(result);
|
||||||
|
|
||||||
|
task->FinishResult(vresult);
|
||||||
|
} catch (...) {
|
||||||
|
task->FinishException(boost::current_exception());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Language->SetCurrentInterpreter(interp);
|
||||||
|
PyEval_ReleaseThread(m_ThreadState);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,15 @@
|
||||||
|
|
||||||
using namespace icinga;
|
using namespace icinga;
|
||||||
|
|
||||||
|
PythonInterpreter *PythonLanguage::m_CurrentInterpreter;
|
||||||
|
|
||||||
REGISTER_SCRIPTLANGUAGE("Python", PythonLanguage);
|
REGISTER_SCRIPTLANGUAGE("Python", PythonLanguage);
|
||||||
|
|
||||||
|
PyMethodDef PythonLanguage::m_NativeMethodDef[] = {
|
||||||
|
{ "RegisterFunction", &PythonLanguage::PyRegisterFunction, METH_VARARGS, NULL },
|
||||||
|
{ NULL, NULL } /* sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
PythonLanguage::PythonLanguage(void)
|
PythonLanguage::PythonLanguage(void)
|
||||||
: ScriptLanguage()
|
: ScriptLanguage()
|
||||||
{
|
{
|
||||||
|
@ -37,7 +44,7 @@ PythonLanguage::PythonLanguage(void)
|
||||||
|
|
||||||
m_MainThreadState = PyThreadState_Get();
|
m_MainThreadState = PyThreadState_Get();
|
||||||
|
|
||||||
m_NativeModule = Py_InitModule("ire", NULL);
|
m_NativeModule = Py_InitModule("ire", m_NativeMethodDef);
|
||||||
|
|
||||||
(void) PyThreadState_Swap(NULL);
|
(void) PyThreadState_Swap(NULL);
|
||||||
PyEval_ReleaseLock();
|
PyEval_ReleaseLock();
|
||||||
|
@ -128,12 +135,33 @@ PyObject *PythonLanguage::MarshalToPython(const Value& value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Value PythonLanguage::MarshalFromPython(PyObject *value, const ScriptArgumentHint& hint)
|
Value PythonLanguage::MarshalFromPython(PyObject *value)
|
||||||
{
|
{
|
||||||
if (value == Py_None) {
|
if (value == Py_None) {
|
||||||
return Empty;
|
return Empty;
|
||||||
} else if (PyTuple_Check(value)) {
|
} else if (PyTuple_Check(value) && PyTuple_Size(value) == 2) {
|
||||||
// TODO: look up object
|
PyObject *ptype, *pname;
|
||||||
|
|
||||||
|
ptype = PyTuple_GetItem(value, 0);
|
||||||
|
|
||||||
|
if (ptype == NULL || !PyString_Check(ptype))
|
||||||
|
BOOST_THROW_EXCEPTION(invalid_argument("Tuple must contain two strings."));
|
||||||
|
|
||||||
|
String type = PyString_AsString(ptype);
|
||||||
|
|
||||||
|
pname = PyTuple_GetItem(value, 1);
|
||||||
|
|
||||||
|
if (pname == NULL || !PyString_Check(pname))
|
||||||
|
BOOST_THROW_EXCEPTION(invalid_argument("Tuple must contain two strings."));
|
||||||
|
|
||||||
|
String name = PyString_AsString(pname);
|
||||||
|
|
||||||
|
DynamicObject::Ptr object = DynamicObject::GetObject(type, name);
|
||||||
|
|
||||||
|
if (!object)
|
||||||
|
BOOST_THROW_EXCEPTION(invalid_argument("Object '" + name + "' of type '" + type + "' does not exist."));
|
||||||
|
|
||||||
|
return object;
|
||||||
} else if (PyFloat_Check(value)) {
|
} else if (PyFloat_Check(value)) {
|
||||||
return PyFloat_AsDouble(value);
|
return PyFloat_AsDouble(value);
|
||||||
} else if (PyString_Check(value)) {
|
} else if (PyString_Check(value)) {
|
||||||
|
@ -143,7 +171,7 @@ Value PythonLanguage::MarshalFromPython(PyObject *value, const ScriptArgumentHin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *PythonLanguage::CallNativeFunction(PyObject *self, PyObject *args)
|
PyObject *PythonLanguage::PyCallNativeFunction(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
assert(PyString_Check(self));
|
assert(PyString_Check(self));
|
||||||
|
|
||||||
|
@ -165,20 +193,28 @@ PyObject *PythonLanguage::CallNativeFunction(PyObject *self, PyObject *args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptTask::Ptr task = boost::make_shared<ScriptTask>(function, arguments);
|
PyThreadState *tstate = PyEval_SaveThread();
|
||||||
task->Start();
|
|
||||||
task->Wait();
|
Value result;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Value result = task->GetResult();
|
ScriptTask::Ptr task = boost::make_shared<ScriptTask>(function, arguments);
|
||||||
|
task->Start();
|
||||||
|
task->Wait();
|
||||||
|
|
||||||
return MarshalToPython(result);
|
result = task->GetResult();
|
||||||
} catch (const std::exception& ex) {
|
} catch (const std::exception& ex) {
|
||||||
|
PyEval_RestoreThread(tstate);
|
||||||
|
|
||||||
String message = diagnostic_information(ex);
|
String message = diagnostic_information(ex);
|
||||||
PyErr_SetString(PyExc_RuntimeError, message.CStr());
|
PyErr_SetString(PyExc_RuntimeError, message.CStr());
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyEval_RestoreThread(tstate);
|
||||||
|
|
||||||
|
return MarshalToPython(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -189,20 +225,20 @@ PyObject *PythonLanguage::CallNativeFunction(PyObject *self, PyObject *args)
|
||||||
*/
|
*/
|
||||||
void PythonLanguage::RegisterNativeFunction(const String& name, const ScriptFunction::Ptr& function)
|
void PythonLanguage::RegisterNativeFunction(const String& name, const ScriptFunction::Ptr& function)
|
||||||
{
|
{
|
||||||
(void) PyThreadState_Swap(m_MainThreadState);
|
PyThreadState *tstate = PyThreadState_Swap(m_MainThreadState);
|
||||||
|
|
||||||
PyObject *pname = PyString_FromString(name.CStr());
|
PyObject *pname = PyString_FromString(name.CStr());
|
||||||
|
|
||||||
PyMethodDef *md = new PyMethodDef;
|
PyMethodDef *md = new PyMethodDef;
|
||||||
md->ml_name = strdup(name.CStr());
|
md->ml_name = strdup(name.CStr());
|
||||||
md->ml_meth = &PythonLanguage::CallNativeFunction;
|
md->ml_meth = &PythonLanguage::PyCallNativeFunction;
|
||||||
md->ml_flags = METH_VARARGS;
|
md->ml_flags = METH_VARARGS;
|
||||||
md->ml_doc = NULL;
|
md->ml_doc = NULL;
|
||||||
|
|
||||||
PyObject *pfunc = PyCFunction_NewEx(md, pname, m_NativeModule);
|
PyObject *pfunc = PyCFunction_NewEx(md, pname, m_NativeModule);
|
||||||
(void) PyModule_AddObject(m_NativeModule, name.CStr(), pfunc);
|
(void) PyModule_AddObject(m_NativeModule, name.CStr(), pfunc);
|
||||||
|
|
||||||
(void) PyThreadState_Swap(NULL);
|
(void) PyThreadState_Swap(tstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -212,7 +248,7 @@ void PythonLanguage::RegisterNativeFunction(const String& name, const ScriptFunc
|
||||||
*/
|
*/
|
||||||
void PythonLanguage::UnregisterNativeFunction(const String& name)
|
void PythonLanguage::UnregisterNativeFunction(const String& name)
|
||||||
{
|
{
|
||||||
(void) PyThreadState_Swap(m_MainThreadState);
|
PyThreadState *tstate = PyThreadState_Swap(m_MainThreadState);
|
||||||
|
|
||||||
PyObject *pdict = PyModule_GetDict(m_NativeModule);
|
PyObject *pdict = PyModule_GetDict(m_NativeModule);
|
||||||
PyObject *pname = PyString_FromString(name.CStr());
|
PyObject *pname = PyString_FromString(name.CStr());
|
||||||
|
@ -227,5 +263,55 @@ void PythonLanguage::UnregisterNativeFunction(const String& name)
|
||||||
(void) PyDict_DelItem(pdict, pname);
|
(void) PyDict_DelItem(pdict, pname);
|
||||||
Py_DECREF(pname);
|
Py_DECREF(pname);
|
||||||
|
|
||||||
(void) PyThreadState_Swap(NULL);
|
(void) PyThreadState_Swap(tstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *PythonLanguage::PyRegisterFunction(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
PyObject *object;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "sO", &name, &object))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
PythonInterpreter *interp = GetCurrentInterpreter();
|
||||||
|
|
||||||
|
if (interp == NULL) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "GetCurrentInterpreter() returned NULL.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PyCallable_Check(object)) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Function object is not callable.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock lock(Application::GetMutex());
|
||||||
|
interp->RegisterPythonFunction(name, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Py_INCREF(Py_None);
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current interpreter object. Caller must hold the GIL.
|
||||||
|
*
|
||||||
|
* @returns The current interpreter.
|
||||||
|
*/
|
||||||
|
PythonInterpreter *PythonLanguage::GetCurrentInterpreter(void)
|
||||||
|
{
|
||||||
|
return m_CurrentInterpreter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current interpreter. Caller must hold the GIL.
|
||||||
|
*
|
||||||
|
* @param interpreter The interpreter.
|
||||||
|
*/
|
||||||
|
void PythonLanguage::SetCurrentInterpreter(PythonInterpreter *interpreter)
|
||||||
|
{
|
||||||
|
m_CurrentInterpreter = interpreter;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
namespace icinga
|
namespace icinga
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class PythonInterpreter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Python scripting language.
|
* The Python scripting language.
|
||||||
*
|
*
|
||||||
|
@ -41,17 +43,24 @@ public:
|
||||||
|
|
||||||
PyThreadState *GetMainThreadState(void) const;
|
PyThreadState *GetMainThreadState(void) const;
|
||||||
|
|
||||||
|
static PythonInterpreter *GetCurrentInterpreter(void);
|
||||||
|
static void SetCurrentInterpreter(PythonInterpreter *interpreter);
|
||||||
|
|
||||||
|
static PyObject *MarshalToPython(const Value& value);
|
||||||
|
static Value MarshalFromPython(PyObject *value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PyThreadState *m_MainThreadState;
|
PyThreadState *m_MainThreadState;
|
||||||
PyObject *m_NativeModule;
|
PyObject *m_NativeModule;
|
||||||
|
static PythonInterpreter *m_CurrentInterpreter;
|
||||||
|
|
||||||
void RegisterNativeFunction(const String& name, const ScriptFunction::Ptr& function);
|
void RegisterNativeFunction(const String& name, const ScriptFunction::Ptr& function);
|
||||||
void UnregisterNativeFunction(const String& name);
|
void UnregisterNativeFunction(const String& name);
|
||||||
|
|
||||||
static PyObject *CallNativeFunction(PyObject *self, PyObject *args);
|
static PyObject *PyCallNativeFunction(PyObject *self, PyObject *args);
|
||||||
|
static PyObject *PyRegisterFunction(PyObject *self, PyObject *args);
|
||||||
|
|
||||||
static PyObject *MarshalToPython(const Value& value);
|
static PyMethodDef m_NativeMethodDef[];
|
||||||
static Value MarshalFromPython(PyObject *value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue