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