Replace PyObject_GetAttrString with direct type references

Replace ~230 occurrences of PyObject_GetAttrString(McRFPy_API::mcrf_module, "TypeName")
with direct &mcrfpydef::PyXxxType references across 32 source files.

Each PyObject_GetAttrString call returns a new reference. When used inline
in PyObject_IsInstance(), that reference was immediately leaked. When used
for tp_alloc, the reference required careful Py_DECREF management that was
often missing on error paths.

Direct type references are compile-time constants that never need reference
counting, eliminating ~230 potential leak sites and removing ~100 lines of
Py_DECREF/Py_XDECREF cleanup code.

Also adds extractDrawable() helper in UICollection.cpp to replace repeated
8-way type-check-and-extract chains with a single function call.

Closes #267, closes #268

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
John McCardle 2026-03-07 23:18:42 -05:00
commit 71eb01c950
32 changed files with 249 additions and 944 deletions

View file

@ -7,6 +7,7 @@
#include "McRFPy_Automation.h" // #111 - For simulated mouse position
#include "PythonObjectCache.h" // #184 - For subclass callback support
#include "McRFPy_API.h" // For Vector type access
#include "PyVector.h" // For direct PyVectorType reference
#include "PyMouseButton.h" // For MouseButton enum
#include "PyInputState.h" // For InputState enum
#include <algorithm>
@ -55,16 +56,7 @@ static bool tryCallPythonMethod(UIDrawable* drawable, const char* method_name,
if (method && PyCallable_Check(method) && method != Py_None) {
// Create Vector object for position (matches property callback signature)
PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
if (!vector_type) {
PyErr_Print();
PyErr_Clear();
Py_XDECREF(method);
Py_DECREF(pyObj);
return false;
}
PyObject* pos = PyObject_CallFunction(vector_type, "ff", mousepos.x, mousepos.y);
Py_DECREF(vector_type);
PyObject* pos = PyObject_CallFunction((PyObject*)&mcrfpydef::PyVectorType, "ff", mousepos.x, mousepos.y);
if (!pos) {
PyErr_Print();
PyErr_Clear();
@ -164,16 +156,7 @@ static bool tryCallPythonMethod(UIDrawable* drawable, const char* method_name,
if (method && PyCallable_Check(method) && method != Py_None) {
// Create Vector object for position
PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
if (!vector_type) {
PyErr_Print();
PyErr_Clear();
Py_XDECREF(method);
Py_DECREF(pyObj);
return false;
}
PyObject* pos = PyObject_CallFunction(vector_type, "ff", mousepos.x, mousepos.y);
Py_DECREF(vector_type);
PyObject* pos = PyObject_CallFunction((PyObject*)&mcrfpydef::PyVectorType, "ff", mousepos.x, mousepos.y);
if (!pos) {
PyErr_Print();
PyErr_Clear();