Skip to content

Commit 46de384

Browse files
committed
Improve __len__&__bool__ implementation for builtins
1 parent 9f624c7 commit 46de384

File tree

2 files changed

+303
-9
lines changed

2 files changed

+303
-9
lines changed

src/godot/builtins_pyx/operator.pyx.j2

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,43 @@ def __eq__(self, object other):
3232
{% if builtin.is_transparent_builtin %}
3333
return (
3434
{% for field_path in builtin.all_nested_scalar_members %}
35-
{% if not loop.first%}
36-
and
37-
{% endif %}
38-
self._gd_data.{{ field_path }} == other_gd_data.{{ field_path }}
35+
{%if not loop.first%}and {% endif %}self._gd_data.{{ field_path }} == other_gd_data.{{ field_path }}
3936
{% endfor %}
4037
)
4138
{% else %}
4239
return gdapi.gd_{{ builtin.snake_name }}_op_equal_{{ builtin.snake_name }}(&self._gd_data, other_gd_data)
4340
{% endif %}
4441

45-
{% if builtin.original_name in ("Array", "Dictionary") or builtin.is_packed_array %}
42+
{% if builtin.original_name in ("Dictionary", "Array") or builtin.is_packed_array %}
43+
{# Note `__bool__` is not needed if `__len__` is defined #}
44+
def __len__(self):
45+
cdef int64_t size = gdapi.gd_{{ builtin.snake_name }}_meth_size(&self._gd_data)
46+
return size
47+
{% elif builtin.original_name in ("String", "StringName") %}
4648
{# Note `__bool__` is not needed if `__len__` is defined #}
4749
def __len__(self):
48-
return self.size()
50+
cdef int64_t length = gdapi.gd_{{ builtin.snake_name }}_meth_length(&self._gd_data)
51+
return length
4952
{% elif builtin.is_transparent_builtin %}
5053
def __bool__(self):
5154
return (
52-
{% for member in builtin.all_nested_scalar_members %}
53-
{% if not loop.first %}or {% endif %}self.{{ member }} != 0
55+
{% for field_path in builtin.all_nested_scalar_members %}
56+
{% if not loop.first %}or {% endif %}self._gd_data.{{ field_path }} != 0
5457
{% endfor %}
5558
)
56-
{% else %}
59+
{% elif builtin.original_name == "NodePath" %}
60+
def __bool__(self):
61+
cdef int64_t is_empty = gdapi.gd_{{ builtin.snake_name }}_meth_is_empty(&self._gd_data)
62+
return not is_empty
63+
{% elif builtin.original_name == "RID" %}
5764
def __bool__(self):
5865
cdef {{ builtin.c_type }} zero
5966
memset(&zero, 0, cython.sizeof({{ builtin.c_type }}))
6067
return not gdapi.gd_{{ builtin.snake_name }}_op_equal_{{ builtin.snake_name }}(&self._gd_data, &zero)
68+
{% else %}
69+
def __bool__(self):
70+
cdef uint8_t is_null = gdapi.gd_{{ builtin.snake_name }}_meth_is_null(&self._gd_data)
71+
return not is_null
6172
{% endif %}
6273

6374
{% if builtin.original_name == "Array" or builtin.is_packed_array %}

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

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,3 +811,286 @@ def test_enum():
811811
assert_issubclass(godot.Vector2i.Axis, enum.IntEnum)
812812
assert_eq(godot.Vector2i.Axis.X, 0)
813813
assert_eq(godot.Vector2i.Axis.Y, 1)
814+
815+
816+
@clodotest.parametrize(
817+
"kind",
818+
[
819+
"STRING",
820+
"VECTOR2",
821+
"VECTOR2I",
822+
"RECT2",
823+
"RECT2I",
824+
"TRANSFORM2D",
825+
"VECTOR3",
826+
"VECTOR3I",
827+
"VECTOR4",
828+
"VECTOR4I",
829+
"PLANE",
830+
"AABB",
831+
"QUATERNION",
832+
"BASIS",
833+
"TRANSFORM3D",
834+
"PROJECTION",
835+
"COLOR",
836+
"RID",
837+
"CALLABLE",
838+
"SIGNAL",
839+
"STRING_NAME",
840+
"NODE_PATH",
841+
"DICTIONARY",
842+
"ARRAY",
843+
"PACKED_BYTE_ARRAY",
844+
],
845+
)
846+
def test_len_and_bool(kind: str):
847+
match kind:
848+
case "STRING":
849+
assert_eq(bool(godot.GDString("foo")), True)
850+
assert_eq(bool(godot.GDString()), False)
851+
assert_eq(len(godot.GDString()), 0)
852+
assert_eq(len(godot.GDString("foo")), 3)
853+
854+
case "VECTOR2":
855+
assert_eq(bool(godot.Vector2(1, 0)), True)
856+
assert_eq(bool(godot.Vector2(0, 1)), True)
857+
assert_eq(bool(godot.Vector2()), False)
858+
with clodotest.raises(TypeError):
859+
len(godot.Vector2())
860+
861+
case "VECTOR2I":
862+
assert_eq(bool(godot.Vector2i(1, 0)), True)
863+
assert_eq(bool(godot.Vector2i(0, 1)), True)
864+
assert_eq(bool(godot.Vector2i()), False)
865+
with clodotest.raises(TypeError):
866+
len(godot.Vector2i())
867+
868+
case "RECT2":
869+
assert_eq(bool(godot.Rect2(godot.Vector2(1, 1), godot.Vector2())), True)
870+
assert_eq(bool(godot.Rect2(godot.Vector2(), godot.Vector2(1, 1))), True)
871+
assert_eq(bool(godot.Rect2()), False)
872+
with clodotest.raises(TypeError):
873+
len(godot.Rect2())
874+
875+
case "RECT2I":
876+
assert_eq(bool(godot.Rect2i(godot.Vector2i(1, 1), godot.Vector2i())), True)
877+
assert_eq(bool(godot.Rect2i(godot.Vector2i(), godot.Vector2i(1, 1))), True)
878+
assert_eq(bool(godot.Rect2i()), False)
879+
with clodotest.raises(TypeError):
880+
len(godot.Rect2i())
881+
882+
case "TRANSFORM2D":
883+
assert_eq(
884+
bool(godot.Transform2D(godot.Vector2(1, 1), godot.Vector2(), godot.Vector2())), True
885+
)
886+
assert_eq(
887+
bool(godot.Transform2D(godot.Vector2(), godot.Vector2(1, 1), godot.Vector2())), True
888+
)
889+
assert_eq(
890+
bool(godot.Transform2D(godot.Vector2(), godot.Vector2(), godot.Vector2(1, 1))), True
891+
)
892+
assert_eq(bool(godot.Transform2D()), False)
893+
with clodotest.raises(TypeError):
894+
len(godot.Transform2D())
895+
896+
case "VECTOR3":
897+
assert_eq(bool(godot.Vector3(1, 0, 0)), True)
898+
assert_eq(bool(godot.Vector3(0, 1, 0)), True)
899+
assert_eq(bool(godot.Vector3(0, 0, 1)), True)
900+
assert_eq(bool(godot.Vector3()), False)
901+
with clodotest.raises(TypeError):
902+
len(godot.Vector3())
903+
904+
case "VECTOR3I":
905+
assert_eq(bool(godot.Vector3i(1, 0, 0)), True)
906+
assert_eq(bool(godot.Vector3i(0, 1, 0)), True)
907+
assert_eq(bool(godot.Vector3i(0, 0, 1)), True)
908+
assert_eq(bool(godot.Vector3i()), False)
909+
with clodotest.raises(TypeError):
910+
len(godot.Vector3i())
911+
912+
case "VECTOR4":
913+
assert_eq(bool(godot.Vector4(1, 0, 0, 0)), True)
914+
assert_eq(bool(godot.Vector4(0, 1, 0, 0)), True)
915+
assert_eq(bool(godot.Vector4(0, 0, 1, 0)), True)
916+
assert_eq(bool(godot.Vector4(0, 0, 0, 1)), True)
917+
assert_eq(bool(godot.Vector4()), False)
918+
with clodotest.raises(TypeError):
919+
len(godot.Vector4())
920+
921+
case "VECTOR4I":
922+
assert_eq(bool(godot.Vector4i(1, 0, 0, 0)), True)
923+
assert_eq(bool(godot.Vector4i(0, 1, 0, 0)), True)
924+
assert_eq(bool(godot.Vector4i(0, 0, 1, 0)), True)
925+
assert_eq(bool(godot.Vector4i(0, 0, 0, 1)), True)
926+
assert_eq(bool(godot.Vector4i()), False)
927+
with clodotest.raises(TypeError):
928+
len(godot.Vector4i())
929+
930+
case "PLANE":
931+
assert_eq(bool(godot.Plane(1)), True)
932+
assert_eq(bool(godot.Plane(0, godot.Vector3(1, 1, 1))), True)
933+
assert_eq(bool(godot.Plane()), False)
934+
with clodotest.raises(TypeError):
935+
len(godot.Plane())
936+
937+
case "AABB":
938+
assert_eq(bool(godot.AABB(godot.Vector3(1, 1, 1), godot.Vector3())), True)
939+
assert_eq(bool(godot.AABB(godot.Vector3(), godot.Vector3(1, 1, 1))), True)
940+
assert_eq(bool(godot.AABB()), False)
941+
with clodotest.raises(TypeError):
942+
len(godot.AABB())
943+
944+
case "QUATERNION":
945+
assert_eq(bool(godot.Quaternion(1, 0, 0, 0)), True)
946+
assert_eq(bool(godot.Quaternion(0, 1, 0, 0)), True)
947+
assert_eq(bool(godot.Quaternion(0, 0, 1, 0)), True)
948+
assert_eq(bool(godot.Quaternion(0, 0, 0, 1)), True)
949+
assert_eq(bool(godot.Quaternion()), False)
950+
with clodotest.raises(TypeError):
951+
len(godot.Quaternion())
952+
953+
case "BASIS":
954+
assert_eq(
955+
bool(godot.Basis(godot.Vector3(1, 1, 1), godot.Vector3(), godot.Vector3())), True
956+
)
957+
assert_eq(
958+
bool(godot.Basis(godot.Vector3(), godot.Vector3(1, 1, 1), godot.Vector3())), True
959+
)
960+
assert_eq(
961+
bool(godot.Basis(godot.Vector3(), godot.Vector3(), godot.Vector3(1, 1, 1))), True
962+
)
963+
assert_eq(bool(godot.Basis()), False)
964+
with clodotest.raises(TypeError):
965+
len(godot.Basis())
966+
967+
case "TRANSFORM3D":
968+
assert_eq(
969+
bool(
970+
godot.Transform3D(
971+
godot.Basis(godot.Vector3(1, 1, 1), godot.Vector3(), godot.Vector3()),
972+
godot.Vector3(),
973+
)
974+
),
975+
True,
976+
)
977+
assert_eq(
978+
bool(
979+
godot.Transform3D(
980+
godot.Basis(),
981+
godot.Vector3(1, 1, 1),
982+
)
983+
),
984+
True,
985+
)
986+
assert_eq(bool(godot.Transform3D()), False)
987+
with clodotest.raises(TypeError):
988+
len(godot.Transform3D())
989+
990+
case "PROJECTION":
991+
assert_eq(
992+
bool(
993+
godot.Projection(
994+
godot.Vector4(1, 1, 1), godot.Vector4(), godot.Vector4(), godot.Vector4()
995+
)
996+
),
997+
True,
998+
)
999+
assert_eq(
1000+
bool(
1001+
godot.Projection(
1002+
godot.Vector4(), godot.Vector4(1, 1, 1), godot.Vector4(), godot.Vector4()
1003+
)
1004+
),
1005+
True,
1006+
)
1007+
assert_eq(
1008+
bool(
1009+
godot.Projection(
1010+
godot.Vector4(), godot.Vector4(), godot.Vector4(1, 1, 1), godot.Vector4()
1011+
)
1012+
),
1013+
True,
1014+
)
1015+
assert_eq(
1016+
bool(
1017+
godot.Projection(
1018+
godot.Vector4(), godot.Vector4(), godot.Vector4(), godot.Vector4(1, 1, 1)
1019+
)
1020+
),
1021+
True,
1022+
)
1023+
assert_eq(bool(godot.Projection()), False)
1024+
with clodotest.raises(TypeError):
1025+
len(godot.Projection())
1026+
1027+
case "COLOR":
1028+
assert_eq(bool(godot.Color(1, 0, 0, 0)), True)
1029+
assert_eq(bool(godot.Color(0, 1, 0, 0)), True)
1030+
assert_eq(bool(godot.Color(0, 0, 1, 0)), True)
1031+
assert_eq(bool(godot.Color(0, 0, 0, 1)), True)
1032+
assert_eq(bool(godot.Projection()), False)
1033+
with clodotest.raises(TypeError):
1034+
len(godot.Color())
1035+
1036+
case "RID":
1037+
assert_eq(bool(godot.RID()), False)
1038+
# TODO: is there a way to build a RID with a non-zero id ?
1039+
with clodotest.raises(TypeError):
1040+
len(godot.RID())
1041+
1042+
case "CALLABLE":
1043+
assert_eq(bool(godot.GDCallable()), False)
1044+
from godot.singletons import OS
1045+
1046+
c = godot.GDCallable._create(OS, "get_cmdline_args")
1047+
assert_eq(bool(c), True)
1048+
with clodotest.raises(TypeError):
1049+
len(godot.GDCallable())
1050+
1051+
case "SIGNAL":
1052+
assert_eq(bool(godot.Signal()), False)
1053+
from godot.singletons import OS, Input
1054+
1055+
assert_eq(bool(Input.joy_connection_changed), False)
1056+
c = godot.GDCallable._create(OS, "get_cmdline_args")
1057+
clodotest.skip(reason="TODO: `Signal.connect` returns a `ERR_UNCONFIGURED`")
1058+
assert_eq(Input.joy_connection_changed.connect(c), godot.Error.OK)
1059+
assert_eq(bool(Input.joy_connection_changed), True)
1060+
with clodotest.raises(TypeError):
1061+
len(godot.Signal())
1062+
1063+
case "STRING_NAME":
1064+
assert_eq(bool(godot.StringName("foo")), True)
1065+
assert_eq(bool(godot.StringName()), False)
1066+
assert_eq(len(godot.StringName("foo")), 3)
1067+
assert_eq(len(godot.StringName()), 0)
1068+
1069+
case "NODE_PATH":
1070+
assert_eq(bool(godot.NodePath("foo")), True)
1071+
assert_eq(bool(godot.NodePath()), False)
1072+
with clodotest.raises(TypeError):
1073+
len(godot.NodePath())
1074+
1075+
case "DICTIONARY":
1076+
assert_eq(
1077+
bool(godot.GDDictionary([(1, godot.GDString("foo")), (godot.GDString("bar"), 2)])),
1078+
True,
1079+
)
1080+
assert_eq(bool(godot.GDDictionary()), False)
1081+
assert_eq(
1082+
len(godot.GDDictionary([(1, godot.GDString("foo")), (godot.GDString("bar"), 2)])), 2
1083+
)
1084+
assert_eq(len(godot.GDDictionary()), 0)
1085+
1086+
case "ARRAY":
1087+
assert_eq(bool(godot.GDArray((1, godot.GDString("foo")))), True)
1088+
assert_eq(bool(godot.GDArray()), False)
1089+
assert_eq(len(godot.GDArray((1, godot.GDString("foo")))), 2)
1090+
assert_eq(len(godot.GDArray()), 0)
1091+
1092+
case "PACKED_BYTE_ARRAY":
1093+
assert_eq(bool(godot.PackedStringArray([godot.GDString("foo")])), True)
1094+
assert_eq(bool(godot.PackedStringArray()), False)
1095+
assert_eq(len(godot.PackedStringArray([godot.GDString("foo")])), 1)
1096+
assert_eq(len(godot.PackedStringArray()), 0)

0 commit comments

Comments
 (0)