• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stevearc / godot_parser / #75

22 Feb 2026 11:59PM UTC coverage: 95.652% (-0.8%) from 96.412%
#75

Pull #19

github

web-flow
Merge 049de7933 into bf5fc96f9
Pull Request #19: Parser updates

215 of 236 branches covered (91.1%)

Branch coverage included in aggregate %.

78 of 86 new or added lines in 4 files covered. (90.7%)

907 of 937 relevant lines covered (96.8%)

0.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.39
/godot_parser/objects.py
1
"""Wrappers for Godot's non-primitive object types"""
2

3
from functools import partial
1✔
4
from typing import Type, TypeVar
1✔
5

6
from .util import stringify_object
1✔
7

8
__all__ = [
1✔
9
    "GDObject",
10
    "Vector2",
11
    "Vector3",
12
    "Color",
13
    "NodePath",
14
    "ExtResource",
15
    "SubResource",
16
    "StringName",
17
    "TypedArray",
18
    "TypedDictionary",
19
]
20

21
GD_OBJECT_REGISTRY = {}
1✔
22

23

24
class GDObjectMeta(type):
1✔
25
    """
26
    This is me trying to be too clever for my own good
27

28
    Odds are high that it'll cause some weird hard-to-debug issues at some point, but
29
    isn't it neeeeeat? -_-
30
    """
31

32
    def __new__(cls, name, bases, dct):
1✔
33
        x = super().__new__(cls, name, bases, dct)
1✔
34
        GD_OBJECT_REGISTRY[name] = x
1✔
35
        return x
1✔
36

37

38
GDObjectType = TypeVar("GDObjectType", bound="GDObject")
1✔
39

40

41
class GDObject(metaclass=GDObjectMeta):
1✔
42
    """
43
    Base class for all GD Object types
44

45
    Can be used to represent any GD type. For example::
46

47
        GDObject('Vector2', 1, 2) == Vector2(1, 2)
48
    """
49

50
    def __init__(self, name, *args) -> None:
1✔
51
        self.name = name
1✔
52
        self.args = list(args)
1✔
53

54
    @classmethod
1✔
55
    def from_parser(cls: Type[GDObjectType], parse_result) -> GDObjectType:
1✔
56
        name = parse_result[0]
1✔
57
        factory = GD_OBJECT_REGISTRY.get(name, partial(GDObject, name))
1✔
58
        return factory(*parse_result[1:])
1✔
59

60
    def __str__(self) -> str:
1✔
61
        return "%s(%s)" % (
1✔
62
            self.name,
63
            ", ".join([stringify_object(v) for v in self.args]),
64
        )
65

66
    def __repr__(self) -> str:
1✔
67
        return self.__str__()
1✔
68

69
    def __eq__(self, other) -> bool:
1✔
70
        if not isinstance(other, GDObject):
1✔
71
            return False
1✔
72
        return self.name == other.name and self.args == other.args
1✔
73

74
    def __ne__(self, other) -> bool:
1✔
75
        return not self.__eq__(other)
1✔
76

77
    def __hash__(self):
1✔
78
        return hash(frozenset((self.name, *self.args)))
1✔
79

80

81
class Vector2(GDObject):
1✔
82
    def __init__(self, x: float, y: float) -> None:
1✔
83
        super().__init__("Vector2", x, y)
1✔
84

85
    def __getitem__(self, idx) -> float:
1✔
86
        return self.args[idx]
1✔
87

88
    def __setitem__(self, idx: int, value: float):
1✔
89
        self.args[idx] = value
1✔
90

91
    @property
1✔
92
    def x(self) -> float:
1✔
93
        """Getter for x"""
94
        return self.args[0]
1✔
95

96
    @x.setter
1✔
97
    def x(self, x: float) -> None:
1✔
98
        """Setter for x"""
99
        self.args[0] = x
1✔
100

101
    @property
1✔
102
    def y(self) -> float:
1✔
103
        """Getter for y"""
104
        return self.args[1]
1✔
105

106
    @y.setter
1✔
107
    def y(self, y: float) -> None:
1✔
108
        """Setter for y"""
109
        self.args[1] = y
1✔
110

111

112
class Vector3(GDObject):
1✔
113
    def __init__(self, x: float, y: float, z: float) -> None:
1✔
114
        super().__init__("Vector3", x, y, z)
1✔
115

