Skip to content

Commit 232b211

Browse files
committed
Add support for static methods and __eq__ operator in classes
1 parent 9d57991 commit 232b211

File tree

3 files changed

+158
-8
lines changed

3 files changed

+158
-8
lines changed

src/godot/classes.pxd.j2

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,20 @@ cdef class BaseGDObject:
1616
@staticmethod
1717
cdef inline object cast_from_variant(const gd_variant_t *gdvar):
1818
cdef gd_object_t obj = gdapi.gd_object_from_variant(gdvar)
19+
if obj == NULL:
20+
return None
1921
cdef object class_name = _object_call(obj, "get_class", [])
20-
cdef object klass = _load_class(class_name)
22+
# TODO: Kind of wasteful to have to convert Godot string here...
23+
cdef object klass = _load_class(str(class_name))
2124
return klass._from_ptr(<size_t>obj)
2225

2326
@staticmethod
2427
cdef inline object cast_from_object(gd_object_t obj):
28+
if obj == NULL:
29+
return None
2530
cdef object class_name = _object_call(obj, "get_class", [])
26-
cdef object klass = _load_class(class_name)
31+
# TODO: Kind of wasteful to have to convert Godot string here...
32+
cdef object klass = _load_class(str(class_name))
2733
return klass._from_ptr(<size_t>obj)
2834

2935
{#

src/godot/classes.pyx.j2

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ cdef class BaseGDObject:
3333
def __repr__(self):
3434
return f"<{type(self).__name__} wrapper on 0x{<size_t>self._gd_ptr:x}>"
3535

36+
def __eq__(self, object other):
37+
try:
38+
return self._gd_ptr == (<BaseGDObject?>other)._gd_ptr
39+
except TypeError:
40+
return NotImplemented
41+
3642
def _set_gd_ptr(self, ptr: int):
3743
# /!\ doing `<gd_object_t>ptr` would return the address of
3844
# the PyObject instead of casting it value !
@@ -194,8 +200,13 @@ cdef object _load_class(str name):
194200

195201
def _generate_method(spec, py_meth_name):
196202
gd_meth_name = spec["name"]
197-
def _meth(self, *args):
198-
return _meth_call(self, gd_meth_name, args)
203+
if spec["flags"] & 32: # METHOD_FLAG_STATIC == 32
204+
@staticmethod
205+
def _meth(*args):
206+
return _object_call(classdb, "class_call_static", [gdname, gd_meth_name, *args])
207+
else:
208+
def _meth(self, *args):
209+
return _meth_call(self, gd_meth_name, args)
199210
_meth.__name__ = py_meth_name
200211
return _meth
201212

tests/4-use-godot-from-python/tests/test_classes.py

Lines changed: 137 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,34 @@
11
from enum import Enum
22
import clodotest
3-
from clodotest import assert_eq, assert_isinstance, assert_issubclass
3+
from clodotest import assert_eq, assert_ne, assert_isinstance, assert_issubclass
44

55
import godot
66

77

8+
def test_eq_operator():
9+
from godot.classes import Node
10+
11+
node1 = Node.new()
12+
node2 = Node.new()
13+
try:
14+
assert_ne(node1, None)
15+
assert_ne(node1, 42)
16+
17+
node1.name = "node1"
18+
node2.name = "node2"
19+
20+
assert_ne(node1, node2)
21+
assert_eq(node1, node1)
22+
23+
node1.add_child(node2)
24+
node2b = node1.get_child(0)
25+
assert_eq(node2, node2b)
26+
27+
finally:
28+
node2.free()
29+
node1.free()
30+
31+
832
def test_bad_meth_to_create_non_refcounted_object():
933
from godot.classes import Node2D
1034

@@ -61,11 +85,120 @@ def test_create_non_refcounted_object():
6185
node.free()
6286

6387

64-
def test_method():
65-
clodotest.skip("TODO")
88+
@clodotest.parametrize(
89+
"kind",
90+
[
91+
"normal_with_return_value",
92+
"normal_with_param",
93+
"normal_with_named_param",
94+
"inherited",
95+
"const",
96+
"virtual",
97+
"static",
98+
"vararg",
99+
],
100+
)
101+
def test_method(kind: str):
102+
from godot.classes import Node, JSON
103+
104+
node = Node.new()
105+
try:
106+
match kind:
107+
case "normal_with_return_value":
108+
assert_isinstance(node.get_tree_string(), godot.GDString)
109+
assert_eq(node.find_child("dummy"), None) # Return None or `Node` instance
110+
node2 = Node.new()
111+
try:
112+
node2.name = "child"
113+
node.add_child(node2)
114+
assert_eq(node.get_child(0), node2)
115+
finally:
116+
node2.free()
117+
118+
case "normal_with_param":
119+
assert_isinstance(node.is_ancestor_of(node), bool)
120+
121+
case "normal_with_named_param":
122+
clodotest.skip(reason="TODO: named param not supported yet")
123+
assert_isinstance(node.is_ancestor_of(node=node), bool)
66124

125+
case "inherited":
126+
# `get_class` is defined in `Object`
127+
assert_isinstance(node.get_class(), godot.GDString)
67128

68-
def test_property():
129+
case "const":
130+
assert_isinstance(node.can_process(), bool)
131+
132+
case "virtual":
133+
clodotest.skip(reason="TODO: find a virtual method overwritten by a subclass ?")
134+
135+
case "static":
136+
assert_isinstance(JSON.stringify(42), godot.GDString)
137+
138+
case "vararg":
139+
assert_isinstance(node.call("is_ancestor_of", node), bool)
140+
141+
case unknown:
142+
assert False, unknown
143+
144+
finally:
145+
node.free()
146+
147+
148+
@clodotest.parametrize(
149+
"kind",
150+
[
151+
"scalar",
152+
"enum",
153+
"class",
154+
],
155+
)
156+
def test_property(kind: str):
157+
from godot.classes import Node
158+
159+
node = Node.new()
160+
try:
161+
match kind:
162+
case "scalar":
163+
assert_eq(node.name, godot.StringName(""))
164+
165+
node.name = godot.StringName("foo")
166+
assert_eq(node.name, godot.StringName("foo"))
167+
168+
node.name = "bar"
169+
assert_eq(node.name, godot.StringName("bar"))
170+
171+
case "enum":
172+
clodotest.skip(
173+
reason="TODO: enum currently return `int` instead of `Enum` instance"
174+
)
175+
assert_eq(
176+
node.physics_interpolation_mode,
177+
node.PhysicsInterpolationMode.PHYSICS_INTERPOLATION_MODE_INHERIT,
178+
)
179+
node.physics_interpolation_mode = (
180+
node.PhysicsInterpolationMode.PHYSICS_INTERPOLATION_MODE_ON
181+
)
182+
assert_eq(
183+
node.physics_interpolation_mode,
184+
node.PhysicsInterpolationMode.PHYSICS_INTERPOLATION_MODE_ON,
185+
)
186+
187+
case "class":
188+
node2 = Node.new()
189+
assert_eq(node2.owner, None)
190+
try:
191+
node.add_child(node2)
192+
assert_eq(node2.owner, None)
193+
node2.owner = node
194+
assert_eq(node2.owner, node)
195+
finally:
196+
node2.free()
197+
198+
case unknown:
199+
assert False, unknown
200+
finally:
201+
node.free()
69202
clodotest.skip("TODO")
70203

71204

0 commit comments

Comments
 (0)