Skip to content

Commit 27488fd

Browse files
committed
Rework initialization
1 parent 71b4bca commit 27488fd

File tree

2 files changed

+85
-85
lines changed

2 files changed

+85
-85
lines changed

src/_pythonscript.pyx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,18 @@ cdef api void _pythonscript_early_init() with gil:
193193
# print(f"PYTHONPATH: {sys.path}")
194194

195195

196-
cdef api void _pythonscript_deinitialize() with gil:
196+
cdef api void _pythonscript_initialize(int p_level) with gil:
197+
if p_level == GDEXTENSION_INITIALIZATION_SERVERS:
198+
pass
199+
200+
# Language registration must be done at `GDEXTENSION_INITIALIZATION_SERVERS`
201+
# level which is too early to have have everything we need for (e.g. `OS` singleton).
202+
# So we have to do another init step at `GDEXTENSION_INITIALIZATION_SCENE` level.
203+
if p_level == GDEXTENSION_INITIALIZATION_SCENE:
204+
pass
205+
206+
207+
cdef api void _pythonscript_deinitialize(int p_level) with gil:
197208
# TODO: unregister the language once https://github.com/godotengine/godot/pull/67155 is merged
198209

199210
if _pythons_script_language is not None:

src/pythonscript.c

Lines changed: 73 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,11 @@
4141

4242
typedef enum {
4343
STALLED, // Intitial state
44+
4445
ENTRYPOINT_CALLED, // pythonscript_init called
4546
ENTRYPOINT_RETURNED, // pythonscript_init returns
4647

47-
PYTHON_INTERPRETER_INIT,
48-
PYTHONSCRIPT_MODULE_INIT,
49-
PYTHONSCRIPT_INTERNAL_INIT,
50-
51-
// All set !
52-
READY,
53-
54-
// Teardown
55-
PYTHONSCRIPT_MODULE_TEARDOWN,
56-
PYTHON_INTERPRETER_TEARDOWN,
48+
PYTHON_INTERPRETER_READY,
5749

5850
CRASHED, // Something went wrong :'(
5951
} PythonscriptState;
@@ -100,23 +92,15 @@ DLL_EXPORT void pythonscript_gdstringname_delete(GDExtensionStringNamePtr ptr) {
10092
pythonscript_gdextension->print_warning(msg, __func__, __FILE__, __LINE__, false); \
10193
}
10294

