| 
									
										
										
										
											2024-04-02 14:35:52 -06:00
										 |  |  | /* PyInterpreterConfig API */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "Python.h"
 | 
					
						
							| 
									
										
										
										
											2025-03-17 09:19:04 +00:00
										 |  |  | #include "pycore_interp.h" // Py_RTFLAGS
 | 
					
						
							| 
									
										
										
										
											2024-04-02 14:35:52 -06:00
										 |  |  | #include "pycore_pylifecycle.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <stdbool.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "config_common.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char * | 
					
						
							|  |  |  | gil_flag_to_str(int flag) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     switch (flag) { | 
					
						
							|  |  |  |     case PyInterpreterConfig_DEFAULT_GIL: | 
					
						
							|  |  |  |         return "default"; | 
					
						
							|  |  |  |     case PyInterpreterConfig_SHARED_GIL: | 
					
						
							|  |  |  |         return "shared"; | 
					
						
							|  |  |  |     case PyInterpreterConfig_OWN_GIL: | 
					
						
							|  |  |  |         return "own"; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_SystemError, | 
					
						
							|  |  |  |                         "invalid interpreter config 'gil' value"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | gil_flag_from_str(const char *str, int *p_flag) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int flag; | 
					
						
							|  |  |  |     if (str == NULL) { | 
					
						
							|  |  |  |         flag = PyInterpreterConfig_DEFAULT_GIL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (strcmp(str, "default") == 0) { | 
					
						
							|  |  |  |         flag = PyInterpreterConfig_DEFAULT_GIL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (strcmp(str, "shared") == 0) { | 
					
						
							|  |  |  |         flag = PyInterpreterConfig_SHARED_GIL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (strcmp(str, "own") == 0) { | 
					
						
							|  |  |  |         flag = PyInterpreterConfig_OWN_GIL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         PyErr_Format(PyExc_ValueError, | 
					
						
							|  |  |  |                      "unsupported interpreter config .gil value '%s'", str); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *p_flag = flag; | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyObject * | 
					
						
							|  |  |  | _PyInterpreterConfig_AsDict(PyInterpreterConfig *config) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *dict = PyDict_New(); | 
					
						
							|  |  |  |     if (dict == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define ADD(NAME, OBJ)                                              \
 | 
					
						
							|  |  |  |         do {                                                        \ | 
					
						
							|  |  |  |             int res = PyDict_SetItemString(dict, NAME, (OBJ));      \ | 
					
						
							|  |  |  |             Py_DECREF(OBJ);                                         \ | 
					
						
							|  |  |  |             if (res < 0) {                                          \ | 
					
						
							|  |  |  |                 goto error;                                         \ | 
					
						
							|  |  |  |             }                                                       \ | 
					
						
							|  |  |  |         } while (0) | 
					
						
							|  |  |  | #define ADD_BOOL(FIELD) \
 | 
					
						
							|  |  |  |         ADD(#FIELD, Py_NewRef(config->FIELD ? Py_True : Py_False)) | 
					
						
							|  |  |  | #define ADD_STR(FIELD, STR)                                         \
 | 
					
						
							|  |  |  |         do {                                                        \ | 
					
						
							|  |  |  |             if (STR == NULL) {                                      \ | 
					
						
							|  |  |  |                 goto error;                                         \ | 
					
						
							|  |  |  |             }                                                       \ | 
					
						
							|  |  |  |             PyObject *obj = PyUnicode_FromString(STR);              \ | 
					
						
							|  |  |  |             if (obj == NULL) {                                      \ | 
					
						
							|  |  |  |                 goto error;                                         \ | 
					
						
							|  |  |  |             }                                                       \ | 
					
						
							|  |  |  |             ADD(#FIELD, obj);                                       \ | 
					
						
							|  |  |  |         } while (0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ADD_BOOL(use_main_obmalloc); | 
					
						
							|  |  |  |     ADD_BOOL(allow_fork); | 
					
						
							|  |  |  |     ADD_BOOL(allow_exec); | 
					
						
							|  |  |  |     ADD_BOOL(allow_threads); | 
					
						
							|  |  |  |     ADD_BOOL(allow_daemon_threads); | 
					
						
							|  |  |  |     ADD_BOOL(check_multi_interp_extensions); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ADD_STR(gil, gil_flag_to_str(config->gil)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #undef ADD_STR
 | 
					
						
							|  |  |  | #undef ADD_BOOL
 | 
					
						
							|  |  |  | #undef ADD
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return dict; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     Py_DECREF(dict); | 
					
						
							|  |  |  |     return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _config_dict_get_bool(PyObject *dict, const char *name, int *p_flag) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *item; | 
					
						
							|  |  |  |     if (_config_dict_get(dict, name, &item) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // For now we keep things strict, rather than using PyObject_IsTrue().
 | 
					
						
							|  |  |  |     int flag = item == Py_True; | 
					
						
							|  |  |  |     if (!flag && item != Py_False) { | 
					
						
							|  |  |  |         Py_DECREF(item); | 
					
						
							|  |  |  |         config_dict_invalid_type(name); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_DECREF(item); | 
					
						
							|  |  |  |     *p_flag = flag; | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _config_dict_copy_str(PyObject *dict, const char *name, | 
					
						
							|  |  |  |                       char *buf, size_t bufsize) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *item; | 
					
						
							|  |  |  |     if (_config_dict_get(dict, name, &item) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!PyUnicode_Check(item)) { | 
					
						
							|  |  |  |         Py_DECREF(item); | 
					
						
							|  |  |  |         config_dict_invalid_type(name); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     strncpy(buf, PyUnicode_AsUTF8(item), bufsize-1); | 
					
						
							|  |  |  |     buf[bufsize-1] = '\0'; | 
					
						
							|  |  |  |     Py_DECREF(item); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | interp_config_from_dict(PyObject *origdict, PyInterpreterConfig *config, | 
					
						
							|  |  |  |                         bool missing_allowed) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *dict = PyDict_New(); | 
					
						
							|  |  |  |     if (dict == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (PyDict_Update(dict, origdict) < 0) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CHECK(NAME)                                                 \
 | 
					
						
							|  |  |  |     do {                                                            \ | 
					
						
							|  |  |  |         if (PyErr_Occurred()) {                                     \ | 
					
						
							|  |  |  |             goto error;                                             \ | 
					
						
							|  |  |  |         }                                                           \ | 
					
						
							|  |  |  |         else {                                                      \ | 
					
						
							|  |  |  |             if (!missing_allowed) {                                 \ | 
					
						
							|  |  |  |                 (void)config_dict_get(dict, NAME);                  \ | 
					
						
							|  |  |  |                 assert(PyErr_Occurred());                           \ | 
					
						
							|  |  |  |                 goto error;                                         \ | 
					
						
							|  |  |  |             }                                                       \ | 
					
						
							|  |  |  |         }                                                           \ | 
					
						
							|  |  |  |     } while (0) | 
					
						
							|  |  |  | #define COPY_BOOL(FIELD)                                            \
 | 
					
						
							|  |  |  |     do {                                                            \ | 
					
						
							|  |  |  |         int flag;                                                   \ | 
					
						
							|  |  |  |         if (_config_dict_get_bool(dict, #FIELD, &flag) < 0) {       \ | 
					
						
							|  |  |  |             CHECK(#FIELD);                                          \ | 
					
						
							|  |  |  |         }                                                           \ | 
					
						
							|  |  |  |         else {                                                      \ | 
					
						
							|  |  |  |             config->FIELD = flag;                                   \ | 
					
						
							|  |  |  |             (void)PyDict_PopString(dict, #FIELD, NULL);             \ | 
					
						
							|  |  |  |         }                                                           \ | 
					
						
							|  |  |  |     } while (0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     COPY_BOOL(use_main_obmalloc); | 
					
						
							|  |  |  |     COPY_BOOL(allow_fork); | 
					
						
							|  |  |  |     COPY_BOOL(allow_exec); | 
					
						
							|  |  |  |     COPY_BOOL(allow_threads); | 
					
						
							|  |  |  |     COPY_BOOL(allow_daemon_threads); | 
					
						
							|  |  |  |     COPY_BOOL(check_multi_interp_extensions); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // PyInterpreterConfig.gil
 | 
					
						
							|  |  |  |     char buf[20]; | 
					
						
							|  |  |  |     if (_config_dict_copy_str(dict, "gil", buf, 20) < 0) { | 
					
						
							|  |  |  |         CHECK("gil"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         int flag; | 
					
						
							|  |  |  |         if (gil_flag_from_str(buf, &flag) < 0) { | 
					
						
							|  |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         config->gil = flag; | 
					
						
							|  |  |  |         (void)PyDict_PopString(dict, "gil", NULL); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #undef COPY_BOOL
 | 
					
						
							|  |  |  | #undef CHECK
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Py_ssize_t unused = PyDict_GET_SIZE(dict); | 
					
						
							|  |  |  |     if (unused == 1) { | 
					
						
							|  |  |  |         PyErr_Format(PyExc_ValueError, | 
					
						
							|  |  |  |                      "config dict has 1 extra item (%R)", dict); | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (unused > 0) { | 
					
						
							|  |  |  |         PyErr_Format(PyExc_ValueError, | 
					
						
							|  |  |  |                      "config dict has %d extra items (%R)", unused, dict); | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-04-02 19:10:26 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Py_DECREF(dict); | 
					
						
							| 
									
										
										
										
											2024-04-02 14:35:52 -06:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     Py_DECREF(dict); | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyInterpreterConfig_InitFromDict(PyInterpreterConfig *config, PyObject *dict) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!PyDict_Check(dict)) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_TypeError, "dict expected"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (interp_config_from_dict(dict, config, false) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyInterpreterConfig_UpdateFromDict(PyInterpreterConfig *config, PyObject *dict) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!PyDict_Check(dict)) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_TypeError, "dict expected"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (interp_config_from_dict(dict, config, true) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyInterpreterConfig_InitFromState(PyInterpreterConfig *config, | 
					
						
							|  |  |  |                                    PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Populate the config by re-constructing the values from the interpreter.
 | 
					
						
							|  |  |  |     *config = (PyInterpreterConfig){ | 
					
						
							|  |  |  | #define FLAG(flag) \
 | 
					
						
							|  |  |  |         (interp->feature_flags & Py_RTFLAGS_ ## flag) | 
					
						
							|  |  |  |         .use_main_obmalloc = FLAG(USE_MAIN_OBMALLOC), | 
					
						
							|  |  |  |         .allow_fork = FLAG(FORK), | 
					
						
							|  |  |  |         .allow_exec = FLAG(EXEC), | 
					
						
							|  |  |  |         .allow_threads = FLAG(THREADS), | 
					
						
							|  |  |  |         .allow_daemon_threads = FLAG(DAEMON_THREADS), | 
					
						
							|  |  |  |         .check_multi_interp_extensions = FLAG(MULTI_INTERP_EXTENSIONS), | 
					
						
							|  |  |  | #undef FLAG
 | 
					
						
							|  |  |  |         .gil = interp->ceval.own_gil | 
					
						
							|  |  |  |             ? PyInterpreterConfig_OWN_GIL | 
					
						
							|  |  |  |             : PyInterpreterConfig_SHARED_GIL, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } |