Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
touilleMan committed Dec 26, 2024
1 parent 3231075 commit 1fdded2
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 45 deletions.
99 changes: 56 additions & 43 deletions src/_pythonscript.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ from godot.hazmat.gdextension_interface cimport *
from godot.hazmat.gdapi cimport *
from godot.hazmat.extension_class cimport *
from godot.builtins cimport *
from godot.classes cimport _load_class, _load_singleton
from godot.classes cimport _load_class, _load_singleton, _cleanup_loaded_classes_and_singletons

include "_pythonscript_editor.pxi"
include "_pythonscript_extension_class_language.pxi"
Expand Down Expand Up @@ -131,6 +131,11 @@ cdef void _register_pythonscript_classes():
PythonScript._PythonScript__godot_extension_register_class()


cdef void _unregister_pythonscript_classes():
PythonScript._PythonScript__godot_extension_unregister_class()
PythonScriptLanguage._PythonScriptLanguage__godot_extension_unregister_class()


cdef void _customize_config():
import sys
ProjectSettings = _load_singleton("ProjectSettings")
Expand Down Expand Up @@ -279,9 +284,47 @@ cdef void _register_pythonscript_language():
return


cdef void _unregister_pythonscript_language():
global _pythons_script_language
cdef StringName gdname_engine
cdef StringName gdname_unregister_script_language
cdef GDExtensionObjectPtr singleton
cdef GDExtensionMethodBindPtr bind
cdef GDExtensionConstTypePtr[1] args
cdef gd_int_t ret

gdname_engine = StringName("Engine")
gdname_unregister_script_language = StringName("unregister_script_language")
singleton = pythonscript_gdextension.global_get_singleton(&gdname_engine._gd_data)
if singleton == NULL:
print("Failed to unregister Python from Godot: failed to retreive `Engine` singleton", flush=True)
return

bind = pythonscript_gdextension.classdb_get_method_bind(
&gdname_engine._gd_data,
&gdname_unregister_script_language._gd_data,
1850254898,
)
if bind == NULL:
print("Failed to unregister Python from Godot: failed to retreive `Engine::unregister_script_language`", flush=True)
return

args = [&_pythons_script_language._gd_ptr]
pythonscript_gdextension.object_method_bind_ptrcall(
bind,
singleton,
args,
&ret,
)
if ret != 0: # TODO: use `Error.Ok` here
print(f"Failed to unregister Python from Godot: `Engine::unregister_script_language` returned error {ret}", flush=True)
return

_pythons_script_language = None


cdef void _print_banner():
import sys
ProjectSettings = _load_singleton("ProjectSettings")

if _setup_config_entry("python/print_startup_info", True):
from godot._version import __version__ as pythonscript_version
Expand All @@ -293,15 +336,18 @@ cdef void _print_banner():


cdef public void _pythonscript_initialize(int p_level) noexcept with gil:
# TODO: re-enable me when the GD objects memory leak hunt is over
if p_level == GDEXTENSION_INITIALIZATION_SERVERS:
_register_pythonscript_classes()
pass

# Language registration must be done at `GDEXTENSION_INITIALIZATION_SERVERS` level which
# is too early to have have everything we need for (e.g. `ClassDB` & `OS` singletons).
# So we have to do another init step at `GDEXTENSION_INITIALIZATION_SCENE` level.
if p_level == GDEXTENSION_INITIALIZATION_SCENE:
_customize_config()
_register_pythonscript_language()
# TODO: re-enable me when the GD objects memory leak hunt is over
# _register_pythonscript_language()
# Finally proudly print banner ;-)
_print_banner()

Expand All @@ -317,52 +363,19 @@ cdef public void _pythonscript_deinitialize(int p_level) noexcept with gil:
# That will continue until `godot_gdnative_terminate` is called (which is
# responsible for the actual teardown of the interpreter).

cdef GDExtensionObjectPtr singleton
cdef GDExtensionMethodBindPtr bind
cdef GDExtensionConstTypePtr[1] args
cdef StringName gdname_engine
cdef StringName gdname_register_script_language
cdef gd_int_t ret

if p_level >= GDEXTENSION_INITIALIZATION_SCENE:
_deinitialize_callback_hook(p_level)

if p_level == GDEXTENSION_INITIALIZATION_SCENE and _pythons_script_language is not None:

# Unregister Python from Godot

gdname_engine = StringName("Engine")
gdname_unregister_script_language = StringName("unregister_script_language")
singleton = pythonscript_gdextension.global_get_singleton(&gdname_engine._gd_data)
if singleton == NULL:
print("Failed to unregister Python from Godot: failed to retreive `Engine` singleton", flush=True)
return

bind = pythonscript_gdextension.classdb_get_method_bind(
&gdname_engine._gd_data,
&gdname_unregister_script_language._gd_data,
1850254898,
)
if bind == NULL:
print("Failed to unregister Python from Godot: failed to retreive `Engine::unregister_script_language`", flush=True)
return

