1- from cpython.ref cimport Py_INCREF, Py_DECREF, PyObject
2- from .hazmat cimport gdapi, gdptrs, gdextension_interface
3- from .hazmat.gdtypes cimport *
4- from .builtins cimport *
1+ # This file is included by `builtins.pxd.j2`
52
63from enum import IntEnum
74import inspect
85from types import UnionType
96import dataclasses
107
118
12- # ####################################################################
13- # Godot classes exposed to Python #
14- # ####################################################################
9+ # #############################################################################
10+ # BaseGDObject #
11+ # #############################################################################
1512
1613
17- def __getattr__ (name: str ):
18- try :
19- return _load_class(name)
20- except RuntimeError :
21- raise AttributeError
14+ # BaseGDObject is the parent class of all Godot classes
15+ # Note it is defined here instead of in `classes.pyx` to avoid recursive import
2216
2317
2418cdef class BaseGDObject:
@@ -91,6 +85,11 @@ cdef class BaseGDObject:
9185 return wrapper
9286
9387
88+ # ####################################################################
89+ # Godot classes exposed to Python #
90+ # ####################################################################
91+
92+
9493cdef object _loaded_singletons = {}
9594cdef object _loaded_classes = {}
9695
@@ -106,14 +105,13 @@ cpdef BaseGDObject _load_singleton(str name):
106105 except KeyError :
107106 pass
108107
109- cdef object cls = _load_class(name)
110- cdef gd_string_name_t gdname = gdapi.gd_string_name_from_unchecked_pystr(name)
111- cdef gd_object_t gdobj = gdptrs.gdptr_global_get_singleton(& gdname)
112- gdapi.gd_string_name_del(& gdname)
108+ cdef StringName gd_name = StringName(name)
109+ cdef gd_object_t gdobj = gdptrs.gdptr_global_get_singleton(& gd_name._gd_data)
113110
114111 if gdobj == NULL :
115112 raise RuntimeError (f" Singleton `{name}` doesn't exist in Godot !" )
116113
114+ cdef object cls = _load_class(gd_name)
117115 cdef BaseGDObject singleton = < BaseGDObject> cls ._steal_from_ptr(< size_t> gdobj)
118116
119117 _loaded_singletons[name] = singleton
@@ -132,29 +130,24 @@ cdef inline object _meth_call(BaseGDObject obj, object name, object args):
132130 return _object_call(obj._gd_ptr, StringName(" call" ), [name, * args])
133131
134132
135- cdef object _load_class (str name):
136- try :
137- return _loaded_classes[name]
138- except KeyError :
139- pass
133+ cdef inline object _build_class_from_spec (str name, StringName gd_name ):
134+ # TODO: ClassDB won't be needed once method uses ptrcall
135+ # Load our good friend ClassDB
136+ cdef StringName gdname_classdb = StringName( " ClassDB " )
137+ cdef gd_object_t classdb = gdptrs.gdptr_global_get_singleton( & gdname_classdb._gd_data)
140138
141139 from godot import _classes_api
140+ cdef list spec
142141 try :
143142 spec = getattr (_classes_api, name)
144143 except AttributeError :
145144 raise RuntimeError (f" Class `{name}` doesn't exist in Godot !" )
146145
147- # TODO: ClassDB won't be needed once method uses ptrcall
148- # Load our good friend ClassDB
149- cdef StringName gdname_classdb = StringName(" ClassDB" )
150- cdef gd_object_t classdb = gdptrs.gdptr_global_get_singleton(& gdname_classdb._gd_data)
151-
152- cdef StringName gd_name = StringName(name)
153146 parent = spec[0 ]
154147 is_refcounted = spec[1 ]
155148 items_spec = iter (spec[2 :])
156149 if parent:
157- parent_cls = _load_class(parent)
150+ parent_cls = _load_class(StringName( parent) )
158151 bases = (parent_cls, )
159152 else :
160153 bases = (BaseGDObject, )
@@ -163,20 +156,23 @@ cdef object _load_class(str name):
163156
164157 if not is_refcounted and name == " RefCounted" :
165158
159+ def _gen ():
160+ cdef StringName gdstr_unreference = StringName(" unreference" )
161+
166162 def _del (self ):
167163 print (f' [DEBUG] {type(self).__name__}.__del__()' , flush= True )
168164 cdef BaseGDObject obj = < BaseGDObject> self
169- if _object_call(obj._gd_ptr, StringName( " unreference " ) , []):
165+ if _object_call(obj._gd_ptr, gdstr_unreference , []):
170166 gdptrs.gdptr_object_destroy(obj._gd_ptr)
171167 obj._gd_ptr = NULL
172168
173- attrs[" __del__" ] = _del
174-
175169 def _free (self ):
176170 print (f' [DEBUG] {type(self).__name__}.free()' , flush= True )
177171 raise RuntimeError (" RefCounted Godot object, cannot be freed" )
178172
179- attrs[" free" ] = _free
173+ return _del, _free
174+
175+ attrs[" __del__" ], attrs[" free" ] = _gen()
180176
181177 while True :
182178 try :
@@ -293,9 +289,18 @@ cdef object _load_class(str name):
293289 # `Object` defines a `free`, but it doesn't work properly (instead we rely on `BaseGDObject.free`)
294290 attrs.pop(" free" , None )
295291
296- cdef object klass = type (name, bases, attrs)
292+ return type (name, bases, attrs)
293+
294+
295+ cpdef object _load_class(StringName gd_name):
296+ try :
297+ return _loaded_classes[gd_name]
298+ except KeyError :
299+ pass
300+
301+ cdef object klass = _build_class_from_spec(str (gd_name), gd_name)
297302
298- _loaded_classes[name ] = klass
303+ _loaded_classes[gd_name ] = klass
299304 return klass
300305
301306
0 commit comments