103-
static void _late_initialize() {
104-
if (state == PYTHONSCRIPT_INTERNAL_INIT) {
105-
_pythonscript_late_init();
106-
state = READY;
107-
}
108-
}
109-
110-
static void _early_initialize() {
95+
// Initialize Python interpreter & godot
96+
static void _initialize_python() {
11197
if (state != ENTRYPOINT_RETURNED) {
11298
printf("Pythonscript: Invalid internal state (this should never happen !)\n");
11399
goto error;
114100
}
115101

116102
// Initialize CPython interpreter
117-
state = PYTHON_INTERPRETER_INIT;
118103

119-
PyStatus status;
120104
PyConfig config;
121105
PyConfig_InitIsolatedConfig(&config);
122106
config.configure_c_stdio = 1;
@@ -165,22 +149,24 @@ static void _early_initialize() {
165149
gdstring_destructor(gd_basedir_path);
166150

167151
// 4) Configure pythonhome with base dir
168-
status = PyConfig_SetBytesString(
169-
&config,
170-
&config.home,
171-
basedir_path
172-
);
173-
pythonscript_gdextension->mem_free(basedir_path);
174-
if (PyStatus_Exception(status)) {
175-
GD_PRINT_ERROR("Pythonscript: Cannot initialize Python interpreter");
176-
GD_PRINT_ERROR(status.err_msg);
177-
goto error;
152+
{
153+
PyStatus status = PyConfig_SetBytesString(
154+
&config,
155+
&config.home,
156+
basedir_path
157+
);
158+
pythonscript_gdextension->mem_free(basedir_path);
159+
if (PyStatus_Exception(status)) {
160+
GD_PRINT_ERROR("Pythonscript: Cannot initialize Python interpreter");
161+
GD_PRINT_ERROR(status.err_msg);
162+
goto error;
163+
}
178164
}
179165
}
180166

181167
// Set program name
182168
{
183-
status = PyConfig_SetBytesString(
169+
PyStatus status = PyConfig_SetBytesString(
184170
&config,
185171
&config.program_name,
186172
// TODO: retrieve real argv[0]
@@ -197,12 +183,14 @@ static void _early_initialize() {
197183
// This is much simpler this way given we will have acces to Godot API
198184
// through the nice Python bindings this way
199185

200-
/* Read all configuration at once */
201-
status = PyConfig_Read(&config);
202-
if (PyStatus_Exception(status)) {
203-
GD_PRINT_ERROR("Pythonscript: Cannot initialize Python interpreter");
204-
GD_PRINT_ERROR(status.err_msg);
205-
goto error;
186+
// Read all configuration at once
187+
{
188+
PyStatus status = PyConfig_Read(&config);
189+
if (PyStatus_Exception(status)) {
190+
GD_PRINT_ERROR("Pythonscript: Cannot initialize Python interpreter");
191+
GD_PRINT_ERROR(status.err_msg);
192+
goto error;
193+
}
206194
}
207195

208196
// TODO
@@ -214,11 +202,13 @@ static void _early_initialize() {
214202
// goto error;
215203
// }
216204

217-
status = Py_InitializeFromConfig(&config);
218-
if (PyStatus_Exception(status)) {
219-
GD_PRINT_ERROR("Pythonscript: Cannot initialize Python interpreter");
220-
GD_PRINT_ERROR(status.err_msg);
221-
goto error;
205+
{
206+
PyStatus status = Py_InitializeFromConfig(&config);
207+
if (PyStatus_Exception(status)) {
208+
GD_PRINT_ERROR("Pythonscript: Cannot initialize Python interpreter");
209+
GD_PRINT_ERROR(status.err_msg);
210+
goto error;
211+
}
222212
}
223213

224214
// // TODO: site.USER_SITE seems to point to an invalid location in ~/.local
@@ -234,78 +224,75 @@ static void _early_initialize() {
234224
PyRun_SimpleString("import sys\nprint('PYTHON_PATH:', sys.path)\n");
235225
#endif
236226

237-
state = PYTHONSCRIPT_MODULE_INIT;
238227

239-
int ret = import__pythonscript();
240-
if (ret != 0) {
241-
GD_PRINT_ERROR("Pythonscript: Cannot load Python module `_pythonscript`");
242-
goto error;
228+
{
229+
int ret = import__pythonscript();
230+
if (ret != 0) {
231+
GD_PRINT_ERROR("Pythonscript: Cannot load Python module `_pythonscript`");
232+
goto post_init_error;
233+
}
243234
}
244235

245-
state = PYTHONSCRIPT_INTERNAL_INIT;
246-
247-
_pythonscript_early_init();
248236

249237
PyConfig_Clear(&config);
250238

251239
// Release the Kraken... er I mean the GIL !
252240
gilstate = PyEval_SaveThread();
253241

242+
state = PYTHON_INTERPRETER_READY;
254243
return;
255244

256-
error:
257-
258-
if (state > PYTHONSCRIPT_MODULE_INIT && state < PYTHONSCRIPT_MODULE_TEARDOWN) {
259-
_pythonscript_deinitialize();
260-
}
261-
262-
if (state > PYTHON_INTERPRETER_INIT && state < PYTHON_INTERPRETER_TEARDOWN) {
263-
PyConfig_Clear(&config);
245+
post_init_error:
246+
PyConfig_Clear(&config);
247+
{
264248
int ret = Py_FinalizeEx();
265249
if (ret != 0) {
266250
GD_PRINT_ERROR("Pythonscript: Cannot finalize Python interpreter");
267251
}
268252
}
269253

254+
error:
270255
state = CRASHED;
271256
}
272257

273-
static void _deinitialize(void *userdata, GDExtensionInitializationLevel p_level) {
274-
(void) userdata; // acknowledge unreferenced parameter
275-
if (p_level != GDEXTENSION_INITIALIZATION_SERVERS) {
276-
return;
258+
static void _deinitialize_python() {
259+
if (state != PYTHON_INTERPRETER_READY) {
260+
printf("Pythonscript: Invalid internal state (this should never happen !)\n");
261+
goto error;
277262
}
278263

279-
if (state > PYTHON_INTERPRETER_INIT && state < PYTHON_INTERPRETER_TEARDOWN) {
280-
281-
// Re-acquire the gil in order to finalize properly
282-
PyEval_RestoreThread(gilstate);
283-
284-
if (state > PYTHONSCRIPT_MODULE_INIT && state < PYTHONSCRIPT_MODULE_TEARDOWN) {
285-
_pythonscript_deinitialize();
286-
}
264+
// Re-acquire the gil in order to finalize properly
265+
PyEval_RestoreThread(gilstate);
287266

288-
int ret = Py_FinalizeEx();
289-
if (ret != 0) {
290-
GD_PRINT_ERROR("Pythonscript: Cannot finalize Python interpreter");
291-
state = CRASHED;
292-
return;
293-
}
267+
int ret = Py_FinalizeEx();
268+
if (ret != 0) {
269+
GD_PRINT_ERROR("Pythonscript: Cannot finalize Python interpreter");
294270
}
295271

296272
state = STALLED;
273+
return;
274+
275+
error:
276+
state = CRASHED;
297277
}
298278

299279
static void _initialize(void *userdata, GDExtensionInitializationLevel p_level) {
300280
(void) userdata; // acknowledge unreferenced parameter
281+
if (state == ENTRYPOINT_RETURNED && p_level == GDEXTENSION_INITIALIZATION_CORE) {
282+
_initialize_python();
283+
}
284+
if (state != CRASHED) {
285+
_pythonscript_initialize(p_level);
286+
}
287+
}
301288

302-
// Language registration must be done at `GDEXTENSION_INITIALIZATION_SERVERS`
303-
// level which is too early to have have everything we need for (e.g. `OS` singleton).
304-
// So we have to do another init step at `GDEXTENSION_INITIALIZATION_SCENE` level.
305-
if (p_level == GDEXTENSION_INITIALIZATION_SERVERS) {
306-
_early_initialize();
307-
} else if (p_level == GDEXTENSION_INITIALIZATION_SCENE) {
308-
_late_initialize();
289+
static void _deinitialize(void *userdata, GDExtensionInitializationLevel p_level) {
290+
(void) userdata; // acknowledge unreferenced parameter
291+
if (state != CRASHED) {
292+
_pythonscript_deinitialize(p_level);
293+
}
294+
if (state == PYTHON_INTERPRETER_READY && p_level == GDEXTENSION_INITIALIZATION_CORE) {
295+
_deinitialize_python();
309296
}
310297
}
311298

@@ -381,7 +368,9 @@ DLL_EXPORT GDExtensionBool pythonscript_init(
381368
GD_PRINT_WARNING(buff);
382369
}
383370

384-
r_initialization->minimum_initialization_level = GDEXTENSION_INITIALIZATION_SERVERS;
371+
// Initialize as early as possible, this way we can have 3rd party plugins written
372+
// in Python/Cython that can do things at this level
373+
r_initialization->minimum_initialization_level = GDEXTENSION_INITIALIZATION_CORE;
385374
r_initialization->userdata = NULL;
386375
r_initialization->initialize = _initialize;
387376
r_initialization->deinitialize = _deinitialize;

0 commit comments

Comments
 (0)