Python: multiple values in the "path" option.

This commit is contained in:
Valentin Bartenev
2020-12-22 17:53:41 +03:00
parent 65295c8141
commit cac762ab7e
5 changed files with 124 additions and 36 deletions

View File

@@ -9,6 +9,12 @@
date="" time="" date="" time=""
packager="Andrei Belov <defan@nginx.com>"> packager="Andrei Belov <defan@nginx.com>">
<change type="feature">
<para>
ability to specify multiple directories in the "path" option of Python apps.
</para>
</change>
<change type="bugfix"> <change type="bugfix">
<para> <para>
invalid HTTP responses were generated for some unusual status codes. invalid HTTP responses were generated for some unusual status codes.

View File

@@ -47,13 +47,13 @@ typedef struct {
typedef struct { typedef struct {
char *home; char *home;
nxt_str_t path; nxt_conf_value_t *path;
nxt_str_t module; nxt_str_t module;
char *callable; char *callable;
nxt_str_t protocol; nxt_str_t protocol;
uint32_t threads; uint32_t threads;
uint32_t thread_stack_size; uint32_t thread_stack_size;
} nxt_python_app_conf_t; } nxt_python_app_conf_t;

View File

@@ -96,6 +96,10 @@ static nxt_int_t nxt_conf_vldt_return(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data); nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data); nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_python_path_element(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_python_protocol(nxt_conf_validation_t *vldt, static nxt_int_t nxt_conf_vldt_python_protocol(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data); nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_threads(nxt_conf_validation_t *vldt, static nxt_int_t nxt_conf_vldt_threads(nxt_conf_validation_t *vldt,
@@ -491,7 +495,8 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
.type = NXT_CONF_VLDT_STRING, .type = NXT_CONF_VLDT_STRING,
}, { }, {
.name = nxt_string("path"), .name = nxt_string("path"),
.type = NXT_CONF_VLDT_STRING, .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
.validator = nxt_conf_vldt_python_path,
}, { }, {
.name = nxt_string("module"), .name = nxt_string("module"),
.type = NXT_CONF_VLDT_STRING, .type = NXT_CONF_VLDT_STRING,
@@ -1376,6 +1381,34 @@ nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
} }
static nxt_int_t
nxt_conf_vldt_python_path(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data)
{
if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
return nxt_conf_vldt_array_iterator(vldt, value,
&nxt_conf_vldt_python_path_element);
}
/* NXT_CONF_STRING */
return NXT_OK;
}
static nxt_int_t
nxt_conf_vldt_python_path_element(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value)
{
if (nxt_conf_type(value) != NXT_CONF_STRING) {
return nxt_conf_vldt_error(vldt, "The \"path\" array must contain "
"only string values.");
}
return NXT_OK;
}
static nxt_int_t static nxt_int_t
nxt_conf_vldt_python_protocol(nxt_conf_validation_t *vldt, nxt_conf_vldt_python_protocol(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data) nxt_conf_value_t *value, void *data)

View File

@@ -182,7 +182,7 @@ static nxt_conf_map_t nxt_python_app_conf[] = {
{ {
nxt_string("path"), nxt_string("path"),
NXT_CONF_MAP_STR, NXT_CONF_MAP_PTR,
offsetof(nxt_common_app_conf_t, u.python.path), offsetof(nxt_common_app_conf_t, u.python.path),
}, },

View File

@@ -24,6 +24,7 @@ typedef struct {
static nxt_int_t nxt_python_start(nxt_task_t *task, static nxt_int_t nxt_python_start(nxt_task_t *task,
nxt_process_data_t *data); nxt_process_data_t *data);
static nxt_int_t nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value);
static int nxt_python_init_threads(nxt_python_app_conf_t *c); static int nxt_python_init_threads(nxt_python_app_conf_t *c);
static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx); static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx);
static void *nxt_python_thread_func(void *main_ctx); static void *nxt_python_thread_func(void *main_ctx);
@@ -67,7 +68,7 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
int rc; int rc;
char *nxt_py_module; char *nxt_py_module;
size_t len; size_t len;
PyObject *obj, *pypath, *module; PyObject *obj, *module;
nxt_str_t proto; nxt_str_t proto;
const char *callable; const char *callable;
nxt_unit_ctx_t *unit_ctx; nxt_unit_ctx_t *unit_ctx;
@@ -162,38 +163,18 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
} }
nxt_py_stderr_flush = PyObject_GetAttrString(obj, "flush"); nxt_py_stderr_flush = PyObject_GetAttrString(obj, "flush");
/* obj is a Borrowed reference. */
obj = NULL;
if (nxt_slow_path(nxt_py_stderr_flush == NULL)) { if (nxt_slow_path(nxt_py_stderr_flush == NULL)) {
nxt_alert(task, "Python failed to get \"flush\" attribute of " nxt_alert(task, "Python failed to get \"flush\" attribute of "
"\"sys.stderr\" object"); "\"sys.stderr\" object");
goto fail; goto fail;
} }
/* obj is a Borrowed reference. */ if (nxt_slow_path(nxt_python_set_path(task, c->path) != NXT_OK)) {
goto fail;
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"); obj = Py_BuildValue("[s]", "unit");
@@ -317,6 +298,74 @@ fail:
} }
static nxt_int_t
nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value)
{
int ret;
PyObject *path, *sys;
nxt_str_t str;
nxt_uint_t n;
nxt_conf_value_t *array;
if (value == NULL) {
return NXT_OK;
}
sys = PySys_GetObject((char *) "path");
if (nxt_slow_path(sys == NULL)) {
nxt_alert(task, "Python failed to get \"sys.path\" list");
return NXT_ERROR;
}
/* sys is a Borrowed reference. */
if (nxt_conf_type(value) == NXT_CONF_STRING) {
n = 0;
goto value_is_string;
}
/* NXT_CONF_ARRAY */
array = value;
n = nxt_conf_array_elements_count(array);
while (n != 0) {
n--;
/*
* Insertion in front of existing paths starting from the last element
* to preserve original order while giving priority to the values
* specified in the "path" option.
*/
value = nxt_conf_get_array_element(array, n);
value_is_string:
nxt_conf_get_string(value, &str);
path = PyString_FromStringAndSize((char *) str.start, str.length);
if (nxt_slow_path(path == NULL)) {
nxt_alert(task, "Python failed to create string object \"%V\"",
&str);
return NXT_ERROR;
}
ret = PyList_Insert(sys, 0, path);
Py_DECREF(path);
if (nxt_slow_path(ret != 0)) {
nxt_alert(task, "Python failed to insert \"%V\" into \"sys.path\"",
&str);
return NXT_ERROR;
}
}
return NXT_OK;
}
static int static int
nxt_python_init_threads(nxt_python_app_conf_t *c) nxt_python_init_threads(nxt_python_app_conf_t *c)
{ {