summaryrefslogtreecommitdiffhomepage
path: root/src/python/nxt_python.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/python/nxt_python.c')
-rw-r--r--src/python/nxt_python.c331
1 files changed, 331 insertions, 0 deletions
diff --git a/src/python/nxt_python.c b/src/python/nxt_python.c
new file mode 100644
index 00000000..5b6021bb
--- /dev/null
+++ b/src/python/nxt_python.c
@@ -0,0 +1,331 @@
+
+/*
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <Python.h>
+
+#include <nxt_main.h>
+#include <nxt_router.h>
+#include <nxt_unit.h>
+
+#include <python/nxt_python.h>
+
+#include NXT_PYTHON_MOUNTS_H
+
+
+#if PY_MAJOR_VERSION == 3
+#define PyString_FromStringAndSize(str, size) \
+ PyUnicode_DecodeLatin1((str), (size), "strict")
+
+#else
+#define PyUnicode_InternInPlace PyString_InternInPlace
+#endif
+
+static nxt_int_t nxt_python_start(nxt_task_t *task,
+ nxt_process_data_t *data);
+static void nxt_python_atexit(void);
+
+static uint32_t compat[] = {
+ NXT_VERNUM, NXT_DEBUG,
+};
+
+
+NXT_EXPORT nxt_app_module_t nxt_app_module = {
+ sizeof(compat),
+ compat,
+ nxt_string("python"),
+ PY_VERSION,
+ nxt_python_mounts,
+ nxt_nitems(nxt_python_mounts),
+ NULL,
+ nxt_python_start,
+};
+
+static PyObject *nxt_py_stderr_flush;
+PyObject *nxt_py_application;
+
+#if PY_MAJOR_VERSION == 3
+static wchar_t *nxt_py_home;
+#else
+static char *nxt_py_home;
+#endif
+
+
+static nxt_int_t
+nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
+{
+ int rc;
+ char *nxt_py_module;
+ size_t len;
+ PyObject *obj, *pypath, *module;
+ nxt_unit_ctx_t *unit_ctx;
+ nxt_unit_init_t python_init;
+ nxt_common_app_conf_t *app_conf;
+ nxt_python_app_conf_t *c;
+#if PY_MAJOR_VERSION == 3
+ char *path;
+ size_t size;
+ nxt_int_t pep405;
+
+ static const char pyvenv[] = "/pyvenv.cfg";
+ static const char bin_python[] = "/bin/python";
+#endif
+
+ app_conf = data->app;
+ c = &app_conf->u.python;
+
+ if (c->home != NULL) {
+ len = nxt_strlen(c->home);
+
+#if PY_MAJOR_VERSION == 3
+
+ path = nxt_malloc(len + sizeof(pyvenv));
+ if (nxt_slow_path(path == NULL)) {
+ nxt_alert(task, "Failed to allocate memory");
+ return NXT_ERROR;
+ }
+
+ nxt_memcpy(path, c->home, len);
+ nxt_memcpy(path + len, pyvenv, sizeof(pyvenv));
+
+ pep405 = (access(path, R_OK) == 0);
+
+ nxt_free(path);
+
+ if (pep405) {
+ size = (len + sizeof(bin_python)) * sizeof(wchar_t);
+
+ } else {
+ size = (len + 1) * sizeof(wchar_t);
+ }
+
+ nxt_py_home = nxt_malloc(size);
+ if (nxt_slow_path(nxt_py_home == NULL)) {
+ nxt_alert(task, "Failed to allocate memory");
+ return NXT_ERROR;
+ }
+
+ if (pep405) {
+ mbstowcs(nxt_py_home, c->home, len);
+ mbstowcs(nxt_py_home + len, bin_python, sizeof(bin_python));
+ Py_SetProgramName(nxt_py_home);
+
+ } else {
+ mbstowcs(nxt_py_home, c->home, len + 1);
+ Py_SetPythonHome(nxt_py_home);
+ }
+
+#else
+ nxt_py_home = nxt_malloc(len + 1);
+ if (nxt_slow_path(nxt_py_home == NULL)) {
+ nxt_alert(task, "Failed to allocate memory");
+ return NXT_ERROR;
+ }
+
+ nxt_memcpy(nxt_py_home, c->home, len + 1);
+ Py_SetPythonHome(nxt_py_home);
+#endif
+ }
+
+ Py_InitializeEx(0);
+
+ module = NULL;
+ obj = NULL;
+
+ obj = PySys_GetObject((char *) "stderr");
+ if (nxt_slow_path(obj == NULL)) {
+ nxt_alert(task, "Python failed to get \"sys.stderr\" object");
+ goto fail;
+ }
+
+ nxt_py_stderr_flush = PyObject_GetAttrString(obj, "flush");
+ if (nxt_slow_path(nxt_py_stderr_flush == NULL)) {
+ nxt_alert(task, "Python failed to get \"flush\" attribute of "
+ "\"sys.stderr\" object");
+ goto fail;
+ }
+
+ /* obj is a Borrowed reference. */
+
+ if (c->path.length > 0) {
+ obj = PyString_FromStringAndSize((char *) c->path.start,
+ c->path.length);
+
+ if (nxt_slow_path(obj == NULL)) {
+ nxt_alert(task, "Python failed to create string object \"%V\"",
+ &c->path);
+ goto fail;
+ }
+
+ pypath = PySys_GetObject((char *) "path");
+
+ if (nxt_slow_path(pypath == NULL)) {
+ nxt_alert(task, "Python failed to get \"sys.path\" list");
+ goto fail;
+ }
+
+ if (nxt_slow_path(PyList_Insert(pypath, 0, obj) != 0)) {
+ nxt_alert(task, "Python failed to insert \"%V\" into \"sys.path\"",
+ &c->path);
+ goto fail;
+ }
+
+ Py_DECREF(obj);
+ }
+
+ obj = Py_BuildValue("[s]", "unit");
+ if (nxt_slow_path(obj == NULL)) {
+ nxt_alert(task, "Python failed to create the \"sys.argv\" list");
+ goto fail;
+ }
+
+ if (nxt_slow_path(PySys_SetObject((char *) "argv", obj) != 0)) {
+ nxt_alert(task, "Python failed to set the \"sys.argv\" list");
+ goto fail;
+ }
+
+ Py_CLEAR(obj);
+
+ nxt_py_module = nxt_alloca(c->module.length + 1);
+ nxt_memcpy(nxt_py_module, c->module.start, c->module.length);
+ nxt_py_module[c->module.length] = '\0';
+
+ module = PyImport_ImportModule(nxt_py_module);
+ if (nxt_slow_path(module == NULL)) {
+ nxt_alert(task, "Python failed to import module \"%s\"", nxt_py_module);
+ nxt_python_print_exception();
+ goto fail;
+ }
+
+ obj = PyDict_GetItemString(PyModule_GetDict(module), "application");
+ if (nxt_slow_path(obj == NULL)) {
+ nxt_alert(task, "Python failed to get \"application\" "
+ "from module \"%s\"", nxt_py_module);
+ goto fail;
+ }
+
+ if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
+ nxt_alert(task, "\"application\" in module \"%s\" "
+ "is not a callable object", nxt_py_module);
+ goto fail;
+ }
+
+ nxt_py_application = obj;
+ obj = NULL;
+
+ Py_INCREF(nxt_py_application);
+
+ Py_CLEAR(module);
+
+ nxt_unit_default_init(task, &python_init);
+
+ python_init.shm_limit = data->app->shm_limit;
+
+ rc = nxt_python_wsgi_init(task, &python_init);
+ if (nxt_slow_path(rc == NXT_ERROR)) {
+ goto fail;
+ }
+
+ unit_ctx = nxt_unit_init(&python_init);
+ if (nxt_slow_path(unit_ctx == NULL)) {
+ goto fail;
+ }
+
+ rc = nxt_python_wsgi_run(unit_ctx);
+
+ nxt_unit_done(unit_ctx);
+
+ nxt_python_atexit();
+
+ exit(rc);
+
+ return NXT_OK;
+
+fail:
+
+ Py_XDECREF(obj);
+ Py_XDECREF(module);
+
+ nxt_python_atexit();
+
+ return NXT_ERROR;
+}
+
+
+nxt_int_t
+nxt_python_init_strings(nxt_python_string_t *pstr)
+{
+ PyObject *obj;
+
+ while (pstr->string.start != NULL) {
+ obj = PyString_FromStringAndSize((char *) pstr->string.start,
+ pstr->string.length);
+ if (nxt_slow_path(obj == NULL)) {
+ return NXT_ERROR;
+ }
+
+ PyUnicode_InternInPlace(&obj);
+
+ *pstr->object_p = obj;
+
+ pstr++;
+ }
+
+ return NXT_OK;
+}
+
+
+void
+nxt_python_done_strings(nxt_python_string_t *pstr)
+{
+ PyObject *obj;
+
+ while (pstr->string.start != NULL) {
+ obj = *pstr->object_p;
+
+ Py_XDECREF(obj);
+ *pstr->object_p = NULL;
+
+ pstr++;
+ }
+}
+
+
+static void
+nxt_python_atexit(void)
+{
+ nxt_python_wsgi_done();
+
+ Py_XDECREF(nxt_py_stderr_flush);
+ Py_XDECREF(nxt_py_application);
+
+ Py_Finalize();
+
+ if (nxt_py_home != NULL) {
+ nxt_free(nxt_py_home);
+ }
+}
+
+
+void
+nxt_python_print_exception(void)
+{
+ PyErr_Print();
+
+#if PY_MAJOR_VERSION == 3
+ /* The backtrace may be buffered in sys.stderr file object. */
+ {
+ PyObject *result;
+
+ result = PyObject_CallFunction(nxt_py_stderr_flush, NULL);
+ if (nxt_slow_path(result == NULL)) {
+ PyErr_Clear();
+ return;
+ }
+
+ Py_DECREF(result);
+ }
+#endif
+}