diff options
author | synodriver <diguohuangjiajinweijun@gmail.com> | 2023-05-27 22:18:46 +0800 |
---|---|---|
committer | Andrew Clayton <a.clayton@nginx.com> | 2023-06-01 00:25:03 +0100 |
commit | 93ed66958e11b40cc06dcb32fc8e623967af6347 (patch) | |
tree | 720ddd839d0594a0ffc6fb36e8c8a71fcd42b2a9 /src/python/nxt_python_asgi_lifespan.c | |
parent | 31ff94add9c4043a753683d9e8b68733c69aa1ac (diff) | |
download | unit-93ed66958e11b40cc06dcb32fc8e623967af6347.tar.gz unit-93ed66958e11b40cc06dcb32fc8e623967af6347.tar.bz2 |
Python: Add ASGI lifespan state support.
Lifespan state is a special dict in asgi lifespan scope, which allow
applications to persist data from the lifespan cycle to request/response
handling. The scope["state"] namespace provides a place to store these
sorts of things. The server will ensure that a shallow copy of the
namespace is passed into each subsequent request/response call into the
application.
Some frameworks are already taking advantage of this feature, for
example, starlette, and without this feature they wouldn't work
properly.
Signed-off-by: synodriver <diguohuangjiajinweijun@gmail.com>
Reviewed-by: Andrew Clayton <a.clayton@nginx.com>
[ Minor code tweaks to avoid lines > 80 chars, static a function and
re-work the PyMemberDef structure initialisation for Python <3.7
and -Wwrite-strings compatibility - Andrew ]
Tested-by: <https://github.com/synodriver>
Tested-by: <https://github.com/hawiliali>
Closes: <https://github.com/nginx/unit/issues/864>
Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
Diffstat (limited to 'src/python/nxt_python_asgi_lifespan.c')
-rw-r--r-- | src/python/nxt_python_asgi_lifespan.c | 54 |
1 files changed, 53 insertions, 1 deletions
diff --git a/src/python/nxt_python_asgi_lifespan.c b/src/python/nxt_python_asgi_lifespan.c index 1fc0e6b7..041cca21 100644 --- a/src/python/nxt_python_asgi_lifespan.c +++ b/src/python/nxt_python_asgi_lifespan.c @@ -12,6 +12,8 @@ #include <python/nxt_python_asgi.h> #include <python/nxt_python_asgi_str.h> +#include <structmember.h> + typedef struct { PyObject_HEAD @@ -25,6 +27,7 @@ typedef struct { PyObject *startup_future; PyObject *shutdown_future; PyObject *receive_future; + PyObject *state; } nxt_py_asgi_lifespan_t; static PyObject *nxt_py_asgi_lifespan_target_startup( @@ -41,6 +44,7 @@ static PyObject *nxt_py_asgi_lifespan_send_shutdown( nxt_py_asgi_lifespan_t *lifespan, int v, PyObject *dict); static PyObject *nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t *lifespan); static PyObject *nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future); +static void nxt_py_asgi_lifespan_dealloc(PyObject *self); static PyMethodDef nxt_py_asgi_lifespan_methods[] = { @@ -50,6 +54,26 @@ static PyMethodDef nxt_py_asgi_lifespan_methods[] = { { NULL, NULL, 0, 0 } }; +static PyMemberDef nxt_py_asgi_lifespan_members[] = { + { +#if PY_VERSION_HEX >= NXT_PYTHON_VER(3, 7) + .name = "state", +#else + .name = (char *)"state", +#endif + .type = T_OBJECT_EX, + .offset = offsetof(nxt_py_asgi_lifespan_t, state), + .flags = READONLY, +#if PY_VERSION_HEX >= NXT_PYTHON_VER(3, 7) + .doc = PyDoc_STR("lifespan.state") +#else + .doc = (char *)PyDoc_STR("lifespan.state") +#endif + }, + + { NULL, 0, 0, 0, NULL } +}; + static PyAsyncMethods nxt_py_asgi_async_methods = { .am_await = nxt_py_asgi_await, }; @@ -59,13 +83,14 @@ static PyTypeObject nxt_py_asgi_lifespan_type = { .tp_name = "unit._asgi_lifespan", .tp_basicsize = sizeof(nxt_py_asgi_lifespan_t), - .tp_dealloc = nxt_py_asgi_dealloc, + .tp_dealloc = nxt_py_asgi_lifespan_dealloc, .tp_as_async = &nxt_py_asgi_async_methods, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = "unit ASGI Lifespan object", .tp_iter = nxt_py_asgi_iter, .tp_iternext = nxt_py_asgi_next, .tp_methods = nxt_py_asgi_lifespan_methods, + .tp_members = nxt_py_asgi_lifespan_members, }; @@ -163,12 +188,29 @@ nxt_py_asgi_lifespan_target_startup(nxt_py_asgi_ctx_data_t *ctx_data, lifespan->shutdown_called = 0; lifespan->shutdown_future = NULL; lifespan->receive_future = NULL; + lifespan->state = NULL; scope = nxt_py_asgi_new_scope(NULL, nxt_py_lifespan_str, nxt_py_2_0_str); if (nxt_slow_path(scope == NULL)) { goto release_future; } + lifespan->state = PyDict_New(); + if (nxt_slow_path(lifespan->state == NULL)) { + nxt_unit_req_error(NULL, + "Python failed to create 'state' dict"); + goto release_future; + } + + if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_state_str, + lifespan->state) == -1)) + { + nxt_unit_req_error(NULL, + "Python failed to set 'scope.state' item"); + Py_CLEAR(lifespan->state); + goto release_future; + } + if (!target->asgi_legacy) { nxt_unit_req_debug(NULL, "Python call ASGI 3.0 application"); @@ -604,4 +646,14 @@ nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future) } +static void +nxt_py_asgi_lifespan_dealloc(PyObject *self) +{ + nxt_py_asgi_lifespan_t *lifespan = (nxt_py_asgi_lifespan_t *)self; + + Py_CLEAR(lifespan->state); + PyObject_Del(self); +} + + #endif /* NXT_HAVE_ASGI */ |