python: Fix object deallocation.
Handing a reference to the wrapper object created a non-trivial circular reference that Pythons garbage collector is unable to break. Explicitly break it by using a weak reference. * lang/python/helpers.c (pygpgme_stash_callback_exception): Retrieve object from weak reference. * lang/python/pyme/core.py (Context.__del__): Free status callback. (Context.set_passphrase_cb): Use a weak reference. (Context.set_progress_cb): Likewise. (Context.set_status_cb): Likewise. (Context.op_edit): Likewise. Signed-off-by: Justus Winter <justus@gnupg.org>
This commit is contained in:
parent
e74cd9fb80
commit
ebfe2300c3
@ -64,9 +64,9 @@ void pygpgme_clear_generic_cb(PyObject **cb) {
|
|||||||
/* Exception support for callbacks. */
|
/* Exception support for callbacks. */
|
||||||
#define EXCINFO "_callback_excinfo"
|
#define EXCINFO "_callback_excinfo"
|
||||||
|
|
||||||
static void pygpgme_stash_callback_exception(PyObject *self)
|
static void pygpgme_stash_callback_exception(PyObject *weak_self)
|
||||||
{
|
{
|
||||||
PyObject *ptype, *pvalue, *ptraceback, *excinfo;
|
PyObject *self, *ptype, *pvalue, *ptraceback, *excinfo;
|
||||||
|
|
||||||
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
|
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
|
||||||
excinfo = PyTuple_New(3);
|
excinfo = PyTuple_New(3);
|
||||||
@ -86,7 +86,23 @@ static void pygpgme_stash_callback_exception(PyObject *self)
|
|||||||
PyTuple_SetItem(excinfo, 2, Py_None);
|
PyTuple_SetItem(excinfo, 2, Py_None);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject_SetAttrString(self, EXCINFO, excinfo);
|
self = PyWeakref_GetObject(weak_self);
|
||||||
|
/* self only has a borrowed reference. */
|
||||||
|
if (self == Py_None) {
|
||||||
|
/* This should not happen, as even if we're called from the data
|
||||||
|
release callback triggered from the wrappers destructor, the
|
||||||
|
object is still alive and hence the weak reference still refers
|
||||||
|
to the object. However, in case this ever changes, not seeing
|
||||||
|
any exceptions is worse than having a little extra code, so
|
||||||
|
here we go. */
|
||||||
|
fprintf(stderr,
|
||||||
|
"Error occurred in callback, but the wrapper object "
|
||||||
|
"has been deallocated.\n");
|
||||||
|
PyErr_Restore(ptype, pvalue, ptraceback);
|
||||||
|
PyErr_Print();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
PyObject_SetAttrString(self, EXCINFO, excinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *pygpgme_raise_callback_exception(PyObject *self)
|
PyObject *pygpgme_raise_callback_exception(PyObject *self)
|
||||||
|
@ -16,9 +16,7 @@
|
|||||||
# License along with this library; if not, write to the Free Software
|
# License along with this library; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
# import generators for portability with python2.2
|
import weakref
|
||||||
|
|
||||||
|
|
||||||
from . import pygpgme
|
from . import pygpgme
|
||||||
from .errors import errorcheck, GPGMEError
|
from .errors import errorcheck, GPGMEError
|
||||||
from . import errors
|
from . import errors
|
||||||
@ -149,6 +147,7 @@ class Context(GpgmeWrapper):
|
|||||||
|
|
||||||
self._free_passcb()
|
self._free_passcb()
|
||||||
self._free_progresscb()
|
self._free_progresscb()
|
||||||
|
self._free_statuscb()
|
||||||
if self.own and pygpgme.gpgme_release:
|
if self.own and pygpgme.gpgme_release:
|
||||||
pygpgme.gpgme_release(self.wrapped)
|
pygpgme.gpgme_release(self.wrapped)
|
||||||
|
|
||||||
@ -252,9 +251,9 @@ class Context(GpgmeWrapper):
|
|||||||
else:
|
else:
|
||||||
self.last_passcb = pygpgme.new_PyObject_p_p()
|
self.last_passcb = pygpgme.new_PyObject_p_p()
|
||||||
if hook == None:
|
if hook == None:
|
||||||
hookdata = (self, func)
|
hookdata = (weakref.ref(self), func)
|
||||||
else:
|
else:
|
||||||
hookdata = (self, func, hook)
|
hookdata = (weakref.ref(self), func, hook)
|
||||||
pygpgme.pygpgme_set_passphrase_cb(self.wrapped, hookdata, self.last_passcb)
|
pygpgme.pygpgme_set_passphrase_cb(self.wrapped, hookdata, self.last_passcb)
|
||||||
|
|
||||||
def set_progress_cb(self, func, hook=None):
|
def set_progress_cb(self, func, hook=None):
|
||||||
@ -275,9 +274,9 @@ class Context(GpgmeWrapper):
|
|||||||
else:
|
else:
|
||||||
self.last_progresscb = pygpgme.new_PyObject_p_p()
|
self.last_progresscb = pygpgme.new_PyObject_p_p()
|
||||||
if hook == None:
|
if hook == None:
|
||||||
hookdata = (self, func)
|
hookdata = (weakref.ref(self), func)
|
||||||
else:
|
else:
|
||||||
hookdata = (self, func, hook)
|
hookdata = (weakref.ref(self), func, hook)
|
||||||
pygpgme.pygpgme_set_progress_cb(self.wrapped, hookdata, self.last_progresscb)
|
pygpgme.pygpgme_set_progress_cb(self.wrapped, hookdata, self.last_progresscb)
|
||||||
|
|
||||||
def set_status_cb(self, func, hook=None):
|
def set_status_cb(self, func, hook=None):
|
||||||
@ -297,9 +296,9 @@ class Context(GpgmeWrapper):
|
|||||||
else:
|
else:
|
||||||
self.last_statuscb = pygpgme.new_PyObject_p_p()
|
self.last_statuscb = pygpgme.new_PyObject_p_p()
|
||||||
if hook == None:
|
if hook == None:
|
||||||
hookdata = (self, func)
|
hookdata = (weakref.ref(self), func)
|
||||||
else:
|
else:
|
||||||
hookdata = (self, func, hook)
|
hookdata = (weakref.ref(self), func, hook)
|
||||||
pygpgme.pygpgme_set_status_cb(self.wrapped, hookdata,
|
pygpgme.pygpgme_set_status_cb(self.wrapped, hookdata,
|
||||||
self.last_statuscb)
|
self.last_statuscb)
|
||||||
|
|
||||||
@ -333,9 +332,9 @@ class Context(GpgmeWrapper):
|
|||||||
if key == None:
|
if key == None:
|
||||||
raise ValueError("op_edit: First argument cannot be None")
|
raise ValueError("op_edit: First argument cannot be None")
|
||||||
if fnc_value:
|
if fnc_value:
|
||||||
opaquedata = (self, func, fnc_value)
|
opaquedata = (weakref.ref(self), func, fnc_value)
|
||||||
else:
|
else:
|
||||||
opaquedata = (self, func)
|
opaquedata = (weakref.ref(self), func)
|
||||||
|
|
||||||
result = pygpgme.gpgme_op_edit(self.wrapped, key, opaquedata, out)
|
result = pygpgme.gpgme_op_edit(self.wrapped, key, opaquedata, out)
|
||||||
if self._callback_excinfo:
|
if self._callback_excinfo:
|
||||||
|
Loading…
Reference in New Issue
Block a user