xref: /unit/src/python/nxt_python.c (revision 1624:e46b1b422545)
1 
2 /*
3  * Copyright (C) NGINX, Inc.
4  */
5 
6 
7 #include <Python.h>
8 
9 #include <nxt_main.h>
10 #include <nxt_router.h>
11 #include <nxt_unit.h>
12 
13 #include <python/nxt_python.h>
14 
15 #include NXT_PYTHON_MOUNTS_H
16 
17 
18 static nxt_int_t nxt_python_start(nxt_task_t *task,
19     nxt_process_data_t *data);
20 static void nxt_python_atexit(void);
21 
22 static uint32_t  compat[] = {
23     NXT_VERNUM, NXT_DEBUG,
24 };
25 
26 
27 NXT_EXPORT nxt_app_module_t  nxt_app_module = {
28     sizeof(compat),
29     compat,
30     nxt_string("python"),
31     PY_VERSION,
32     nxt_python_mounts,
33     nxt_nitems(nxt_python_mounts),
34     NULL,
35     nxt_python_start,
36 };
37 
38 static PyObject           *nxt_py_stderr_flush;
39 PyObject                  *nxt_py_application;
40 
41 #if PY_MAJOR_VERSION == 3
42 static wchar_t            *nxt_py_home;
43 #else
44 static char               *nxt_py_home;
45 #endif
46 
47 
48 static nxt_int_t
49 nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
50 {
51     int                    rc, asgi;
52     char                   *nxt_py_module;
53     size_t                 len;
54     PyObject               *obj, *pypath, *module;
55     const char             *callable;
56     nxt_unit_ctx_t         *unit_ctx;
57     nxt_unit_init_t        python_init;
58     nxt_common_app_conf_t  *app_conf;
59     nxt_python_app_conf_t  *c;
60 #if PY_MAJOR_VERSION == 3
61     char                   *path;
62     size_t                 size;
63     nxt_int_t              pep405;
64 
65     static const char pyvenv[] = "/pyvenv.cfg";
66     static const char bin_python[] = "/bin/python";
67 #endif
68 
69     app_conf = data->app;
70     c = &app_conf->u.python;
71 
72     if (c->home != NULL) {
73         len = nxt_strlen(c->home);
74 
75 #if PY_MAJOR_VERSION == 3
76 
77         path = nxt_malloc(len + sizeof(pyvenv));
78         if (nxt_slow_path(path == NULL)) {
79             nxt_alert(task, "Failed to allocate memory");
80             return NXT_ERROR;
81         }
82 
83         nxt_memcpy(path, c->home, len);
84         nxt_memcpy(path + len, pyvenv, sizeof(pyvenv));
85 
86         pep405 = (access(path, R_OK) == 0);
87 
88         nxt_free(path);
89 
90         if (pep405) {
91             size = (len + sizeof(bin_python)) * sizeof(wchar_t);
92 
93         } else {
94             size = (len + 1) * sizeof(wchar_t);
95         }
96 
97         nxt_py_home = nxt_malloc(size);
98         if (nxt_slow_path(nxt_py_home == NULL)) {
99             nxt_alert(task, "Failed to allocate memory");
100             return NXT_ERROR;
101         }
102 
103         if (pep405) {
104             mbstowcs(nxt_py_home, c->home, len);
105             mbstowcs(nxt_py_home + len, bin_python, sizeof(bin_python));
106             Py_SetProgramName(nxt_py_home);
107 
108         } else {
109             mbstowcs(nxt_py_home, c->home, len + 1);
110             Py_SetPythonHome(nxt_py_home);
111         }
112 
113 #else
114         nxt_py_home = nxt_malloc(len + 1);
115         if (nxt_slow_path(nxt_py_home == NULL)) {
116             nxt_alert(task, "Failed to allocate memory");
117             return NXT_ERROR;
118         }
119 
120         nxt_memcpy(nxt_py_home, c->home, len + 1);
121         Py_SetPythonHome(nxt_py_home);
122 #endif
123     }
124 
125     Py_InitializeEx(0);
126 
127     module = NULL;
128     obj = NULL;
129 
130     obj = PySys_GetObject((char *) "stderr");
131     if (nxt_slow_path(obj == NULL)) {
132         nxt_alert(task, "Python failed to get \"sys.stderr\" object");
133         goto fail;
134     }
135 
136     nxt_py_stderr_flush = PyObject_GetAttrString(obj, "flush");
137     if (nxt_slow_path(nxt_py_stderr_flush == NULL)) {
138         nxt_alert(task, "Python failed to get \"flush\" attribute of "
139                         "\"sys.stderr\" object");
140         goto fail;
141     }
142 
143     /* obj is a Borrowed reference. */
144 
145     if (c->path.length > 0) {
146         obj = PyString_FromStringAndSize((char *) c->path.start,
147                                          c->path.length);
148 
149         if (nxt_slow_path(obj == NULL)) {
150             nxt_alert(task, "Python failed to create string object \"%V\"",
151                       &c->path);
152             goto fail;
153         }
154 
155         pypath = PySys_GetObject((char *) "path");
156 
157         if (nxt_slow_path(pypath == NULL)) {
158             nxt_alert(task, "Python failed to get \"sys.path\" list");
159             goto fail;
160         }
161 
162         if (nxt_slow_path(PyList_Insert(pypath, 0, obj) != 0)) {
163             nxt_alert(task, "Python failed to insert \"%V\" into \"sys.path\"",
164                       &c->path);
165             goto fail;
166         }
167 
168         Py_DECREF(obj);
169     }
170 
171     obj = Py_BuildValue("[s]", "unit");
172     if (nxt_slow_path(obj == NULL)) {
173         nxt_alert(task, "Python failed to create the \"sys.argv\" list");
174         goto fail;
175     }
176 
177     if (nxt_slow_path(PySys_SetObject((char *) "argv", obj) != 0)) {
178         nxt_alert(task, "Python failed to set the \"sys.argv\" list");
179         goto fail;
180     }
181 
182     Py_CLEAR(obj);
183 
184     nxt_py_module = nxt_alloca(c->module.length + 1);
185     nxt_memcpy(nxt_py_module, c->module.start, c->module.length);
186     nxt_py_module[c->module.length] = '\0';
187 
188     module = PyImport_ImportModule(nxt_py_module);
189     if (nxt_slow_path(module == NULL)) {
190         nxt_alert(task, "Python failed to import module \"%s\"", nxt_py_module);
191         nxt_python_print_exception();
192         goto fail;
193     }
194 
195     callable = (c->callable != NULL) ? c->callable : "application";
196 
197     obj = PyDict_GetItemString(PyModule_GetDict(module), callable);
198     if (nxt_slow_path(obj == NULL)) {
199         nxt_alert(task, "Python failed to get \"%s\" "
200                   "from module \"%s\"", callable, nxt_py_module);
201         goto fail;
202     }
203 
204     if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
205         nxt_alert(task, "\"%s\" in module \"%s\" "
206                   "is not a callable object", callable, nxt_py_module);
207         goto fail;
208     }
209 
210     nxt_py_application = obj;
211     obj = NULL;
212 
213     Py_INCREF(nxt_py_application);
214 
215     Py_CLEAR(module);
216 
217     nxt_unit_default_init(task, &python_init);
218 
219     python_init.shm_limit = data->app->shm_limit;
220 
221     asgi = nxt_python_asgi_check(nxt_py_application);
222 
223     if (asgi) {
224         rc = nxt_python_asgi_init(task, &python_init);
225 
226     } else {
227         rc = nxt_python_wsgi_init(task, &python_init);
228     }
229 
230     if (nxt_slow_path(rc == NXT_ERROR)) {
231         goto fail;
232     }
233 
234     unit_ctx = nxt_unit_init(&python_init);
235     if (nxt_slow_path(unit_ctx == NULL)) {
236         goto fail;
237     }
238 
239     if (asgi) {
240         rc = nxt_python_asgi_run(unit_ctx);
241 
242     } else {
243         rc = nxt_python_wsgi_run(unit_ctx);
244     }
245 
246     nxt_unit_done(unit_ctx);
247 
248     nxt_python_atexit();
249 
250     exit(rc);
251 
252     return NXT_OK;
253 
254 fail:
255 
256     Py_XDECREF(obj);
257     Py_XDECREF(module);
258 
259     nxt_python_atexit();
260 
261     return NXT_ERROR;
262 }
263 
264 
265 nxt_int_t
266 nxt_python_init_strings(nxt_python_string_t *pstr)
267 {
268     PyObject  *obj;
269 
270     while (pstr->string.start != NULL) {
271         obj = PyString_FromStringAndSize((char *) pstr->string.start,
272                                          pstr->string.length);
273         if (nxt_slow_path(obj == NULL)) {
274             return NXT_ERROR;
275         }
276 
277         PyUnicode_InternInPlace(&obj);
278 
279         *pstr->object_p = obj;
280 
281         pstr++;
282     }
283 
284     return NXT_OK;
285 }
286 
287 
288 void
289 nxt_python_done_strings(nxt_python_string_t *pstr)
290 {
291     PyObject  *obj;
292 
293     while (pstr->string.start != NULL) {
294         obj = *pstr->object_p;
295 
296         Py_XDECREF(obj);
297         *pstr->object_p = NULL;
298 
299         pstr++;
300     }
301 }
302 
303 
304 static void
305 nxt_python_atexit(void)
306 {
307     nxt_python_wsgi_done();
308     nxt_python_asgi_done();
309 
310     Py_XDECREF(nxt_py_stderr_flush);
311     Py_XDECREF(nxt_py_application);
312 
313     Py_Finalize();
314 
315     if (nxt_py_home != NULL) {
316         nxt_free(nxt_py_home);
317     }
318 }
319 
320 
321 void
322 nxt_python_print_exception(void)
323 {
324     PyErr_Print();
325 
326 #if PY_MAJOR_VERSION == 3
327     /* The backtrace may be buffered in sys.stderr file object. */
328     {
329         PyObject  *result;
330 
331         result = PyObject_CallFunction(nxt_py_stderr_flush, NULL);
332         if (nxt_slow_path(result == NULL)) {
333             PyErr_Clear();
334             return;
335         }
336 
337         Py_DECREF(result);
338     }
339 #endif
340 }
341