• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* PyInterpreterConfig API */
2 
3 #include "Python.h"
4 #include "pycore_pylifecycle.h"
5 
6 #include <stdbool.h>
7 
8 #include "config_common.h"
9 
10 
11 static const char *
gil_flag_to_str(int flag)12 gil_flag_to_str(int flag)
13 {
14     switch (flag) {
15     case PyInterpreterConfig_DEFAULT_GIL:
16         return "default";
17     case PyInterpreterConfig_SHARED_GIL:
18         return "shared";
19     case PyInterpreterConfig_OWN_GIL:
20         return "own";
21     default:
22         PyErr_SetString(PyExc_SystemError,
23                         "invalid interpreter config 'gil' value");
24         return NULL;
25     }
26 }
27 
28 static int
gil_flag_from_str(const char * str,int * p_flag)29 gil_flag_from_str(const char *str, int *p_flag)
30 {
31     int flag;
32     if (str == NULL) {
33         flag = PyInterpreterConfig_DEFAULT_GIL;
34     }
35     else if (strcmp(str, "default") == 0) {
36         flag = PyInterpreterConfig_DEFAULT_GIL;
37     }
38     else if (strcmp(str, "shared") == 0) {
39         flag = PyInterpreterConfig_SHARED_GIL;
40     }
41     else if (strcmp(str, "own") == 0) {
42         flag = PyInterpreterConfig_OWN_GIL;
43     }
44     else {
45         PyErr_Format(PyExc_ValueError,
46                      "unsupported interpreter config .gil value '%s'", str);
47         return -1;
48     }
49     *p_flag = flag;
50     return 0;
51 }
52 
53 PyObject *
_PyInterpreterConfig_AsDict(PyInterpreterConfig * config)54 _PyInterpreterConfig_AsDict(PyInterpreterConfig *config)
55 {
56     PyObject *dict = PyDict_New();
57     if (dict == NULL) {
58         return NULL;
59     }
60 
61 #define ADD(NAME, OBJ)                                              \
62         do {                                                        \
63             int res = PyDict_SetItemString(dict, NAME, (OBJ));      \
64             Py_DECREF(OBJ);                                         \
65             if (res < 0) {                                          \
66                 goto error;                                         \
67             }                                                       \
68         } while (0)
69 #define ADD_BOOL(FIELD) \
70         ADD(#FIELD, Py_NewRef(config->FIELD ? Py_True : Py_False))
71 #define ADD_STR(FIELD, STR)                                         \
72         do {                                                        \
73             if (STR == NULL) {                                      \
74                 goto error;                                         \
75             }                                                       \
76             PyObject *obj = PyUnicode_FromString(STR);              \
77             if (obj == NULL) {                                      \
78                 goto error;                                         \
79             }                                                       \
80             ADD(#FIELD, obj);                                       \
81         } while (0)
82 
83     ADD_BOOL(use_main_obmalloc);
84     ADD_BOOL(allow_fork);
85     ADD_BOOL(allow_exec);
86     ADD_BOOL(allow_threads);
87     ADD_BOOL(allow_daemon_threads);
88     ADD_BOOL(check_multi_interp_extensions);
89 
90     ADD_STR(gil, gil_flag_to_str(config->gil));
91 
92 #undef ADD_STR
93 #undef ADD_BOOL
94 #undef ADD
95 
96     return dict;
97 
98 error:
99     Py_DECREF(dict);
100     return NULL;
101 }
102 
103 static int
_config_dict_get_bool(PyObject * dict,const char * name,int * p_flag)104 _config_dict_get_bool(PyObject *dict, const char *name, int *p_flag)
105 {
106     PyObject *item;
107     if (_config_dict_get(dict, name, &item) < 0) {
108         return -1;
109     }
110     // For now we keep things strict, rather than using PyObject_IsTrue().
111     int flag = item == Py_True;
112     if (!flag && item != Py_False) {
113         Py_DECREF(item);
114         config_dict_invalid_type(name);
115         return -1;
116     }
117     Py_DECREF(item);
118     *p_flag = flag;
119     return 0;
120 }
121 
122 static int
_config_dict_copy_str(PyObject * dict,const char * name,char * buf,size_t bufsize)123 _config_dict_copy_str(PyObject *dict, const char *name,
124                       char *buf, size_t bufsize)
125 {
126     PyObject *item;
127     if (_config_dict_get(dict, name, &item) < 0) {
128         return -1;
129     }
130     if (!PyUnicode_Check(item)) {
131         Py_DECREF(item);
132         config_dict_invalid_type(name);
133         return -1;
134     }
135     strncpy(buf, PyUnicode_AsUTF8(item), bufsize-1);
136     buf[bufsize-1] = '\0';
137     Py_DECREF(item);
138     return 0;
139 }
140 
141 static int
interp_config_from_dict(PyObject * origdict,PyInterpreterConfig * config,bool missing_allowed)142 interp_config_from_dict(PyObject *origdict, PyInterpreterConfig *config,
143                         bool missing_allowed)
144 {
145     PyObject *dict = PyDict_New();
146     if (dict == NULL) {
147         return -1;
148     }
149     if (PyDict_Update(dict, origdict) < 0) {
150         goto error;
151     }
152 
153 #define CHECK(NAME)                                                 \
154     do {                                                            \
155         if (PyErr_Occurred()) {                                     \
156             goto error;                                             \
157         }                                                           \
158         else {                                                      \
159             if (!missing_allowed) {                                 \
160                 (void)config_dict_get(dict, NAME);                  \
161                 assert(PyErr_Occurred());                           \
162                 goto error;                                         \
163             }                                                       \
164         }                                                           \
165     } while (0)
166 #define COPY_BOOL(FIELD)                                            \
167     do {                                                            \
168         int flag;                                                   \
169         if (_config_dict_get_bool(dict, #FIELD, &flag) < 0) {       \
170             CHECK(#FIELD);                                          \
171         }                                                           \
172         else {                                                      \
173             config->FIELD = flag;                                   \
174             (void)PyDict_PopString(dict, #FIELD, NULL);             \
175         }                                                           \
176     } while (0)
177 
178     COPY_BOOL(use_main_obmalloc);
179     COPY_BOOL(allow_fork);
180     COPY_BOOL(allow_exec);
181     COPY_BOOL(allow_threads);
182     COPY_BOOL(allow_daemon_threads);
183     COPY_BOOL(check_multi_interp_extensions);
184 
185     // PyInterpreterConfig.gil
186     char buf[20];
187     if (_config_dict_copy_str(dict, "gil", buf, 20) < 0) {
188         CHECK("gil");
189     }
190     else {
191         int flag;
192         if (gil_flag_from_str(buf, &flag) < 0) {
193             goto error;
194         }
195         config->gil = flag;
196         (void)PyDict_PopString(dict, "gil", NULL);
197     }
198 
199 #undef COPY_BOOL
200 #undef CHECK
201 
202     Py_ssize_t unused = PyDict_GET_SIZE(dict);
203     if (unused == 1) {
204         PyErr_Format(PyExc_ValueError,
205                      "config dict has 1 extra item (%R)", dict);
206         goto error;
207     }
208     else if (unused > 0) {
209         PyErr_Format(PyExc_ValueError,
210                      "config dict has %d extra items (%R)", unused, dict);
211         goto error;
212     }
213 
214     Py_DECREF(dict);
215     return 0;
216 
217 error:
218     Py_DECREF(dict);
219     return -1;
220 }
221 
222 int
_PyInterpreterConfig_InitFromDict(PyInterpreterConfig * config,PyObject * dict)223 _PyInterpreterConfig_InitFromDict(PyInterpreterConfig *config, PyObject *dict)
224 {
225     if (!PyDict_Check(dict)) {
226         PyErr_SetString(PyExc_TypeError, "dict expected");
227         return -1;
228     }
229     if (interp_config_from_dict(dict, config, false) < 0) {
230         return -1;
231     }
232     return 0;
233 }
234 
235 int
_PyInterpreterConfig_UpdateFromDict(PyInterpreterConfig * config,PyObject * dict)236 _PyInterpreterConfig_UpdateFromDict(PyInterpreterConfig *config, PyObject *dict)
237 {
238     if (!PyDict_Check(dict)) {
239         PyErr_SetString(PyExc_TypeError, "dict expected");
240         return -1;
241     }
242     if (interp_config_from_dict(dict, config, true) < 0) {
243         return -1;
244     }
245     return 0;
246 }
247 
248 int
_PyInterpreterConfig_InitFromState(PyInterpreterConfig * config,PyInterpreterState * interp)249 _PyInterpreterConfig_InitFromState(PyInterpreterConfig *config,
250                                    PyInterpreterState *interp)
251 {
252     // Populate the config by re-constructing the values from the interpreter.
253     *config = (PyInterpreterConfig){
254 #define FLAG(flag) \
255         (interp->feature_flags & Py_RTFLAGS_ ## flag)
256         .use_main_obmalloc = FLAG(USE_MAIN_OBMALLOC),
257         .allow_fork = FLAG(FORK),
258         .allow_exec = FLAG(EXEC),
259         .allow_threads = FLAG(THREADS),
260         .allow_daemon_threads = FLAG(DAEMON_THREADS),
261         .check_multi_interp_extensions = FLAG(MULTI_INTERP_EXTENSIONS),
262 #undef FLAG
263         .gil = interp->ceval.own_gil
264             ? PyInterpreterConfig_OWN_GIL
265             : PyInterpreterConfig_SHARED_GIL,
266     };
267     return 0;
268 }
269