summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_python_wsgi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nxt_python_wsgi.c')
-rw-r--r--src/nxt_python_wsgi.c246
1 files changed, 126 insertions, 120 deletions
diff --git a/src/nxt_python_wsgi.c b/src/nxt_python_wsgi.c
index a6d5f217..5bb2fb2c 100644
--- a/src/nxt_python_wsgi.c
+++ b/src/nxt_python_wsgi.c
@@ -86,6 +86,7 @@ static PyObject *nxt_py_input_read(nxt_py_input_t *self, PyObject *args);
static PyObject *nxt_py_input_readline(nxt_py_input_t *self, PyObject *args);
static PyObject *nxt_py_input_readlines(nxt_py_input_t *self, PyObject *args);
+static void nxt_python_print_exception(void);
static int nxt_python_write(nxt_python_run_ctx_t *ctx, PyObject *bytes);
struct nxt_python_run_ctx_s {
@@ -130,58 +131,17 @@ static PyMethodDef nxt_py_input_methods[] = {
static PyTypeObject nxt_py_input_type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "unit._input", /* tp_name */
- (int) sizeof(nxt_py_input_t), /* tp_basicsize */
- 0, /* tp_itemsize */
- (destructor) nxt_py_input_dealloc, /* tp_dealloc */
- 0, /* tp_print */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_compare */
- 0, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- 0, /* tp_call */
- 0, /* tp_str */
- 0, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT, /* tp_flags */
- "unit input object.", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- nxt_py_input_methods, /* tp_methods */
- 0, /* tp_members */
- 0, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- 0, /* tp_init */
- 0, /* tp_alloc */
- 0, /* tp_new */
- 0, /* tp_free */
- 0, /* tp_is_gc */
- 0, /* tp_bases */
- 0, /* tp_mro - method resolution order */
- 0, /* tp_cache */
- 0, /* tp_subclasses */
- 0, /* tp_weaklist */
- 0, /* tp_del */
- 0, /* tp_version_tag */
-#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION > 3
- 0, /* tp_finalize */
-#endif
+
+ .tp_name = "unit._input",
+ .tp_basicsize = sizeof(nxt_py_input_t),
+ .tp_dealloc = (destructor) nxt_py_input_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_doc = "unit input object.",
+ .tp_methods = nxt_py_input_methods,
};
+static PyObject *nxt_py_stderr_flush;
static PyObject *nxt_py_application;
static PyObject *nxt_py_start_resp_obj;
static PyObject *nxt_py_write_obj;
@@ -193,6 +153,7 @@ static wchar_t *nxt_py_home;
static char *nxt_py_home;
#endif
+static PyThreadState *nxt_python_thread_state;
static nxt_python_run_ctx_t *nxt_python_run_ctx;
@@ -279,6 +240,21 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
module = 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;
+ }
+
+ Py_DECREF(obj);
+
if (c->path.length > 0) {
obj = PyString_FromStringAndSize((char *) c->path.start,
c->path.length);
@@ -349,7 +325,7 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
module = PyImport_ImportModule(nxt_py_module);
if (nxt_slow_path(module == NULL)) {
nxt_alert(task, "Python failed to import module \"%s\"", nxt_py_module);
- PyErr_Print();
+ nxt_python_print_exception();
goto fail;
}
@@ -363,7 +339,6 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
nxt_alert(task, "\"application\" in module \"%s\" "
"is not a callable object", nxt_py_module);
- PyErr_Print();
goto fail;
}
@@ -382,10 +357,14 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
goto fail;
}
+ nxt_python_thread_state = PyEval_SaveThread();
+
rc = nxt_unit_run(unit_ctx);
nxt_unit_done(unit_ctx);
+ PyEval_RestoreThread(nxt_python_thread_state);
+
nxt_python_atexit();
exit(rc);
@@ -407,22 +386,26 @@ static void
nxt_python_request_handler(nxt_unit_request_info_t *req)
{
int rc;
- PyObject *result, *iterator, *item, *args, *environ;
+ PyObject *environ, *args, *response, *iterator, *item;
+ PyObject *close, *result;
nxt_python_run_ctx_t run_ctx = {-1, 0, NULL, req};
+ PyEval_RestoreThread(nxt_python_thread_state);
+
environ = nxt_python_get_environ(&run_ctx);
if (nxt_slow_path(environ == NULL)) {
- nxt_unit_request_done(req, NXT_UNIT_ERROR);
-
- return;
+ rc = NXT_UNIT_ERROR;
+ goto done;
}
args = PyTuple_New(2);
if (nxt_slow_path(args == NULL)) {
+ Py_DECREF(environ);
+
nxt_unit_req_error(req, "Python failed to create arguments tuple");
- nxt_unit_request_done(req, NXT_UNIT_ERROR);
- return;
+ rc = NXT_UNIT_ERROR;
+ goto done;
}
PyTuple_SET_ITEM(args, 0, environ);
@@ -432,103 +415,104 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
nxt_python_run_ctx = &run_ctx;
- result = PyObject_CallObject(nxt_py_application, args);
+ response = PyObject_CallObject(nxt_py_application, args);
Py_DECREF(args);
- if (nxt_slow_path(result == NULL)) {
+ if (nxt_slow_path(response == NULL)) {
nxt_unit_req_error(req, "Python failed to call the application");
- PyErr_Print();
-
- nxt_unit_request_done(req, NXT_UNIT_ERROR);
- nxt_python_run_ctx = NULL;
+ nxt_python_print_exception();
- return;
+ rc = NXT_UNIT_ERROR;
+ goto done;
}
- item = NULL;
- iterator = NULL;
+ /* Shortcut: avoid iterate over response string symbols. */
+ if (PyBytes_Check(response)) {
+ rc = nxt_python_write(&run_ctx, response);
- /* Shortcut: avoid iterate over result string symbols. */
- if (PyBytes_Check(result)) {
+ } else {
+ iterator = PyObject_GetIter(response);
- rc = nxt_python_write(&run_ctx, result);
- if (nxt_slow_path(rc != NXT_UNIT_OK)) {
- goto fail;
- }
+ if (nxt_fast_path(iterator != NULL)) {
+ rc = NXT_UNIT_OK;
- } else {
- iterator = PyObject_GetIter(result);
+ while (run_ctx.bytes_sent < run_ctx.content_length) {
+ item = PyIter_Next(iterator);
- if (nxt_slow_path(iterator == NULL)) {
- nxt_unit_req_error(req, "the application returned "
- "not an iterable object");
+ if (item == NULL) {
+ if (nxt_slow_path(PyErr_Occurred() != NULL)) {
+ nxt_unit_req_error(req, "Python failed to iterate over "
+ "the application response object");
+ nxt_python_print_exception();
- goto fail;
- }
+ rc = NXT_UNIT_ERROR;
+ }
- while (run_ctx.bytes_sent < run_ctx.content_length
- && (item = PyIter_Next(iterator)))
- {
- if (nxt_slow_path(!PyBytes_Check(item))) {
- nxt_unit_req_error(req, "the application returned "
- "not a bytestring object");
+ break;
+ }
- goto fail;
- }
+ if (nxt_fast_path(PyBytes_Check(item))) {
+ rc = nxt_python_write(&run_ctx, item);
- rc = nxt_python_write(&run_ctx, item);
- if (nxt_slow_path(rc != NXT_UNIT_OK)) {
- goto fail;
- }
+ } else {
+ nxt_unit_req_error(req, "the application returned "
+ "not a bytestring object");
+ rc = NXT_UNIT_ERROR;
+ }
- Py_DECREF(item);
- }
+ Py_DECREF(item);
- Py_DECREF(iterator);
+ if (nxt_slow_path(rc != NXT_UNIT_OK)) {
+ break;
+ }
+ }
- if (PyObject_HasAttrString(result, "close")) {
- PyObject_CallMethod(result, (char *) "close", NULL);
- }
- }
+ Py_DECREF(iterator);
- if (nxt_slow_path(PyErr_Occurred() != NULL)) {
- nxt_unit_req_error(req, "an application error occurred");
- PyErr_Print();
- }
+ } else {
+ nxt_unit_req_error(req,
+ "the application returned not an iterable object");
+ nxt_python_print_exception();
- nxt_unit_request_done(req, NXT_UNIT_OK);
+ rc = NXT_UNIT_ERROR;
+ }
- Py_DECREF(result);
+ close = PyObject_GetAttrString(response, "close");
- nxt_python_run_ctx = NULL;
+ if (close != NULL) {
+ result = PyObject_CallFunction(close, NULL);
+ if (nxt_slow_path(result == NULL)) {
+ nxt_unit_req_error(req, "Python failed to call the close() "
+ "method of the application response");
+ nxt_python_print_exception();
- return;
+ } else {
+ Py_DECREF(result);
+ }
-fail:
+ Py_DECREF(close);
- if (item != NULL) {
- Py_DECREF(item);
+ } else {
+ PyErr_Clear();
+ }
}
- if (iterator != NULL) {
- Py_DECREF(iterator);
- }
+ Py_DECREF(response);
- if (PyObject_HasAttrString(result, "close")) {
- PyObject_CallMethod(result, (char *) "close", NULL);
- }
+done:
- Py_DECREF(result);
- nxt_python_run_ctx = NULL;
+ nxt_python_thread_state = PyEval_SaveThread();
- nxt_unit_request_done(req, NXT_UNIT_ERROR);
+ nxt_python_run_ctx = NULL;
+ nxt_unit_request_done(req, rc);
}
static void
nxt_python_atexit(void)
{
+ Py_XDECREF(nxt_py_stderr_flush);
Py_XDECREF(nxt_py_application);
Py_XDECREF(nxt_py_start_resp_obj);
Py_XDECREF(nxt_py_write_obj);
@@ -767,7 +751,7 @@ nxt_python_add_sptr(nxt_python_run_ctx_t *ctx, const char *name,
nxt_unit_req_error(ctx->req,
"Python failed to create value string \"%.*s\"",
(int) size, src);
- PyErr_Print();
+ nxt_python_print_exception();
return NXT_UNIT_ERROR;
}
@@ -802,7 +786,7 @@ nxt_python_add_str(nxt_python_run_ctx_t *ctx, const char *name,
nxt_unit_req_error(ctx->req,
"Python failed to create value string \"%.*s\"",
(int) size, str);
- PyErr_Print();
+ nxt_python_print_exception();
return NXT_UNIT_ERROR;
}
@@ -1137,6 +1121,28 @@ nxt_py_input_readlines(nxt_py_input_t *self, PyObject *args)
}
+static 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
+}
+
+
static int
nxt_python_write(nxt_python_run_ctx_t *ctx, PyObject *bytes)
{