feat: Migrate to Python 3.14 (closes #135)
Replace deprecated Python C API calls with modern PyConfig-based initialization: - PySys_SetArgvEx() -> PyConfig.argv (deprecated since 3.11) - Py_InspectFlag -> PyConfig.inspect (deprecated since 3.12) Fix critical memory safety bugs discovered during migration: - PyColor::from_arg() and PyVector::from_arg() now return new references instead of borrowed references, preventing use-after-free when callers call Py_DECREF on the result - GameEngine::testTimers() now holds a local shared_ptr copy during callback execution, preventing use-after-free when timer callbacks call delTimer() on themselves Fix double script execution bug with --exec flag: - Scripts were running twice because GameEngine constructor executed them, then main.cpp deleted and recreated the engine - Now reuses existing engine and just sets auto_exit_after_exec flag Update test syntax to use keyword arguments for Frame/Caption constructors. Test results: 127/130 passing (97.7%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b173f59f22
commit
28396b65c9
14 changed files with 240 additions and 203 deletions
|
|
@ -1,6 +1,7 @@
|
|||
#include "PyVector.h"
|
||||
#include "PyObjectUtils.h"
|
||||
#include "McRFPy_Doc.h"
|
||||
#include "PyRAII.h"
|
||||
#include <cmath>
|
||||
|
||||
PyGetSetDef PyVector::getsetters[] = {
|
||||
|
|
@ -261,35 +262,46 @@ int PyVector::set_member(PyObject* obj, PyObject* value, void* closure)
|
|||
|
||||
PyVectorObject* PyVector::from_arg(PyObject* args)
|
||||
{
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
|
||||
if (PyObject_IsInstance(args, (PyObject*)type)) return (PyVectorObject*)args;
|
||||
|
||||
auto obj = (PyVectorObject*)type->tp_alloc(type, 0);
|
||||
|
||||
// Use RAII for type reference management
|
||||
PyRAII::PyTypeRef type("Vector", McRFPy_API::mcrf_module);
|
||||
if (!type) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check if args is already a Vector instance
|
||||
if (PyObject_IsInstance(args, (PyObject*)type.get())) {
|
||||
Py_INCREF(args); // Return new reference so caller can safely DECREF
|
||||
return (PyVectorObject*)args;
|
||||
}
|
||||
|
||||
// Create new Vector object using RAII
|
||||
PyRAII::PyObjectRef obj(type->tp_alloc(type.get(), 0), true);
|
||||
if (!obj) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Handle different input types
|
||||
if (PyTuple_Check(args)) {
|
||||
// It's already a tuple, pass it directly to init
|
||||
int err = init(obj, args, NULL);
|
||||
int err = init((PyVectorObject*)obj.get(), args, NULL);
|
||||
if (err) {
|
||||
Py_DECREF(obj);
|
||||
// obj will be automatically cleaned up when it goes out of scope
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
// Wrap single argument in a tuple for init
|
||||
PyObject* tuple = PyTuple_Pack(1, args);
|
||||
PyRAII::PyObjectRef tuple(PyTuple_Pack(1, args), true);
|
||||
if (!tuple) {
|
||||
Py_DECREF(obj);
|
||||
return NULL;
|
||||
}
|
||||
int err = init(obj, tuple, NULL);
|
||||
Py_DECREF(tuple);
|
||||
int err = init((PyVectorObject*)obj.get(), tuple.get(), NULL);
|
||||
if (err) {
|
||||
Py_DECREF(obj);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
||||
// Release ownership and return
|
||||
return (PyVectorObject*)obj.release();
|
||||
}
|
||||
|
||||
// Arithmetic operations
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue