add_long() in enumtypes.cpp:33-47 leaks one PyLong reference per enum constant. PyDict_SetItemString does NOT steal references — it increments the refcount internally. After the call succeeds, pyint.release() drops cppy's ownership without decrementing, orphaning the original reference.
Current code (enumtypes.cpp:41-46):
if( PyDict_SetItemString( dict_ptr.get(), name, pyint.get() ) != 0 )
{
return false;
}
pyint.release(); // Release the reference since the operation succeeded
The comment "Release the reference since the operation succeeded" reveals a misunderstanding — PyDict_SetItemString added its own reference, so the caller's reference should be decremented, not orphaned.
This function is called ~100+ times during init_enumtypes() (once per enum value across 10 enum types), leaking one PyLong per call. For small integers (-5 to 256), the leak is benign (CPython caches them), but values outside that range leak ~28 bytes each.
Fix — just remove pyint.release():
if( PyDict_SetItemString( dict_ptr.get(), name, pyint.get() ) != 0 )
{
return false;
}
// Let cppy::ptr destructor decref correctly.
// PyDict_SetItemString already added its own reference.
return true;
Found by cext-review-toolkit.
add_long()inenumtypes.cpp:33-47leaks onePyLongreference per enum constant.PyDict_SetItemStringdoes NOT steal references — it increments the refcount internally. After the call succeeds,pyint.release()drops cppy's ownership without decrementing, orphaning the original reference.Current code (
enumtypes.cpp:41-46):The comment
"Release the reference since the operation succeeded"reveals a misunderstanding —PyDict_SetItemStringadded its own reference, so the caller's reference should be decremented, not orphaned.This function is called ~100+ times during
init_enumtypes()(once per enum value across 10 enum types), leaking onePyLongper call. For small integers (-5 to 256), the leak is benign (CPython caches them), but values outside that range leak ~28 bytes each.Fix — just remove
pyint.release():Found by cext-review-toolkit.