args = [&_pythons_script_language._gd_ptr]
pythonscript_gdextension.object_method_bind_ptrcall(
bind,
singleton,
args,
&ret,
)
if ret != 0: # TODO: use `Error.Ok` here
print(f"Failed to unregister Python from Godot: `Engine::unregister_script_language` returned error {ret}", flush=True)
return

_pythons_script_language = None
# _unregister_pythonscript_language()
pass

if p_level == GDEXTENSION_INITIALIZATION_SERVERS:

# Unregister Python classes from Godot's classDB

PythonScript._PythonScript__godot_extension_unregister_class()
PythonScriptLanguage._PythonScriptLanguage__godot_extension_unregister_class()
_unregister_pythonscript_classes()
_cleanup_loaded_classes_and_singletons()
gc_protector = _get_extension_gc_protector()
print('!!!!!!!! gc_protector', repr(gc_protector))
gc_protector.clear()
3 changes: 3 additions & 0 deletions src/godot/builtins_pyx/conversion.pyx.j2
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ cdef object gd_variant_steal_into_pyobj(const gd_variant_t *gdvar):
cdef inline object _gd_variant_steal_into_pyobj_{{ builtin.cy_type }}(const gd_variant_t *gdvar):
cdef {{ builtin.cy_type }} ret = {{ builtin.cy_type }}.__new__({{ builtin.cy_type }})
ret._gd_data = {{ builtin.c_name_prefix }}_from_variant(<gd_variant_t *>gdvar)
gd_variant_del(gdvar)
return ret
{% endfor %}

Expand All @@ -111,6 +112,7 @@ cdef object gd_variant_copy_into_pyobj(const gd_variant_t *gdvar):
#########################################################################


# TODO rename given stealing is not possible with GDExtension C++ orientated API
cdef bint gd_variant_steal_from_pyobj(object pyobj, gd_variant_t *gdvar):
if pyobj is None:
pythonscript_gdextension.variant_new_nil(gdvar)
Expand Down Expand Up @@ -138,5 +140,6 @@ cdef bint gd_variant_steal_from_pyobj(object pyobj, gd_variant_t *gdvar):
cdef inline void _gd_variant_steal_from_pyobj_pystr(object pyobj, gd_variant_t *gdvar):
cdef gd_string_t gdstr = gd_string_from_unchecked_pystr(pyobj)
gdvar[0] = gd_string_into_variant(&gdstr)
gd_string_del(&gdstr)
# into conversion steals the ownership, so don't need to call `gd_string_del(gdstr)`
{% endmacro %}
1 change: 1 addition & 0 deletions src/godot/classes.pxd.j2
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,5 @@ cdef class {{ cls.cy_type }}({{ cls.inherits.cy_type if cls.inherits else "" }})

cdef object _load_class(str name)
cdef object _load_singleton(str name)
cdef void _cleanup_loaded_classes_and_singletons()
cdef object _object_call(gd_object_t obj, str meth, args)
14 changes: 12 additions & 2 deletions src/godot/classes.pyx.j2
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ cdef object _loaded_singletons = {}
cdef object _loaded_classes = {}


cdef void _cleanup_loaded_classes_and_singletons():
_loaded_singletons.clear()
_loaded_classes.clear()


cdef object _load_singleton(str name):
try:
return _loaded_singletons[name]
Expand Down Expand Up @@ -186,6 +191,7 @@ cdef object _load_class(str name):


cdef object _object_call(GDExtensionObjectPtr obj, str meth, args):
cdef object pyret
cdef gd_variant_t ret
cdef GDExtensionCallError call_error

Expand All @@ -206,6 +212,8 @@ cdef object _object_call(GDExtensionObjectPtr obj, str meth, args):
# TODO: provide a helper for string name from Python str creation
cdef gd_string_name_t meth_gdstrname = gd_string_name_from_unchecked_pystr(meth)
variant_args[0] = gd_string_name_into_variant(&meth_gdstrname)
gd_string_name_del(&meth_gdstrname)
# TODO: rename !
# Into conversion steals the owneship, so no need to delete meth_gdstrname

for i, arg in enumerate(args, 1):
Expand All @@ -222,10 +230,12 @@ cdef object _object_call(GDExtensionObjectPtr obj, str meth, args):
&ret,
&call_error,
)
gd_variant_del(&variant_args[0]) # Only param we created without stealing ownership
for i in range(args_with_meth_len):
gd_variant_del(&variant_args[i])
# gd_variant_del(&variant_args[0]) # Only param we created without stealing ownership
if call_error.error == GDEXTENSION_CALL_OK:
return gd_variant_steal_into_pyobj(&ret)
# No need to destroy ret given the conversion has stolen ownership on data !
return gd_variant_steal_into_pyobj(&ret)

# TODO: improve ret error raised exception type ?
elif call_error.error == GDEXTENSION_CALL_ERROR_INVALID_METHOD:
Expand Down

0 comments on commit 1fdded2

Please sign in to comment.