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

@ -514,8 +514,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
switch (drawable->derived_type()) {
case PyObjectsEnum::UIFRAME:
{
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame");
if (!type) return nullptr;
type = &mcrfpydef::PyUIFrameType;
auto pyObj = (PyUIFrameObject*)type->tp_alloc(type, 0);
if (pyObj) {
pyObj->data = std::static_pointer_cast<UIFrame>(drawable);
@ -526,8 +525,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
}
case PyObjectsEnum::UICAPTION:
{
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption");
if (!type) return nullptr;
type = &mcrfpydef::PyUICaptionType;
auto pyObj = (PyUICaptionObject*)type->tp_alloc(type, 0);
if (pyObj) {
pyObj->data = std::static_pointer_cast<UICaption>(drawable);
@ -539,8 +537,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
}
case PyObjectsEnum::UISPRITE:
{
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite");
if (!type) return nullptr;
type = &mcrfpydef::PyUISpriteType;
auto pyObj = (PyUISpriteObject*)type->tp_alloc(type, 0);
if (pyObj) {
pyObj->data = std::static_pointer_cast<UISprite>(drawable);
@ -551,8 +548,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
}
case PyObjectsEnum::UIGRID:
{
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid");
if (!type) return nullptr;
type = &mcrfpydef::PyUIGridType;
auto pyObj = (PyUIGridObject*)type->tp_alloc(type, 0);
if (pyObj) {
pyObj->data = std::static_pointer_cast<UIGrid>(drawable);
@ -563,8 +559,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
}
case PyObjectsEnum::UILINE:
{
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Line");
if (!type) return nullptr;
type = &mcrfpydef::PyUILineType;
auto pyObj = (PyUILineObject*)type->tp_alloc(type, 0);
if (pyObj) {
pyObj->data = std::static_pointer_cast<UILine>(drawable);
@ -575,8 +570,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
}
case PyObjectsEnum::UICIRCLE:
{
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Circle");
if (!type) return nullptr;
type = &mcrfpydef::PyUICircleType;
auto pyObj = (PyUICircleObject*)type->tp_alloc(type, 0);
if (pyObj) {
pyObj->data = std::static_pointer_cast<UICircle>(drawable);
@ -587,8 +581,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
}
case PyObjectsEnum::UIARC:
{
type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Arc");
if (!type) return nullptr;
type = &mcrfpydef::PyUIArcType;
auto pyObj = (PyUIArcObject*)type->tp_alloc(type, 0);
if (pyObj) {
pyObj->data = std::static_pointer_cast<UIArc>(drawable);
@ -601,10 +594,6 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
Py_RETURN_NONE;
}
if (type) {
Py_DECREF(type);
}
return obj ? obj : Py_None;
}
@ -622,13 +611,9 @@ static PyObject* convertEntityToPython(std::shared_ptr<UIEntity> entity) {
}
}
PyTypeObject* type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity");
if (!type) {
Py_RETURN_NONE;
}
PyTypeObject* type = &mcrfpydef::PyUIEntityType;
auto pyObj = (PyUIEntityObject*)type->tp_alloc(type, 0);
Py_DECREF(type);
if (!pyObj) {
Py_RETURN_NONE;
@ -653,13 +638,9 @@ static PyObject* convertEntity3DToPython(std::shared_ptr<mcrf::Entity3D> entity)
}
// Create a new wrapper
PyTypeObject* type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity3D");
if (!type) {
Py_RETURN_NONE;
}
PyTypeObject* type = &mcrfpydef::PyEntity3DType;
auto pyObj = (PyEntity3DObject*)type->tp_alloc(type, 0);
Py_DECREF(type);
if (!pyObj) {
Py_RETURN_NONE;