Skip to content

Commit 693f179

Browse files
committed
Minimal draft of classes template
1 parent ecf3ad8 commit 693f179

File tree

6 files changed

+183
-9
lines changed

6 files changed

+183
-9
lines changed

β€Žsrc/godot/builtins.pxd.j2β€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ cdef class {{ builtin.cy_type }}
99

1010
from .hazmat.gdextension_interface cimport *
1111
from .hazmat.gdapi cimport *
12-
from .classes cimport BaseGDObject as GDObject
12+
from .classes cimport GDObject
1313

1414

1515
{{ render_all_conversions() }}

β€Žsrc/godot/classes.pxd.j2β€Ž

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,40 @@
11
cimport cython
22

3-
from .hazmat.gdapi cimport pythonscript_gdextension
4-
from .hazmat.gdextension_interface cimport GDExtensionObjectPtr
3+
from .hazmat.gdapi cimport pythonscript_gdextension, gd_object_t
54

65

76
cdef class BaseGDObject:
8-
cdef GDExtensionObjectPtr _gd_ptr
7+
cdef gd_object_t _gd_ptr
98

109
@staticmethod
11-
cdef BaseGDObject from_ptr(GDExtensionObjectPtr ptr)
10+
cdef BaseGDObject from_ptr(gd_object_t ptr)
1211

1312

1413
cdef inline gdobject_free(BaseGDObject obj):
1514
pythonscript_gdextension.object_destroy(obj._gd_ptr)
1615

1716

