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