116
    def __getitem__(self, idx: int) -> float:
1✔
117
        return self.args[idx]
1✔
118

119
    def __setitem__(self, idx: int, value: float) -> None:
1✔
120
        self.args[idx] = value
1✔
121

122
    @property
1✔
123
    def x(self) -> float:
1✔
124
        """Getter for x"""
125
        return self.args[0]
1✔
126

127
    @x.setter
1✔
128
    def x(self, x: float) -> None:
1✔
129
        """Setter for x"""
130
        self.args[0] = x
1✔
131

132
    @property
1✔
133
    def y(self) -> float:
1✔
134
        """Getter for y"""
135
        return self.args[1]
1✔
136

137
    @y.setter
1✔
138
    def y(self, y: float) -> None:
1✔
139
        """Setter for y"""
140
        self.args[1] = y
1✔
141

142
    @property
1✔
143
    def z(self) -> float:
1✔
144
        """Getter for z"""
145
        return self.args[2]
1✔
146

147
    @z.setter
1✔
148
    def z(self, z: float) -> None:
1✔
149
        """Setter for z"""
150
        self.args[2] = z
1✔
151

152

153
class Color(GDObject):
1✔
154
    def __init__(self, r: float, g: float, b: float, a: float) -> None:
1✔
155
        assert 0 <= r <= 1
1✔
156
        assert 0 <= g <= 1
1✔
157
        assert 0 <= b <= 1
1✔
158
        assert 0 <= a <= 1
1✔
159
        super().__init__("Color", r, g, b, a)
1✔
160

161
    def __getitem__(self, idx: int) -> float:
1✔
162
        return self.args[idx]
1✔
163

164
    def __setitem__(self, idx: int, value: float) -> None:
1✔
165
        self.args[idx] = value
1✔
166

167
    @property
1✔
168
    def r(self) -> float:
1✔
169
        """Getter for r"""
170
        return self.args[0]
1✔
171

172
    @r.setter
1✔
173
    def r(self, r: float) -> None:
1✔
174
        """Setter for r"""
175
        self.args[0] = r
1✔
176

177
    @property
1✔
178
    def g(self) -> float:
1✔
179
        """Getter for g"""
180
        return self.args[1]
1✔
181

182
    @g.setter
1✔
183
    def g(self, g: float) -> None:
1✔
184
        """Setter for g"""
185
        self.args[1] = g
1✔
186

187
    @property
1✔
188
    def b(self) -> float:
1✔
189
        """Getter for b"""
190
        return self.args[2]
1✔
191

192
    @b.setter
1✔
193
    def b(self, b: float) -> None:
1✔
194
        """Setter for b"""
195
        self.args[2] = b
1✔
196

197
    @property
1✔
198
    def a(self) -> float:
1✔
199
        """Getter for a"""
200
        return self.args[3]
1✔
201

202
    @a.setter
1✔
203
    def a(self, a: float) -> None:
1✔
204
        """Setter for a"""
205
        self.args[3] = a
1✔
206

207

208
class NodePath(GDObject):
1✔
209
    def __init__(self, path: str) -> None:
1✔
210
        super().__init__("NodePath", path)
1✔
211

212
    @property
1✔
213
    def path(self) -> str:
1✔
214
        """Getter for path"""
215
        return self.args[0]
1✔
216

217
    @path.setter
1✔
218
    def path(self, path: str) -> None:
1✔
219
        """Setter for path"""
220
        self.args[0] = path
1✔
221

222
    def __str__(self) -> str:
1✔
223
        return '%s("%s")' % (self.name, self.path)
1✔
224

225

226
class ExtResource(GDObject):
1✔
227
    def __init__(self, id: int) -> None:
1✔
228
        super().__init__("ExtResource", id)
1✔
229

230
    @property
1✔
231
    def id(self) -> int:
1✔
232
        """Getter for id"""
233
        return self.args[0]
1✔
234

235
    @id.setter
1✔
236
    def id(self, id: int) -> None:
1✔
237
        """Setter for id"""
238
        self.args[0] = id
1✔
239

240

241
class SubResource(GDObject):
1✔
242
    def __init__(self, id: int) -> None:
1✔
243
        super().__init__("SubResource", id)
1✔
244

245
    @property
1✔
246
    def id(self) -> int:
1✔
247
        """Getter for id"""
248
        return self.args[0]
1✔
249

250
    @id.setter
