xref: /unit/src/python/nxt_python.c (revision 2338:a5f3bc4abb97)
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 typedef struct {
19     pthread_t       thread;
20     nxt_unit_ctx_t  *ctx;
21     void            *ctx_data;
22 } nxt_py_thread_info_t;
23 
24 
25 #if PY_MAJOR_VERSION == 3
26 static nxt_int_t nxt_python3_init_config(nxt_int_t pep405);
27 #endif
28 
29 static nxt_int_t nxt_python_start(nxt_task_t *task,
30     nxt_process_data_t *data);
31 static nxt_int_t nxt_python_set_target(nxt_task_t *task,
32     nxt_python_target_t *target, nxt_conf_value_t *conf);
33 nxt_inline nxt_int_t nxt_python_set_prefix(nxt_task_t *task,
34     nxt_python_target_t *target, nxt_conf_value_t *value);
35 static nxt_int_t nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value);
36 static int nxt_python_init_threads(nxt_python_app_conf_t *c);
37 static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx);
38 static void *nxt_python_thread_func(void *main_ctx);
39 static void nxt_python_join_threads(nxt_unit_ctx_t *ctx,
40     nxt_python_app_conf_t *c);
41 static void nxt_python_atexit(void);
42 
43 static uint32_t  compat[] = {
44     NXT_VERNUM, NXT_DEBUG,
45 };
46 
47 
48 NXT_EXPORT nxt_app_module_t  nxt_app_module = {
49     sizeof(compat),
50     compat,
51     nxt_string("python"),
52     PY_VERSION,
53     nxt_python_mounts,
54     nxt_nitems(nxt_python_mounts),
55     NULL,
56     nxt_python_start,
57 };
58 
59 static PyObject           *nxt_py_stderr_flush;
60 nxt_python_targets_t      *nxt_py_targets;
61 
62 #if PY_MAJOR_VERSION == 3
63 static wchar_t            *nxt_py_home;
64 #else
65 static char               *nxt_py_home;
66 #endif
67 
68 static pthread_attr_t        *nxt_py_thread_attr;
69 static nxt_py_thread_info_t  *nxt_py_threads;
70 static nxt_python_proto_t    nxt_py_proto;
71 
72 
73 #if PY_VERSION_HEX >= NXT_PYTHON_VER(3, 8)
74 
75 static nxt_int_t
nxt_python3_init_config(nxt_int_t pep405)76 nxt_python3_init_config(nxt_int_t pep405)
77 {
78     PyConfig     config;
79     PyStatus     status;
80     nxt_int_t    ret;
81     PyPreConfig  preconfig;
82 
83     ret = NXT_ERROR;
84 
85     PyPreConfig_InitIsolatedConfig(&preconfig);
86     /*
87      * Determine whether to use UTF-8 mode or not, UTF-8
88      * will be enabled if LC_CTYPE is C, POSIX or some
89      * specific UTF-8 locale.
90      */
91     preconfig.utf8_mode = -1;
92 
93     status = Py_PreInitialize(&preconfig);
94     if (PyStatus_Exception(status)) {
95         return ret;
96     }
97 
98     PyConfig_InitIsolatedConfig(&config);
99 
100     if (pep405) {
101         status = PyConfig_SetString(&config, &config.program_name,
102                                     nxt_py_home);
103         if (PyStatus_Exception(status)) {
104             goto out_config_clear;
105         }
106 
107     } else {
108         status = PyConfig_SetString(&config, &config.home, nxt_py_home);
109         if (PyStatus_Exception(status)) {
110             goto out_config_clear;
111         }
112     }
113 
114     status = Py_InitializeFromConfig(&config);
115     if (PyStatus_Exception(status)) {
116         goto out_config_clear;
117     }
118 
119     ret = NXT_OK;
120 
121 out_config_clear:
122 
123     PyConfig_Clear(&config);
124 
125     return ret;
126 }
127 
128 #elif PY_MAJOR_VERSION == 3
129 
130 static nxt_int_t
nxt_python3_init_config(nxt_int_t pep405)131 nxt_python3_init_config(nxt_int_t pep405)
132 {
133     if (pep405) {
134         Py_SetProgramName(nxt_py_home);
135 
136     } else {
137         Py_SetPythonHome(nxt_py_home);
138     }
139 
140     return NXT_OK;
141 }
142 
143 #endif
144 
145 
146 static nxt_int_t
nxt_python_start(nxt_task_t * task,nxt_process_data_t * data)147 nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
148 {
149     int                    rc;
150     size_t                 len, size;
151     uint32_t               next;
152     PyObject               *obj;
153     nxt_str_t              proto, probe_proto, name;
154     nxt_int_t              ret, n, i;
155     nxt_unit_ctx_t         *unit_ctx;
156     nxt_unit_init_t        python_init;
157     nxt_conf_value_t       *cv;
158     nxt_python_targets_t   *targets;
159     nxt_common_app_conf_t  *app_conf;
160     nxt_python_app_conf_t  *c;
161 #if PY_MAJOR_VERSION == 3
162     char                   *path;
163     nxt_int_t              pep405;
164 
165     static const char pyvenv[] = "/pyvenv.cfg";
166     static const char bin_python[] = "/bin/python";
167 #endif
168 
169     static const nxt_str_t  wsgi = nxt_string("wsgi");
170     static const nxt_str_t  asgi = nxt_string("asgi");
171 
172     app_conf = data->app;
173     c = &app_conf->u.python;
174 
175     if (c->home != NULL) {
176         len = nxt_strlen(c->home);
177 
178 #if PY_MAJOR_VERSION == 3
179 
180         path = nxt_malloc(len + sizeof(pyvenv));
181         if (nxt_slow_path(path == NULL)) {
182             nxt_alert(task, "Failed to allocate memory");
183             return NXT_ERROR;
184         }
185 
186         nxt_memcpy(path, c->home, len);
187         nxt_memcpy(path + len, pyvenv, sizeof(pyvenv));
188 
189         pep405 = (access(path, R_OK) == 0);
190 
191         nxt_free(path);
192 
193         if (pep405) {
194             size = (len + sizeof(bin_python)) * sizeof(wchar_t);
195 
196         } else {
197             size = (len + 1) * sizeof(wchar_t);
198         }
199 
200         nxt_py_home = nxt_malloc(size);
201         if (nxt_slow_path(nxt_py_home == NULL)) {
202             nxt_alert(task, "Failed to allocate memory");
203             return NXT_ERROR;
204         }
205 
206         if (pep405) {
207             mbstowcs(nxt_py_home, c->home, len);
208             mbstowcs(nxt_py_home + len, bin_python, sizeof(bin_python));
209 
210         } else {
211             mbstowcs(nxt_py_home, c->home, len + 1);
212         }
213 
214         ret = nxt_python3_init_config(pep405);
215         if (nxt_slow_path(ret == NXT_ERROR)) {
216             nxt_alert(task, "Failed to initialise config");
217             return NXT_ERROR;
218         }
219 
220 #else
221         nxt_py_home = nxt_malloc(len + 1);
222         if (nxt_slow_path(nxt_py_home == NULL)) {
223             nxt_alert(task, "Failed to allocate memory");
224             return NXT_ERROR;
225         }
226 
227         nxt_memcpy(nxt_py_home, c->home, len + 1);
228         Py_SetPythonHome(nxt_py_home);
229 #endif
230     }
231 
232     Py_InitializeEx(0);
233 
234 #if PY_VERSION_HEX < NXT_PYTHON_VER(3, 7)
235     if (c->threads > 1) {
236         PyEval_InitThreads();
237     }
238 #endif
239 
240     obj = NULL;
241 
242     python_init.ctx_data = NULL;
243 
244     obj = PySys_GetObject((char *) "stderr");
245     if (nxt_slow_path(obj == NULL)) {
246         nxt_alert(task, "Python failed to get \"sys.stderr\" object");
247         goto fail;
248     }
249 
250     nxt_py_stderr_flush = PyObject_GetAttrString(obj, "flush");
251 
252     /* obj is a Borrowed reference. */
253     obj = NULL;
254 
255     if (nxt_slow_path(nxt_py_stderr_flush == NULL)) {
256         nxt_alert(task, "Python failed to get \"flush\" attribute of "
257                         "\"sys.stderr\" object");
258         goto fail;
259     }
260 
261     if (nxt_slow_path(nxt_python_set_path(task, c->path) != NXT_OK)) {
262         goto fail;
263     }
264 
265     obj = Py_BuildValue("[s]", "unit");
266     if (nxt_slow_path(obj == NULL)) {
267         nxt_alert(task, "Python failed to create the \"sys.argv\" list");
268         goto fail;
269     }
270 
271     if (nxt_slow_path(PySys_SetObject((char *) "argv", obj) != 0)) {
272         nxt_alert(task, "Python failed to set the \"sys.argv\" list");
273         goto fail;
274     }
275 
276     Py_CLEAR(obj);
277 
278     n = (c->targets != NULL ? nxt_conf_object_members_count(c->targets) : 1);
279 
280     size = sizeof(nxt_python_targets_t) + n * sizeof(nxt_python_target_t);
281 
282     targets = nxt_unit_malloc(NULL, size);
283     if (nxt_slow_path(targets == NULL)) {
284         nxt_alert(task, "Could not allocate targets");
285         goto fail;
286     }
287 
288     memset(targets, 0, size);
289 
290     targets->count = n;
291     nxt_py_targets = targets;
292 
293     if (c->targets != NULL) {
294         next = 0;
295 
296         for (i = 0; /* void */; i++) {
297             cv = nxt_conf_next_object_member(c->targets, &name, &next);
298             if (cv == NULL) {
299                 break;
300             }
301 
302             ret = nxt_python_set_target(task, &targets->target[i], cv);
303             if (nxt_slow_path(ret != NXT_OK)) {
304                 goto fail;
305             }
306         }
307 
308     } else {
309         ret = nxt_python_set_target(task, &targets->target[0], app_conf->self);
310         if (nxt_slow_path(ret != NXT_OK)) {
311             goto fail;
312         }
313     }
314 
315     nxt_unit_default_init(task, &python_init, data->app);
316 
317     python_init.data = c;
318     python_init.callbacks.ready_handler = nxt_python_ready_handler;
319 
320     proto = c->protocol;
321 
322     if (proto.length == 0) {
323         proto = nxt_python_asgi_check(targets->target[0].application)
324                 ? asgi : wsgi;
325 
326         for (i = 1; i < targets->count; i++) {
327             probe_proto = nxt_python_asgi_check(targets->target[i].application)
328                           ? asgi : wsgi;
329             if (probe_proto.start != proto.start) {
330                 nxt_alert(task, "A mix of ASGI & WSGI targets is forbidden, "
331                                 "specify protocol in config if incorrect");
332                 goto fail;
333             }
334         }
335     }
336 
337     if (nxt_strstr_eq(&proto, &asgi)) {
338         rc = nxt_python_asgi_init(&python_init, &nxt_py_proto);
339 
340     } else {
341         rc = nxt_python_wsgi_init(&python_init, &nxt_py_proto);
342     }
343 
344     if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
345         goto fail;
346     }
347 
348     rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data, 1);
349     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
350         goto fail;
351     }
352 
353     rc = nxt_python_init_threads(c);
354     if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
355         goto fail;
356     }
357 
358     if (nxt_py_proto.startup != NULL) {
359         if (nxt_py_proto.startup(python_init.ctx_data) != NXT_UNIT_OK) {
360             goto fail;
361         }
362     }
363 
364     unit_ctx = nxt_unit_init(&python_init);
365     if (nxt_slow_path(unit_ctx == NULL)) {
366         goto fail;
367     }
368 
369     rc = nxt_py_proto.run(unit_ctx);
370 
371     nxt_python_join_threads(unit_ctx, c);
372 
373     nxt_unit_done(unit_ctx);
374 
375     nxt_py_proto.ctx_data_free(python_init.ctx_data);
376 
377     nxt_python_atexit();
378 
379     exit(rc);
380 
381     return NXT_OK;
382 
383 fail:
384 
385     nxt_python_join_threads(NULL, c);
386 
387     if (python_init.ctx_data != NULL) {
388         nxt_py_proto.ctx_data_free(python_init.ctx_data);
389     }
390 
391     Py_XDECREF(obj);
392 
393     nxt_python_atexit();
394 
395     return NXT_ERROR;
396 }
397 
398 
399 static nxt_int_t
nxt_python_set_target(nxt_task_t * task,nxt_python_target_t * target,nxt_conf_value_t * conf)400 nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target,
401     nxt_conf_value_t *conf)
402 {
403     char              *callable, *module_name;
404     PyObject          *module, *obj;
405     nxt_str_t         str;
406     nxt_conf_value_t  *value;
407 
408     static nxt_str_t  module_str = nxt_string("module");
409     static nxt_str_t  callable_str = nxt_string("callable");
410     static nxt_str_t  prefix_str = nxt_string("prefix");
411 
412     module = obj = NULL;
413 
414     value = nxt_conf_get_object_member(conf, &module_str, NULL);
415     if (nxt_slow_path(value == NULL)) {
416         goto fail;
417     }
418 
419     nxt_conf_get_string(value, &str);
420 
421     module_name = nxt_alloca(str.length + 1);
422     nxt_memcpy(module_name, str.start, str.length);
423     module_name[str.length] = '\0';
424 
425     module = PyImport_ImportModule(module_name);
426     if (nxt_slow_path(module == NULL)) {
427         nxt_alert(task, "Python failed to import module \"%s\"", module_name);
428         nxt_python_print_exception();
429         goto fail;
430     }
431 
432     value = nxt_conf_get_object_member(conf, &callable_str, NULL);
433     if (value == NULL) {
434         callable = nxt_alloca(12);
435         nxt_memcpy(callable, "application", 12);
436 
437     } else {
438         nxt_conf_get_string(value, &str);
439 
440         callable = nxt_alloca(str.length + 1);
441         nxt_memcpy(callable, str.start, str.length);
442         callable[str.length] = '\0';
443     }
444 
445     obj = PyDict_GetItemString(PyModule_GetDict(module), callable);
446     if (nxt_slow_path(obj == NULL)) {
447         nxt_alert(task, "Python failed to get \"%s\" from module \"%s\"",
448                   callable, module_name);
449         goto fail;
450     }
451 
452     if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
453         nxt_alert(task, "\"%s\" in module \"%s\" is not a callable object",
454                   callable, module_name);
455         goto fail;
456     }
457 
458     value = nxt_conf_get_object_member(conf, &prefix_str, NULL);
459     if (nxt_slow_path(nxt_python_set_prefix(task, target, value) != NXT_OK)) {
460         goto fail;
461     }
462 
463     target->application = obj;
464     obj = NULL;
465 
466     Py_INCREF(target->application);
467     Py_CLEAR(module);
468 
469     return NXT_OK;
470 
471 fail:
472 
473     Py_XDECREF(obj);
474     Py_XDECREF(module);
475 
476     return NXT_ERROR;
477 }
478 
479 
480 nxt_inline nxt_int_t
nxt_python_set_prefix(nxt_task_t * task,nxt_python_target_t * target,nxt_conf_value_t * value)481 nxt_python_set_prefix(nxt_task_t *task, nxt_python_target_t *target,
482     nxt_conf_value_t *value)
483 {
484     u_char            *prefix;
485     nxt_str_t         str;
486 
487     if (value == NULL) {
488         return NXT_OK;
489     }
490 
491     nxt_conf_get_string(value, &str);
492 
493     if (str.length == 0) {
494         return NXT_OK;
495     }
496 
497     if (str.start[str.length - 1] == '/') {
498         str.length--;
499     }
500     target->prefix.length = str.length;
501     prefix = nxt_malloc(str.length);
502     if (nxt_slow_path(prefix == NULL)) {
503         nxt_alert(task, "Failed to allocate target prefix string");
504         return NXT_ERROR;
505     }
506 
507     target->py_prefix = PyString_FromStringAndSize((char *)str.start,
508                                                     str.length);
509     if (nxt_slow_path(target->py_prefix == NULL)) {
510         nxt_free(prefix);
511         nxt_alert(task, "Python failed to allocate target prefix "
512                         "string");
513         return NXT_ERROR;
514     }
515     nxt_memcpy(prefix, str.start, str.length);
516     target->prefix.start = prefix;
517 
518     return NXT_OK;
519 }
520 
521 
522 static nxt_int_t
nxt_python_set_path(nxt_task_t * task,nxt_conf_value_t * value)523 nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value)
524 {
525     int               ret;
526     PyObject          *path, *sys;
527     nxt_str_t         str;
528     nxt_uint_t        n;
529     nxt_conf_value_t  *array;
530 
531     if (value == NULL) {
532         return NXT_OK;
533     }
534 
535     sys = PySys_GetObject((char *) "path");
536     if (nxt_slow_path(sys == NULL)) {
537         nxt_alert(task, "Python failed to get \"sys.path\" list");
538         return NXT_ERROR;
539     }
540 
541     /* sys is a Borrowed reference. */
542 
543     array = value;
544     n = nxt_conf_array_elements_count_or_1(array);
545 
546     while (n != 0) {
547         n--;
548 
549         /*
550          * Insertion in front of existing paths starting from the last element
551          * to preserve original order while giving priority to the values
552          * specified in the "path" option.
553          */
554 
555         value = nxt_conf_get_array_element_or_itself(array, n);
556 
557         nxt_conf_get_string(value, &str);
558 
559         path = PyString_FromStringAndSize((char *) str.start, str.length);
560         if (nxt_slow_path(path == NULL)) {
561             nxt_alert(task, "Python failed to create string object \"%V\"",
562                       &str);
563             return NXT_ERROR;
564         }
565 
566         ret = PyList_Insert(sys, 0, path);
567 
568         Py_DECREF(path);
569 
570         if (nxt_slow_path(ret != 0)) {
571             nxt_alert(task, "Python failed to insert \"%V\" into \"sys.path\"",
572                       &str);
573             return NXT_ERROR;
574         }
575     }
576 
577     return NXT_OK;
578 }
579 
580 
581 static int
nxt_python_init_threads(nxt_python_app_conf_t * c)582 nxt_python_init_threads(nxt_python_app_conf_t *c)
583 {
584     int                    res;
585     uint32_t               i;
586     nxt_py_thread_info_t   *ti;
587     static pthread_attr_t  attr;
588 
589     if (c->threads <= 1) {
590         return NXT_UNIT_OK;
591     }
592 
593     if (c->thread_stack_size > 0) {
594         res = pthread_attr_init(&attr);
595         if (nxt_slow_path(res != 0)) {
596             nxt_unit_alert(NULL, "thread attr init failed: %s (%d)",
597                            strerror(res), res);
598 
599             return NXT_UNIT_ERROR;
600         }
601 
602         res = pthread_attr_setstacksize(&attr, c->thread_stack_size);
603         if (nxt_slow_path(res != 0)) {
604             nxt_unit_alert(NULL, "thread attr set stack size failed: %s (%d)",
605                            strerror(res), res);
606 
607             return NXT_UNIT_ERROR;
608         }
609 
610         nxt_py_thread_attr = &attr;
611     }
612 
613     nxt_py_threads = nxt_unit_malloc(NULL, sizeof(nxt_py_thread_info_t)
614                                            * (c->threads - 1));
615     if (nxt_slow_path(nxt_py_threads == NULL)) {
616         nxt_unit_alert(NULL, "Failed to allocate thread info array");
617 
618         return NXT_UNIT_ERROR;
619     }
620 
621     memset(nxt_py_threads, 0, sizeof(nxt_py_thread_info_t) * (c->threads - 1));
622 
623     for (i = 0; i < c->threads - 1; i++) {
624         ti = &nxt_py_threads[i];
625 
626         res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data, 0);
627         if (nxt_slow_path(res != NXT_UNIT_OK)) {
628             return NXT_UNIT_ERROR;
629         }
630     }
631 
632     return NXT_UNIT_OK;
633 }
634 
635 
636 static int
nxt_python_ready_handler(nxt_unit_ctx_t * ctx)637 nxt_python_ready_handler(nxt_unit_ctx_t *ctx)
638 {
639     int                    res;
640     uint32_t               i;
641     nxt_py_thread_info_t   *ti;
642     nxt_python_app_conf_t  *c;
643 
644     c = ctx->unit->data;
645 
646     if (c->threads <= 1) {
647         return NXT_UNIT_OK;
648     }
649 
650     for (i = 0; i < c->threads - 1; i++) {
651         ti = &nxt_py_threads[i];
652 
653         ti->ctx = ctx;
654 
655         res = pthread_create(&ti->thread, nxt_py_thread_attr,
656                              nxt_python_thread_func, ti);
657 
658         if (nxt_fast_path(res == 0)) {
659             nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1));
660 
661         } else {
662             nxt_unit_alert(ctx, "thread #%d create failed: %s (%d)",
663                            (int) (i + 1), strerror(res), res);
664         }
665     }
666 
667     return NXT_UNIT_OK;
668 }
669 
670 
671 static void *
nxt_python_thread_func(void * data)672 nxt_python_thread_func(void *data)
673 {
674     nxt_unit_ctx_t        *ctx;
675     PyGILState_STATE      gstate;
676     nxt_py_thread_info_t  *ti;
677 
678     ti = data;
679 
680     nxt_unit_debug(ti->ctx, "worker thread #%d start",
681                    (int) (ti - nxt_py_threads + 1));
682 
683     gstate = PyGILState_Ensure();
684 
685     if (nxt_py_proto.startup != NULL) {
686         if (nxt_py_proto.startup(ti->ctx_data) != NXT_UNIT_OK) {
687             goto fail;
688         }
689     }
690 
691     ctx = nxt_unit_ctx_alloc(ti->ctx, ti->ctx_data);
692     if (nxt_slow_path(ctx == NULL)) {
693         goto fail;
694     }
695 
696     (void) nxt_py_proto.run(ctx);
697 
698     nxt_unit_done(ctx);
699 
700 fail:
701 
702     PyGILState_Release(gstate);
703 
704     nxt_unit_debug(NULL, "worker thread #%d end",
705                    (int) (ti - nxt_py_threads + 1));
706 
707     return NULL;
708 }
709 
710 
711 static void
nxt_python_join_threads(nxt_unit_ctx_t * ctx,nxt_python_app_conf_t * c)712 nxt_python_join_threads(nxt_unit_ctx_t *ctx, nxt_python_app_conf_t *c)
713 {
714     int                   res;
715     uint32_t              i;
716     PyThreadState         *thread_state;
717     nxt_py_thread_info_t  *ti;
718 
719     if (nxt_py_threads == NULL) {
720         return;
721     }
722 
723     thread_state = PyEval_SaveThread();
724 
725     for (i = 0; i < c->threads - 1; i++) {
726         ti = &nxt_py_threads[i];
727 
728         if ((uintptr_t) ti->thread == 0) {
729             continue;
730         }
731 
732         res = pthread_join(ti->thread, NULL);
733 
734         if (nxt_fast_path(res == 0)) {
735             nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1));
736 
737         } else {
738             nxt_unit_alert(ctx, "thread #%d join failed: %s (%d)",
739                            (int) (i + 1), strerror(res), res);
740         }
741     }
742 
743     PyEval_RestoreThread(thread_state);
744 
745     for (i = 0; i < c->threads - 1; i++) {
746         ti = &nxt_py_threads[i];
747 
748         if (ti->ctx_data != NULL) {
749             nxt_py_proto.ctx_data_free(ti->ctx_data);
750         }
751     }
752 
753     nxt_unit_free(NULL, nxt_py_threads);
754 }
755 
756 
757 int
nxt_python_init_strings(nxt_python_string_t * pstr)758 nxt_python_init_strings(nxt_python_string_t *pstr)
759 {
760     PyObject  *obj;
761 
762     while (pstr->string.start != NULL) {
763         obj = PyString_FromStringAndSize((char *) pstr->string.start,
764                                          pstr->string.length);
765         if (nxt_slow_path(obj == NULL)) {
766             return NXT_UNIT_ERROR;
767         }
768 
769         PyUnicode_InternInPlace(&obj);
770 
771         *pstr->object_p = obj;
772 
773         pstr++;
774     }
775 
776     return NXT_UNIT_OK;
777 }
778 
779 
780 void
nxt_python_done_strings(nxt_python_string_t * pstr)781 nxt_python_done_strings(nxt_python_string_t *pstr)
782 {
783     PyObject  *obj;
784 
785     while (pstr->string.start != NULL) {
786         obj = *pstr->object_p;
787 
788         Py_XDECREF(obj);
789         *pstr->object_p = NULL;
790 
791         pstr++;
792     }
793 }
794 
795 
796 static void
nxt_python_atexit(void)797 nxt_python_atexit(void)
798 {
799     nxt_int_t            i;
800     nxt_python_target_t  *target;
801 
802     if (nxt_py_proto.done != NULL) {
803         nxt_py_proto.done();
804     }
805 
806     Py_XDECREF(nxt_py_stderr_flush);
807 
808     if (nxt_py_targets != NULL) {
809         for (i = 0; i < nxt_py_targets->count; i++) {
810             target = &nxt_py_targets->target[i];
811 
812             Py_XDECREF(target->application);
813             Py_XDECREF(target->py_prefix);
814 
815             nxt_free(target->prefix.start);
816         }
817 
818         nxt_unit_free(NULL, nxt_py_targets);
819     }
820 
821     Py_Finalize();
822 
823     if (nxt_py_home != NULL) {
824         nxt_free(nxt_py_home);
825     }
826 }
827 
828 
829 void
nxt_python_print_exception(void)830 nxt_python_print_exception(void)
831 {
832     PyErr_Print();
833 
834 #if PY_MAJOR_VERSION == 3
835     /* The backtrace may be buffered in sys.stderr file object. */
836     {
837         PyObject  *result;
838 
839         result = PyObject_CallFunction(nxt_py_stderr_flush, NULL);
840         if (nxt_slow_path(result == NULL)) {
841             PyErr_Clear();
842             return;
843         }
844 
845         Py_DECREF(result);
846     }
847 #endif
848 }
849