17+
{% for cls in api.classes %}
18+
cdef class {{ cls.cy_type }}({{ cls.inherits.cy_type if cls.inherits else "" }}):
19+
{% if cls.inherits is none %}
20+
cdef gd_object_t _gd_ptr
21+
{#
22+
23+
@staticmethod
24+
cdef inline Object cast_from_variant(const godot_variant_t *p_gdvar)
25+
26+
@staticmethod
27+
cdef inline Object cast_from_ptr(godot_object_t *ptr)
28+
#}
29+
{% endif %}
30+
@staticmethod
31+
cdef {{ cls.cy_type }} from_ptr(gd_object_t *_ptr)
32+
{% endfor %}
1833
{# {% for klass in api.classes %} #}
1934

2035
{# {# Cannot put this from_ptr as static method given cdef inline requires a final #} #}
2136
{# {# method (i.e. it cannot be overwritten by child class) #} #}
22-
{# cdef inline {{ klass.cy_type | lower }}_from_ptr(GDExtensionObjectPtr ptr): #}
37+
{# cdef inline {{ klass.cy_type | lower }}_from_ptr(gd_object_t ptr): #}
2338
{# # Call to __new__ bypasses __init__ constructor #}
2439
{# cdef {{ klass.cy_type }} wrapper = {{ klass.cy_type }}.__new__({{ klass.cy_type }}) #}
2540
{# wrapper._gd_ptr = ptr #}
@@ -32,13 +47,13 @@ cdef inline gdobject_free(BaseGDObject obj):
3247
{# #}
3348
{# cdef class {{ klass.cy_type }}({{ klass.inherits.cy_type if klass.inherits is not none else "" }}): #}
3449
{# {% if klass.inherits is none %} #}
35-
{# cdef GDExtensionObjectPtr _gd_ptr #}
50+
{# cdef gd_object_t _gd_ptr #}
3651
{# #}
3752
{# {# @staticmethod #}
3853
{# cdef inline GDObject cast_from_variant(const godot_variant *p_gdvar) #}
3954
{# #}
4055
{# @staticmethod #}
41-
{# cdef inline GDObject cast_from_ptr(GDExtensionObjectPtr *ptr): #}
56+
{# cdef inline GDObject cast_from_ptr(gd_object_t *ptr): #}
4257
{# self._gd_ptr = ptr #} #}
4358
{# {% else %} #}
4459
{# pass #}
@@ -48,4 +63,4 @@ cdef inline gdobject_free(BaseGDObject obj):
4863

4964
cdef object _load_class(str name)
5065
cdef object _load_singleton(str name)
51-
cdef object _object_call(GDExtensionObjectPtr obj, str meth, args)
66+
cdef object _object_call(gd_object_t obj, str meth, args)

β€Žsrc/godot/classes.pyx.j2β€Ž

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{%- from 'classes_pyx/class.pyx.j2' import render_class with context -%}
12
cimport cython
23

34
from .hazmat.gdapi cimport *
@@ -38,6 +39,24 @@ cdef class BaseGDObject:
3839
wrapper._set_gd_ptr(ptr)
3940
return wrapper
4041

42+
43+
# Classes
44+
45+
46+
# Forward declarations
47+
{% for cls in api.classes %}
48+
cdef class {{ cls.cy_type }}
49+
{% endfor %}
50+
{% for cls in api.classes %}
51+
52+
53+
{{ render_class(cls) }}
54+
{% endfor %}
55+
56+
57+
# Singletons
58+
59+
4160
{# {% for klass in api.classes %} #}
4261
{# #}
4362
{# #}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
{% macro render_class(cls) %}
2+
3+
{% if cls.inherits is none %}
4+
from cpython.object cimport PyObject_GenericGetAttr, PyObject_GenericSetAttr
5+
{% endif %}
6+
7+
cdef class {{ cls.cy_type }}({{ cls.inherits.cy_type if cls.inherits else "" }}):
8+
{% if cls.inherits is none %}
9+
10+
{# @staticmethod
11+
cpdef inline {{ cls.cy_type}} new():
12+
raise NotImplementedError("TODO :'(") #}
13+
14+
def free(self):
15+
pythonscript_gdextension.object_destroy(self._gd_ptr)
16+
self._gd_ptr = NULL
17+
18+
def __init__(self):
19+
raise RuntimeError(
20+
f"Use `new()` method to instantiate non-refcounted Godot object (and don't forget to free it !)"
21+
)
22+
23+
def __repr__(self):
24+
return f"<{type(self).__name__} wrapper on 0x{<size_t>self._gd_ptr:x}>"
25+
26+
def _set_gd_ptr(self, ptr):
27+
# /!\ doing `<GDExtensionObjectPtr>ptr` would return the address of
28+
# the PyObject instead of casting it value !
29+
self._gd_ptr = <GDExtensionObjectPtr><size_t>ptr
30+
# Note if the object is a reference, we stole it from the caller given we
31+
# don't call `Reference.reference` here
32+
33+
@classmethod
34+
def _from_ptr(cls, ptr):
35+
cdef object wrapper = cls.__new__(cls)
36+
wrapper._set_gd_ptr(ptr)
37+
return wrapper
38+
39+
{#
40+
@staticmethod
41+
cdef inline Object cast_from_variant(const godot_variant *p_gdvar):
42+
cdef godot_object *ptr = gdapi10.godot_variant_as_object(p_gdvar)
43+
# Retreive class
44+
cdef GDString classname = GDString.__new__(GDString)
45+
with nogil:
46+
gdapi10.godot_method_bind_ptrcall(
47+
__methbind__Object__get_class,
48+
ptr,
49+
NULL,
50+
&classname._gd_data
51+
)
52+
return globals()[str(classname)]._from_ptr(<size_t>ptr)
53+
54+
@staticmethod
55+
cdef inline Object cast_from_ptr(godot_object *ptr):
56+
# Retreive class
57+
cdef GDString classname = GDString.__new__(GDString)
58+
with nogil:
59+
gdapi10.godot_method_bind_ptrcall(
60+
__methbind__Object__get_class,
61+
ptr,
62+
NULL,
63+
&classname._gd_data
64+
)
65+
return globals()[str(classname)]._from_ptr(<size_t>ptr)
66+
#}
67+
68+
def __eq__(self, other):
69+
try:
70+
return self._gd_ptr == (<{{ cls.cy_type }}>other)._gd_ptr
71+
except TypeError:
72+
return False
73+
74+
def __ne__(self, other):
75+
try:
76+
return self._gd_ptr != (<{{ cls.cy_type }}>other)._gd_ptr
77+
except TypeError:
78+
return True
79+
80+
def __getattr__(self, name):
81+
cdef GDString gdname = GDString(name)
82+
cdef GDString gdnamefield = GDString("name")
83+
84+
# If a script is attached to the object, we expose here it methods
85+
if not hasattr(type(self), '__exposed_python_class'):
86+
if self.has_method(name):
87+
88+
def _call(*args):
89+
return {{ cls.cy_type }}.callv(self, gdname, GDArray(args))
90+
91+
return _call
92+
# from functools import partial
93+
# return partial(self.call, gdname)
94+
95+
elif any(x for x in self.get_property_list() if x[gdnamefield] == gdname):
96+
# TODO: Godot currently lacks a `has_property` method
97+
return self.get(gdname)
98+
99+
raise AttributeError(
100+
f"`{type(self).__name__}` object has no attribute `{name}`"
101+
)
102+
103+
def __setattr__(self, name, value):
104+
cdef GDString gdname = GDString(name)
105+
cdef GDString gdnamefield = GDString("name")
106+
107+
if hasattr(type(self), '__exposed_python_class'):
108+
PyObject_GenericSetAttr(self, name, value)
109+
return
110+
111+
# Could retrieve the item inside the Godot class, try to look into
112+
# the attached script if it has one
113+
else:
114+
if any(x for x in self.get_property_list() if x[gdnamefield] == gdname):
115+
# TODO: Godot currently lacks a `has_property` method
116+
self.set(name, value)
117+
return
118+
119+
raise AttributeError(
120+
f"`{type(self).__name__}` object has no attribute `{name}`"
121+
)
122+
123+
def call(self, name, *args):
124+
return self.callv(name, GDArray(args))
125+
{% else %}
126+
pass
127+
{% endif %}
128+
129+
@staticmethod
130+
cdef {{ cls.cy_type }} from_ptr(gd_object_t ptr):
131+
# Call to __new__ bypasses __init__ constructor
132+
cdef {{ cls.cy_type }} wrapper = {{ cls.cy_type }}.__new__({{ cls.cy_type }})
133+
wrapper._gd_ptr = ptr
134+
# Note if the object is a reference, we stole it from the caller given we
135+
# don't call `Reference.reference` here
136+
return wrapper
137+
138+
{% endmacro %}

β€Žsrc/godot/classes_pyx/method.pyx.j2β€Ž

Whitespace-only changes.

β€Žsrc/godot/meson.buildβ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ pyxclasses = custom_target(
149149
generate_tmpl_base_input,
150150
files(
151151
'classes.pyx.j2',
152+
'classes_pyx/class.pyx.j2',
153+
'classes_pyx/method.pyx.j2',
152154
),
153155
],
154156
command: generate_tmpl_cmd,

0 commit comments

Comments
Β (0)