diff --git a/src/main/c/Jep/python/jep_object.c b/src/main/c/Jep/python/jep_object.c index 793bb8dd..7777c28d 100644 --- a/src/main/c/Jep/python/jep_object.c +++ b/src/main/c/Jep/python/jep_object.c @@ -114,6 +114,7 @@ JNIEXPORT void JNICALL Java_jep_python_PyObject_setAttr } EXIT: + Py_XDECREF(pyAttr); PyEval_ReleaseThread(jepThread->tstate); release_utf_char(env, str, attrName); } diff --git a/src/test/java/jep/test/TestGetJPyObject.java b/src/test/java/jep/test/TestGetJPyObject.java index a0065aed..98a16a64 100644 --- a/src/test/java/jep/test/TestGetJPyObject.java +++ b/src/test/java/jep/test/TestGetJPyObject.java @@ -3,6 +3,7 @@ import java.lang.reflect.UndeclaredThrowableException; import java.util.Arrays; import java.util.Deque; +import java.util.concurrent.atomic.AtomicInteger; import jep.Interpreter; import jep.JepException; @@ -113,6 +114,36 @@ public static void testSetAttr(Interpreter interp) throws JepException { } } + public static void testRefCount(Interpreter interp) throws JepException { + AtomicInteger delCount = new AtomicInteger(); + interp.set("delCount", delCount); + interp.exec("class Simple:\n" + + " def __del__(self):\n" + + " delCount.getAndIncrement()" + ); + PyCallable simple = interp.getValue("Simple", PyCallable.class); + /* Check for leaks in setAttr() */ + PyObject obj1 = simple.callAs(PyObject.class); + PyObject obj2 = simple.callAs(PyObject.class); + obj1.setAttr("member", obj2); + obj2.close(); + obj1.close(); + /* When a PyObject is deleted any attributes without other references should also be deleted. */ + if (delCount.get() != 2) { + throw new IllegalStateException("Expecting 2 deletions but got " + delCount.get()); + } + delCount.set(0); + /* Check for leaks in equals() */ + obj1 = simple.callAs(PyObject.class); + obj2 = simple.callAs(PyObject.class); + obj1.equals(obj2); + obj2.close(); + obj1.close(); + if (delCount.get() != 2) { + throw new IllegalStateException("Expecting 2 deletions but got " + delCount.get()); + } + } + public static void testDelAttr(Interpreter interp) throws JepException { interp.eval(buildTestClassPython()); interp.eval("t = testclass(1, 'A String', None)"); @@ -402,6 +433,7 @@ public static void main(String[] args) throws Exception { testIdentity(interp); testGetAttr(interp); testSetAttr(interp); + testRefCount(interp); testDelAttr(interp); testJPyCallable(interp); testToString(interp);