1✔
251
    def id(self, id: int) -> None:
1✔
252
        """Setter for id"""
253
        self.args[0] = id
1✔
254

255

256
class TypedArray:
1✔
257
    def __init__(self, type, list_) -> None:
1✔
258
        self.name = "Array"
1✔
259
        self.type = type
1✔
260
        self.list_ = list_
1✔
261

262
    @classmethod
1✔
263
    def WithCustomName(cls: Type["TypedArray"], name, type, list_) -> "TypedArray":
1✔
264
        custom_array = TypedArray(type, list_)
1✔
265
        custom_array.name = name
1✔
266
        return custom_array
1✔
267

268
    @classmethod
1✔
269
    def from_parser(cls: Type["TypedArray"], parse_result) -> "TypedArray":
1✔
270
        return TypedArray.WithCustomName(*parse_result)
1✔
271

272
    def __str__(self) -> str:
1✔
273
        return "%s[%s](%s)" % (self.name, self.type, stringify_object(self.list_))
1✔
274

275
    def __repr__(self) -> str:
1✔
276
        return self.__str__()
1✔
277

278
    def __eq__(self, other) -> bool:
1✔
279
        if not isinstance(other, TypedArray):
1!
NEW
280
            return False
×
281
        return (
1✔
282
            self.name == other.name
283
            and self.type == other.type
284
            and self.list_ == other.list_
285
        )
286

287
    def __ne__(self, other) -> bool:
1✔
NEW
288
        return not self.__eq__(other)
×
289

290
    def __hash__(self):
1✔
NEW
291
        return hash(frozenset((self.name, self.type, self.list_)))
×
292

293

294
class TypedDictionary:
1✔
295
    def __init__(self, key_type, value_type, dict_) -> None:
1✔
296
        self.name = "Dictionary"
1✔
297
        self.key_type = key_type
1✔
298
        self.value_type = value_type
1✔
299
        self.dict_ = dict_
1✔
300

301
    @classmethod
1✔
302
    def WithCustomName(
1✔
303
        cls: Type["TypedDictionary"], name, key_type, value_type, dict_
304
    ) -> "TypedDictionary":
305
        custom_dict = TypedDictionary(key_type, value_type, dict_)
1✔
306
        custom_dict.name = name
1✔
307
        return custom_dict
1✔
308

309
    @classmethod
1✔
310
    def from_parser(cls: Type["TypedDictionary"], parse_result) -> "TypedDictionary":
1✔
311
        return TypedDictionary.WithCustomName(*parse_result)
1✔
312

313
    def __str__(self) -> str:
1✔
314
        return "%s[%s, %s](%s)" % (
1✔
315
            self.name,
316
            self.key_type,
317
            self.value_type,
318
            stringify_object(self.dict_),
319
        )
320

321
    def __repr__(self) -> str:
1✔
322
        return self.__str__()
1✔
323

324
    def __eq__(self, other) -> bool:
1✔
325
        if not isinstance(other, TypedDictionary):
1!
NEW
326
            return False
×
327
        return (
1✔
328
            self.name == other.name
329
            and self.key_type == other.key_type
330
            and self.value_type == other.value_type
331
            and self.dict_ == other.dict_
332
        )
333

334
    def __ne__(self, other) -> bool:
1✔
NEW
335
        return not self.__eq__(other)
×
336

337
    def __hash__(self):
1✔
NEW
338
        return hash(frozenset((self.name, self.key_type, self.value_type, self.dict_)))
×
339

340

341
class StringName:
1✔
342
    def __init__(self, str) -> None:
1✔
343
        self.str = str
1✔
344

345
    @classmethod
1✔
346
    def from_parser(cls: Type["StringName"], parse_result) -> "StringName":
1✔
347
        return StringName(parse_result[0])
1✔
348

349
    def __str__(self) -> str:
1✔
350
        return "&" + stringify_object(self.str)
1✔
351

352
    def __repr__(self) -> str:
1✔
353
        return self.__str__()
1✔
354

355
    def __eq__(self, other) -> bool:
1✔
356
        if not isinstance(other, StringName):
1!
NEW
357
            return False
×
358
        return self.str == other.str
1✔
359

360
    def __ne__(self, other) -> bool:
1✔
361
        return not self.__eq__(other)
1✔
362

363
    def __hash__(self):
1✔
364
        return hash(self.str)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc