@@ -197,24 +197,20 @@ inline PyObject *read_pair(ReaderT *reader, TypeTreeNodeObject *node, TypeTreeRe
197197 return pair;
198198}
199199
200- inline PyObject *class_from_dict (PyObject *dict, TypeTreeNodeObject *node, TypeTreeReaderConfigT *config)
200+ template <bool swap>
201+ inline PyObject *read_class (ReaderT *reader, TypeTreeNodeObject *node, TypeTreeReaderConfigT *config)
201202{
202203 PyObject *clz = NULL ;
203- PyObject *args = NULL ;
204- PyObject *instance = NULL ;
205- PyObject *keys = NULL ;
206- PyObject *clean_args = NULL ;
207- PyObject *annotations = NULL ;
208- PyObject *extra = NULL ;
204+ PyObject *dict = PyDict_New ();
209205
210206 // Determine the class based on node's _data_type
211207 if (node->_data_type == NodeDataType::pptr)
212208 {
213- // Add 'assetsfile' key for PPtr objects
214209 if (PyDict_SetItemString (dict, " assetsfile" , config->assetfile ) != 0 )
215210 {
216211 PyErr_SetString (PyExc_RuntimeError, " Failed to set 'assetsfile'" );
217- goto error_cleanup;
212+ Py_DECREF (dict);
213+ return NULL ;
218214 }
219215 clz = PyObject_GetAttrString (config->classes , " PPtr" );
220216 }
@@ -227,136 +223,100 @@ inline PyObject *class_from_dict(PyObject *dict, TypeTreeNodeObject *node, TypeT
227223 }
228224 }
229225
230- // If class lookup fails, raise an error
231226 if (clz == NULL )
232227 {
233228 PyErr_SetString (PyExc_ValueError, " Failed to get class" );
234- goto error_cleanup;
229+ Py_DECREF (dict);
230+ return NULL ;
235231 }
236232
237- // Prepare arguments (empty tuple)
238- args = PyTuple_New (0 );
239- if (args == NULL )
233+ for (int i = 0 ; i < PyList_GET_SIZE (node->m_Children ); i++)
240234 {
241- goto error_cleanup;
235+ TypeTreeNodeObject *child = (TypeTreeNodeObject *)PyList_GetItem (node->m_Children , i);
236+ PyObject *child_value = read_typetree_value<swap>(reader, child, config);
237+ if (!child_value)
238+ {
239+ Py_DECREF (dict);
240+ return NULL ;
241+ }
242+ if (PyDict_SetItem (dict, child->m_Name , child_value))
243+ {
244+ Py_DECREF (dict);
245+ Py_DECREF (child_value);
246+ return NULL ;
247+ }
248+ // dict increases ref count, so we need to decref here
249+ Py_DECREF (child_value);
242250 }
243251
244- // Attempt to create the instance from the class and dict
245- instance = PyObject_Call (clz, args, dict);
252+ PyObject *args = PyTuple_New ( 0 );
253+ PyObject * instance = PyObject_Call (clz, args, dict);
246254 if (instance != NULL )
247255 {
248- goto success;
256+ Py_DECREF (args);
257+ Py_DECREF (dict);
258+ return instance;
249259 }
250260 PyErr_Clear ();
251261
252- // If calling the class failed, check for callable clean_name function
253- if (!PyCallable_Check (config->clean_name ))
254- {
255- PyErr_SetString (PyExc_ValueError, " clean_name is not callable" );
256- goto error_cleanup;
257- }
258-
259- // Clean the dictionary keys
260- keys = PyDict_Keys (dict);
261- clean_args = PyTuple_New (1 ); // For passing to the clean_name function
262- if (keys == NULL || clean_args == NULL )
263- {
264- goto error_cleanup;
265- }
266-
267- // Iterate through dict keys, clean them, and update if necessary
262+ PyObject *keys = PyDict_Keys (dict);
263+ PyObject *clean_args = PyTuple_New (1 );
268264 for (Py_ssize_t i = 0 ; i < PyList_Size (keys); i++)
269265 {
270266 PyObject *key = PyList_GetItem (keys, i);
271- Py_INCREF (key); // Protect reference count
272-
273- PyTuple_SetItem (clean_args, 0 , key);
274- PyObject *clean_key = PyObject_CallFunctionObjArgs (config->clean_name , clean_args, NULL );
275- if (clean_key == NULL )
276- {
277- Py_DECREF (key);
278- goto error_cleanup;
279- }
280-
281- if (PyUnicode_Compare (key, clean_key) != 0 )
267+ PyTuple_SET_ITEM (clean_args, 0 , key);
268+ PyObject *clean_key = PyObject_Call (config->clean_name , clean_args, NULL );
269+ if (PyUnicode_Compare (key, clean_key))
282270 {
283- // Replace the key in the dict with the cleaned key
284271 PyObject *value = PyDict_GetItem (dict, key);
285- if (value != NULL )
286- {
287- PyDict_DelItem (dict, key);
288- PyDict_SetItem (dict, clean_key, value);
289- }
272+ PyDict_SetItem (dict, clean_key, value);
273+ PyDict_DelItem (dict, key);
290274 }
291275 Py_DECREF (clean_key);
292- Py_DECREF (key);
293276 }
277+ PyTuple_SET_ITEM (clean_args, 0 , Py_None);
278+ Py_DECREF (clean_args);
279+ Py_DECREF (keys);
294280
295- // Retry creating the class instance with cleaned dictionary
296281 instance = PyObject_Call (clz, args, dict);
297282 if (instance != NULL )
298283 {
299- goto success;
300- }
301-
302- // Handle extra attributes not present in class annotations
303- annotations = PyObject_GetAttrString (clz, " __annotations__" );
304- if (annotations == NULL )
305- {
306- PyErr_SetString (PyExc_ValueError, " Failed to get annotations" );
307- goto error_cleanup;
284+ Py_DECREF (args);
285+ Py_DECREF (dict);
286+ return instance;
308287 }
288+ PyErr_Clear ();
309289
310- extra = PyDict_New ();
290+ PyObject *annonations = PyObject_GetAttrString (clz, " __annotations__" );
291+ PyObject *extras = PyDict_New ();
292+ keys = PyDict_Keys (dict);
311293 for (Py_ssize_t i = 0 ; i < PyList_Size (keys); i++)
312294 {
313295 PyObject *key = PyList_GetItem (keys, i);
314- if (PyDict_Contains (annotations , key) == 0 )
296+ if (PyDict_Contains (annonations , key) == 0 )
315297 {
316298 PyObject *value = PyDict_GetItem (dict, key);
317- PyDict_SetItem (extra , key, value);
299+ PyDict_SetItem (extras , key, value);
318300 PyDict_DelItem (dict, key);
319301 }
320302 }
303+ Py_DECREF (keys);
321304
322305 instance = PyObject_Call (clz, args, dict);
323306 if (instance != NULL )
324307 {
325- // Set extra attributes on the instance
326- keys = PyDict_Keys (extra);
327- for (Py_ssize_t i = 0 ; i < PyList_Size (keys); i++)
308+ PyObject *items = PyDict_Items (extras);
309+ for (Py_ssize_t i = 0 ; i < PyList_Size (items); i++)
328310 {
329- PyObject *key = PyList_GetItem (keys, i);
330- PyObject *value = PyDict_GetItem (extra, key);
311+ PyObject *item = PyList_GetItem (items, i);
312+ PyObject *key = PyTuple_GetItem (item, 0 );
313+ PyObject *value = PyTuple_GetItem (item, 1 );
331314 PyObject_SetAttr (instance, key, value);
332315 }
333- goto success ;
316+ Py_DECREF (items) ;
334317 }
335318
336- PyErr_SetString (PyExc_ValueError, " Failed to create instance" );
337- goto error_cleanup;
338-
339- success:
340- // Cleanup and return the instance
341- Py_DECREF (args);
342- Py_DECREF (clz);
343- Py_DECREF (dict);
344- Py_XDECREF (keys);
345- Py_XDECREF (clean_args);
346- Py_XDECREF (annotations);
347- Py_XDECREF (extra);
348319 return instance;
349-
350- error_cleanup:
351- // Cleanup in case of an error
352- Py_XDECREF (args);
353- Py_XDECREF (clz);
354- Py_XDECREF (dict);
355- Py_XDECREF (keys);
356- Py_XDECREF (clean_args);
357- Py_XDECREF (annotations);
358- Py_XDECREF (extra);
359- return NULL ;
360320}
361321
362322template <bool swap>
@@ -445,27 +405,30 @@ PyObject *read_typetree_value(ReaderT *reader, TypeTreeNodeObject *node, TypeTre
445405 {
446406 // class
447407 value = PyDict_New ();
448- for ( int i = 0 ; i < PyList_GET_SIZE (node-> m_Children ); i++ )
408+ if (config-> as_dict )
449409 {
450- child = (TypeTreeNodeObject *)PyList_GetItem (node->m_Children , i);
451- PyObject *child_value = read_typetree_value<swap>(reader, child, config);
452- if (!child_value)
410+ for (int i = 0 ; i < PyList_GET_SIZE (node->m_Children ); i++)
453411 {
454- Py_DECREF (value);
455- return NULL ;
456- }
457- if (PyDict_SetItem (value, child->m_Name , child_value))
458- {
459- Py_DECREF (value);
412+ child = (TypeTreeNodeObject *)PyList_GetItem (node->m_Children , i);
413+ PyObject *child_value = read_typetree_value<swap>(reader, child, config);
414+ if (!child_value)
415+ {
416+ Py_DECREF (value);
417+ return NULL ;
418+ }
419+ if (PyDict_SetItem (value, child->m_Name , child_value))
420+ {
421+ Py_DECREF (value);
422+ Py_DECREF (child_value);
423+ return NULL ;
424+ }
425+ // dict increases ref count, so we need to decref here
460426 Py_DECREF (child_value);
461- return NULL ;
462427 }
463- // dict increases ref count, so we need to decref here
464- Py_DECREF (child_value);
465428 }
466- if (!config-> as_dict )
429+ else
467430 {
468- value = class_from_dict (value , node, config);
431+ value = read_class<swap>(reader , node, config);
469432 }
470433 }
471434 }
@@ -499,6 +462,26 @@ PyObject *read_typetree(PyObject *self, PyObject *args, PyObject *kwargs)
499462 return NULL ;
500463 }
501464
465+ config.as_dict = Py_IsTrue (as_dict) == 1 ;
466+ if (!config.as_dict )
467+ {
468+ if (PyCallable_Check (config.clean_name ) == 0 )
469+ {
470+ PyErr_SetString (PyExc_ValueError, " clean_name must be callable if not as dict" );
471+ return NULL ;
472+ }
473+ if (Py_IsNone (config.assetfile ) == 1 )
474+ {
475+ PyErr_SetString (PyExc_ValueError, " assetsfile must be set if not as dict" );
476+ return NULL ;
477+ }
478+ if (Py_IsNone (config.classes ) == 1 )
479+ {
480+ PyErr_SetString (PyExc_ValueError, " classes must be set if not as dict" );
481+ return NULL ;
482+ }
483+ }
484+
502485 volatile uint16_t bint = 0x0100 ;
503486 volatile bool is_big_endian = ((uint8_t *)&bint)[0 ] == 1 ;
504487
@@ -536,7 +519,6 @@ PyObject *read_typetree(PyObject *self, PyObject *args, PyObject *kwargs)
536519 return NULL ;
537520 }
538521 ReaderT reader = {static_cast <uint8_t *>(view.buf ), static_cast <uint8_t *>(view.buf ) + view.len , static_cast <uint8_t *>(view.buf )};
539- config.as_dict = Py_IsTrue (as_dict) == 1 ;
540522
541523 PyObject *value;
542524 if (swap)
0 commit comments