mirror of
https://github.com/stefanocasazza/ULib.git
synced 2025-09-28 19:05:55 +08:00
298 lines
8.0 KiB
C++
298 lines
8.0 KiB
C++
// ============================================================================
|
|
//
|
|
// = LIBRARY
|
|
// ULib - c++ library
|
|
//
|
|
// = FILENAME
|
|
// mod_python.cpp - this is a wrapper to embed the PYTHON interpreter
|
|
//
|
|
// = AUTHOR
|
|
// Stefano Casazza
|
|
//
|
|
// ============================================================================
|
|
|
|
#include <ulib/utility/uhttp.h>
|
|
|
|
#undef PACKAGE_NAME
|
|
#undef PACKAGE_STRING
|
|
#undef PACKAGE_TARNAME
|
|
#undef PACKAGE_VERSION
|
|
#undef PACKAGE_BUGREPORT
|
|
|
|
#ifdef HAVE_SSIZE_T
|
|
#undef HAVE_SSIZE_T
|
|
#endif
|
|
#ifdef _GNU_SOURCE
|
|
#undef _GNU_SOURCE
|
|
#endif
|
|
#ifdef _POSIX_C_SOURCE
|
|
#undef _POSIX_C_SOURCE
|
|
#endif
|
|
#ifdef _XOPEN_SOURCE
|
|
#undef _XOPEN_SOURCE
|
|
#endif
|
|
|
|
#include <Python.h>
|
|
|
|
static PyObject* py_module;
|
|
static PyObject* py_userver_on_request_func;
|
|
static PyThreadState* py_main_thread_state;
|
|
|
|
#define U_PROGRAM_NAME "../python/userver.py"
|
|
|
|
extern "C" {
|
|
|
|
static inline void dict_set(PyObject* dict, const char* key, PyObject* val)
|
|
{
|
|
U_TRACE(0, "dict_set(%p,%S,%p)", dict, key, val)
|
|
|
|
U_SYSCALL_VOID(PyDict_SetItemString, "%p,%S,%p", dict, key, val);
|
|
|
|
Py_XDECREF(val);
|
|
}
|
|
|
|
static void UPYTHON_set_environment(void* py_environ, char* name, char* value)
|
|
{
|
|
U_TRACE(0, "UPYTHON_set_environment(%p,%S,%S)", py_environ, name, value)
|
|
|
|
dict_set((PyObject*)py_environ, name, PyString_FromString(value));
|
|
}
|
|
|
|
extern U_EXPORT bool initPYTHON();
|
|
U_EXPORT bool initPYTHON()
|
|
{
|
|
U_TRACE(0, "::initPYTHON()")
|
|
|
|
bool esito = true;
|
|
|
|
U_SET_MODULE_NAME(python);
|
|
|
|
U_INTERNAL_ASSERT_POINTER(UHTTP::py_project_app)
|
|
|
|
U_SYSCALL_VOID(Py_SetProgramName, "%S", (char*)U_PROGRAM_NAME);
|
|
|
|
// initialize thread support
|
|
|
|
U_SYSCALL_VOID_NO_PARAM(PyEval_InitThreads);
|
|
U_SYSCALL_VOID_NO_PARAM(Py_Initialize);
|
|
|
|
char* sargv[] = { (char*)U_PROGRAM_NAME, (UHTTP::py_project_root ? UHTTP::py_project_root->data() : (char*)"."), UHTTP::py_project_app->data(),
|
|
(UHTTP::py_virtualenv_path ? UHTTP::py_virtualenv_path->data() : (char*)"") };
|
|
|
|
U_SYSCALL_VOID(PySys_SetArgv, "%d,%p", 4, sargv);
|
|
|
|
PyObject* py_module_name = PyString_FromString("userver");
|
|
|
|
U_INTERNAL_ASSERT_POINTER(py_module_name)
|
|
|
|
// save a pointer to the main PyThreadState object
|
|
|
|
py_main_thread_state = (PyThreadState*) U_SYSCALL_NO_PARAM(PyThreadState_Get);
|
|
py_module = (PyObject*) U_SYSCALL(PyImport_Import, "%p", py_module_name);
|
|
|
|
if (py_module == U_NULLPTR ||
|
|
PyModule_Check(py_module) == false)
|
|
{
|
|
esito = false;
|
|
|
|
U_WARNING("I can't load python module userver; check parse errors...");
|
|
|
|
PyErr_Print();
|
|
|
|
goto end;
|
|
}
|
|
|
|
Py_DECREF(py_module_name);
|
|
|
|
py_userver_on_request_func = (PyObject*) U_SYSCALL(PyObject_GetAttrString, "%p,%S", py_module, "_userver_on_request");
|
|
|
|
U_INTERNAL_ASSERT_POINTER(py_userver_on_request_func)
|
|
|
|
if (PyCallable_Check(py_userver_on_request_func) == false)
|
|
{
|
|
esito = false;
|
|
|
|
U_WARNING("I can't call _userver_on_request() inside python module userver; check parse errors...");
|
|
|
|
PyErr_Print();
|
|
}
|
|
|
|
// release the lock
|
|
|
|
PyEval_ReleaseLock();
|
|
|
|
end:
|
|
U_RESET_MODULE_NAME;
|
|
|
|
U_RETURN(esito);
|
|
}
|
|
|
|
extern U_EXPORT bool runPYTHON();
|
|
U_EXPORT bool runPYTHON()
|
|
{
|
|
U_TRACE(0, "::runPYTHON()")
|
|
|
|
bool esito = true;
|
|
PyObject* py_result;
|
|
|
|
U_SET_MODULE_NAME(python);
|
|
|
|
PyGILState_STATE gstate = (PyGILState_STATE) U_SYSCALL_NO_PARAM(PyGILState_Ensure);
|
|
PyObject* py_func_args = (PyObject*) U_SYSCALL(PyTuple_New, "%d", 1);
|
|
PyObject* py_environ = (PyObject*) U_SYSCALL_NO_PARAM(PyDict_New);
|
|
|
|
U_INTERNAL_ASSERT_POINTER(py_environ)
|
|
|
|
if (UHTTP::setEnvironmentForLanguageProcessing(U_WSCGI, py_environ, UPYTHON_set_environment) == false)
|
|
{
|
|
esito = false;
|
|
|
|
goto end;
|
|
}
|
|
|
|
dict_set(py_environ, "wsgi.run_once", PyBool_FromLong(0));
|
|
dict_set(py_environ, "wsgi.multithread", PyBool_FromLong(0));
|
|
dict_set(py_environ, "wsgi.multiprocess", PyBool_FromLong(UServer_Base::isPreForked()));
|
|
|
|
if (*UHTTP::body) dict_set(py_environ, "userver.req.content", PyByteArray_FromStringAndSize(U_STRING_TO_PARAM(*UHTTP::body)));
|
|
|
|
// call python
|
|
|
|
U_SYSCALL_VOID(PyTuple_SetItem, "%p,%d,%p", py_func_args, 0, py_environ);
|
|
|
|
py_result = (PyObject*) U_SYSCALL(PyObject_CallObject, "%p,%p", py_userver_on_request_func, py_func_args);
|
|
|
|
Py_DECREF(py_func_args);
|
|
|
|
esito = false;
|
|
|
|
if (py_result)
|
|
{
|
|
if (PyString_Check(py_result))
|
|
{
|
|
esito = true;
|
|
|
|
(void) UClientImage_Base::wbuffer->clear();
|
|
|
|
U_http_info.nResponseCode = HTTP_INTERNAL_ERROR;
|
|
|
|
U_SRV_LOG("python call failed: %S", PyString_AS_STRING(py_result));
|
|
}
|
|
else if (PyTuple_Check(py_result) &&
|
|
PyTuple_Size( py_result) == 3)
|
|
{
|
|
esito = true;
|
|
|
|
PyObject* py_status = PyTuple_GET_ITEM(py_result, 0);
|
|
PyObject* py_headers = PyTuple_GET_ITEM(py_result, 1);
|
|
PyObject* py_body = PyTuple_GET_ITEM(py_result, 2);
|
|
|
|
if (py_status &&
|
|
PyString_Check(py_status))
|
|
{
|
|
// get the status code
|
|
|
|
U_http_info.nResponseCode = ::strtol(PyString_AS_STRING(py_status), U_NULLPTR, 10);
|
|
|
|
U_DUMP("HTTP status = (%d %S)", U_http_info.nResponseCode, UHTTP::getStatusDescription())
|
|
}
|
|
|
|
if (py_headers &&
|
|
PyList_Check(py_headers))
|
|
{
|
|
// get the headers
|
|
|
|
for (int i = 0, size = PyList_Size(py_headers); i < size; ++i)
|
|
{
|
|
PyObject* py_header_tuple = PyList_GET_ITEM(py_headers, i);
|
|
|
|
if (py_header_tuple &&
|
|
PyTuple_Check(py_header_tuple) &&
|
|
PyTuple_Size(py_header_tuple) == 2)
|
|
{
|
|
PyObject* py_name = PyTuple_GET_ITEM(py_header_tuple, 0);
|
|
PyObject* py_value = PyTuple_GET_ITEM(py_header_tuple, 1);
|
|
|
|
if (py_name &&
|
|
py_value &&
|
|
PyString_Check(py_name) &&
|
|
PyString_Check(py_value))
|
|
{
|
|
(void) UClientImage_Base::wbuffer->append(PyString_AS_STRING(py_name));
|
|
(void) UClientImage_Base::wbuffer->append(U_CONSTANT_TO_PARAM(": "));
|
|
|
|
(void) UClientImage_Base::wbuffer->append(PyString_AS_STRING(py_value));
|
|
(void) UClientImage_Base::wbuffer->append(U_CONSTANT_TO_PARAM(U_CRLF));
|
|
}
|
|
}
|
|
}
|
|
|
|
(void) UClientImage_Base::wbuffer->append(U_CONSTANT_TO_PARAM(U_CRLF));
|
|
|
|
U_http_info.endHeader = UClientImage_Base::wbuffer->size();
|
|
}
|
|
|
|
ssize_t rsize = 0;
|
|
char* rcontent = U_NULLPTR;
|
|
|
|
if (PyByteArray_Check(py_body))
|
|
{
|
|
rsize = PyByteArray_Size(py_body);
|
|
rcontent = PyByteArray_AS_STRING(py_body);
|
|
}
|
|
else if (PyString_Check(py_body))
|
|
{
|
|
rsize = PyString_Size(py_body);
|
|
rcontent = PyString_AS_STRING(py_body);
|
|
}
|
|
|
|
if (rcontent &&
|
|
rsize > 0)
|
|
{
|
|
// get the body
|
|
|
|
(void) UClientImage_Base::wbuffer->append(rcontent, rsize);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (esito == false)
|
|
{
|
|
U_WARNING("python call failed");
|
|
|
|
PyErr_Print();
|
|
}
|
|
|
|
Py_XDECREF(py_result);
|
|
|
|
PyGILState_Release(gstate); // Release the thread. No Python API allowed beyond this point
|
|
|
|
UClientImage_Base::environment->setEmpty();
|
|
|
|
end:
|
|
U_RESET_MODULE_NAME;
|
|
|
|
U_RETURN(esito);
|
|
}
|
|
|
|
extern U_EXPORT void endPYTHON();
|
|
U_EXPORT void endPYTHON()
|
|
{
|
|
U_TRACE_NO_PARAM(0, "endPYTHON()")
|
|
|
|
if (py_module)
|
|
{
|
|
// shut down the interpreter
|
|
|
|
U_SYSCALL_VOID_NO_PARAM(PyEval_AcquireLock);
|
|
|
|
U_SYSCALL_VOID(PyThreadState_Swap, "%p", py_main_thread_state);
|
|
|
|
Py_XDECREF(py_userver_on_request_func);
|
|
Py_XDECREF(py_module);
|
|
|
|
U_SYSCALL_VOID_NO_PARAM(Py_Finalize);
|
|
}
|
|
}
|
|
}
|