Skip to content

Commit

Permalink
python: Support application factories
Browse files Browse the repository at this point in the history
Adds support for the app factory pattern to the Python language module.
A factory is a callable that returns a WSGI or ASGI application object.

Unit does not support passing arguments to factories.

Setting the `factory` option to `true` instructs Unit to treat the
configured `callable` as a factory.

For example:

    "my-app": {
        "type": "python",
        "path": "/srv/www/",
        "module": "hello",
        "callable": "create_app",
        "factory": true
    }

This is similar to other WSGI / ASGI servers. E.g.,

    $ uvicorn --factory hello:create_app
    $ gunicorn 'hello:create_app()'

The factory setting defaults to false.

Closes: nginx#1106
Link: <nginx#1336 (comment)>
[ Commit message - Dan / Minor code tweaks - Andrew ]
Signed-off-by: Andrew Clayton <[email protected]>
  • Loading branch information
gourav-kandoria committed Jul 1, 2024
1 parent e0c15ae commit c834352
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 1 deletion.
11 changes: 11 additions & 0 deletions src/nxt_conf_validation.c
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = {
.type = NXT_CONF_VLDT_STRING,
.validator = nxt_conf_vldt_targets_exclusive,
.u.string = "callable",
}, {
.name = nxt_string("factory"),
.type = NXT_CONF_VLDT_BOOLEAN,
.validator = nxt_conf_vldt_targets_exclusive,
.u.string = "factory",
}, {
.name = nxt_string("prefix"),
.type = NXT_CONF_VLDT_STRING,
Expand All @@ -862,6 +867,9 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_target_members[] = {
}, {
.name = nxt_string("callable"),
.type = NXT_CONF_VLDT_STRING,
}, {
.name = nxt_string("factory"),
.type = NXT_CONF_VLDT_BOOLEAN,
}, {
.name = nxt_string("prefix"),
.type = NXT_CONF_VLDT_STRING,
Expand All @@ -880,6 +888,9 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_notargets_members[] = {
}, {
.name = nxt_string("callable"),
.type = NXT_CONF_VLDT_STRING,
}, {
.name = nxt_string("factory"),
.type = NXT_CONF_VLDT_BOOLEAN,
}, {
.name = nxt_string("prefix"),
.type = NXT_CONF_VLDT_STRING,
Expand Down
27 changes: 26 additions & 1 deletion src/python/nxt_python.c
Original file line number Diff line number Diff line change
Expand Up @@ -403,11 +403,13 @@ nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target,
char *callable, *module_name;
PyObject *module, *obj;
nxt_str_t str;
nxt_bool_t is_factory = 0;
nxt_conf_value_t *value;

static nxt_str_t module_str = nxt_string("module");
static nxt_str_t callable_str = nxt_string("callable");
static nxt_str_t prefix_str = nxt_string("prefix");
static nxt_str_t factory_flag_str = nxt_string("factory");

module = obj = NULL;

Expand Down Expand Up @@ -449,7 +451,30 @@ nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target,
goto fail;
}

if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
value = nxt_conf_get_object_member(conf, &factory_flag_str, NULL);
if (value != NULL) {
is_factory = nxt_conf_get_boolean(value);
}

if (is_factory) {
if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
nxt_alert(task,
"factory \"%s\" in module \"%s\" "
"can not be called to fetch callable",
callable, module_name);
goto fail;
}

obj = PyObject_CallObject(obj, NULL);
if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
nxt_alert(task,
"factory \"%s\" in module \"%s\" "
"did not return callable object",
callable, module_name);
goto fail;
}

} else if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
nxt_alert(task, "\"%s\" in module \"%s\" is not a callable object",
callable, module_name);
goto fail;
Expand Down

0 comments on commit c834352

Please sign in to comment.